jh-admin/addon/dividemoney/model/OrderPay.php

736 lines
31 KiB
PHP

<?php
/**
* SaaSMall商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 成都SAAS云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.gobuysaas.com
* =========================================================
*/
namespace addon\dividemoney\model;
use addon\cypay\model\Pay as cyPayModel;
use addon\wechatpay\model\Pay as PayModel;
use app\model\DbModel;
use app\model\member\MemberAccount;
use app\model\system\Cron;
use addon\aliapp\model\MinCode;
use think\facade\Db;
class OrderPay extends DbModel
{
protected $name = 'dividemoney_bill';
protected $autoWriteTimestamp = 'int';
protected $site_id;
/***
* 添加分账账单
* @param $order
* @return array
*/
public function setAccounts($order)
{
try {
$this->site_id = $order['site_id'];
$getAccounts = event('DivideMoneyAccounts', $order); //获取参与分账账单
if ($getAccounts) {
$merge = [];
foreach ($getAccounts as $key => $item) {
foreach ($item as $v) {
$merge[] = $v;
}
}
$count = model('dividemoney_bill')->getCount(['order_id' => $order['order_id']], 'id');
if (!$count && $this->saveAll($merge)) { //添加任务
$cron = new Cron();
$cron->addCron(1, 1, '执行自动分账', 'AutoCronOrderDividemoney', time() + 8, $order['order_id']);
}
}
} catch (\Exception $e) {
return error($e->getMessage());
}
return success();
}
/***
* 开始分账
* @param $site_id
* @param $out_order_id
* @param $states
* @param $settlement 结算
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function startSplitAccount($order_id, $out_order_id = '', $states = 0, $settlement = false)
{
$where = [
['states', '=', $states],
];
if ($out_order_id) {
$where[] = ['out_trade_no', '=', $out_order_id];
} elseif ($order_id) {
$where[] = ['order_id', '=', $order_id];
}
$info = $this->where($where)->select()->toArray();
if ($info) {
$channel_type = $info[0]['channel_type'];
$out_trade_no = $info[0]['out_trade_no'];
$site_id = $info[0]['site_id'];
$is_video_number = $info[0]['is_video_number'];
if (!$order_id) {
$order_id = $info[0]['order_id'];
}
$acc_where = [
['site_id', '=', $site_id],
['account', 'in', array_column($info, 'account')]
];
$arr = $this->name('dividemoney_account')->where($acc_where)->column('*', 'account');
$receiver_list['receiver_list'] = [];
$saveData = [];
$royalty_parameters = [];//支付宝正常数据
$dividemoneyAccount = new DividemoneyAccount();
$balance_parameters = [];
foreach ($info as $item) {
if ($item['channel_type'] == 'member') {
if (isset($balance_parameters[$item['account']])) {//支付宝相同数据累加
$balance_parameters[$item['account']]['amount'] += $item['amount'];
$balance_parameters[$item['account']]['desc'] .= $item['reason'];
} else {
$balance_parameters[$item['account']] = [
'member_id' => $item['account'],
'amount' => $item['amount'],
'desc' => $item['reason'],
];
}
} else {
$channel_type = $item['channel_type'];
}
if ($channel_type == 'aliapp' || $channel_type == 'aliapy') {
if (isset($royalty_parameters[$item['account']])) {//支付宝相同数据累加
$royalty_parameters[$item['account']]['amount'] += $item['amount'];
$royalty_parameters[$item['account']]['desc'] .= $item['reason'];
} else {
$royalty_parameters[$item['account']] = [
'royalty_type' => 'transfer',
'trans_in' => $item['account'],
'amount' => $item['amount'],
'desc' => $item['reason'],
];
}
}
if ($channel_type == 'weapp' || $channel_type == 'wechatpay') {
if (isset($royalty_parameters[$item['account']])) {//支付宝相同数据累加
$royalty_parameters[$item['account']]['amount'] += $item['amount'] * 100;
$royalty_parameters[$item['account']]['description'] .= $item['reason'];
} else {
$royalty_parameters[$item['account']] = [
'type' => $item['account_type'],
'account' => $item['account'],
'name' => $item['realname'],
'amount' => $item['amount'] * 100,
'description' => $item['reason'],
];
}
}
if ($channel_type == 'cypay') { //畅捷分账
if (isset($royalty_parameters[$item['account']])) {//支付宝相同数据累加
$royalty_parameters[$item['account']]['amount'] += $item['amount'] * 100;
$royalty_parameters[$item['account']]['remark'] .= $item['reason'];
} else {
$royalty_parameters[$item['account']] = [
'trans_in' => $item['account'],
'amount' => $item['amount'] * 100,
'description' => $item['reason'] ?: '服务合作',
];
}
}
if ($channel_type == 'Integral') { //积分账号
if (isset($royalty_parameters[$item['account']])) {//支付宝相同数据累加
$royalty_parameters[$item['account']]['amount'] += $item['amount'];
$royalty_parameters[$item['account']]['description'] .= $item['reason'];
} else {
$royalty_parameters[$item['account']] = [
'type' => $item['account_type'],
'account' => $item['account'],
'name' => $item['realname'],
'amount' => $item['amount'],
'description' => $item['reason'],
];
}
}
//验证已经添加账号//微信账号 //积分未添加
if (isset($arr[$item['account']])) { //系统账号存在
$account = $arr[$item['account']];
if (!$account['states']) { //分账账号状态异常
if ($channel_type == 'aliapp' || $channel_type == 'aliapy') { //支付宝批量设置
$receiver_list['receiver_list'][] = [
'type' => $item['account_type'], //'userId',
'account' => $item['account'],
'name' => $item['realname'],
];
}
if ($channel_type == 'weapp' || $channel_type == 'wechatpay') { //微信添加分账账号
$data = [
'account_type' => $item['account_type'],
'account' => $item['account'],
'realname' => $item['realname'],
'divide_rate' => $item['fee_commission'],
];
$res = $dividemoneyAccount->BingWechatAccount($item['site_id'], $data);
if ($res['code'] < 0) { //如果错误
$dividemoneyAccount->where(['id' => $item['id']])->update(['states' => 0, 'refuse' => $res['message']]);
}
}
}
} else { //如果系统账号不存在
if ($channel_type == 'aliapp' || $channel_type == 'aliapy') { //支付宝批量设置
unset($item['id'], $item['create_time'], $item['update_time']);
$saveData[$item['account']] = $item; //需要保存数据
$receiver_list['receiver_list'][] = [
'type' => $item['account_type'],
'account' => $item['account'],
'name' => $item['realname'],
];
}
if ($channel_type == 'cypay') { //畅捷分账不存在保存并添加理论不会出现
$data = [
'realname' => $item['realname'],
'account' => $item['account'],
'divide_rate' => $item['fee_commission'],
];
$res = $dividemoneyAccount->BingCyPayAccount($site_id, $data);
if ($res['code'] < 0) { //如果错误
$dividemoneyAccount->where(['id' => $item['id']])->update(['states' => 0, 'refuse' => $res['message']]);
}
}
if ($channel_type == 'Integral') { //积分账号不存在保存并添加理论不会出现
unset($item['id'], $item['create_time'], $item['update_time']);
$item['states'] = 1;
$saveData[$item['account']] = $item; //需要保存数据
}
if ($channel_type == 'weapp' || $channel_type == 'wechatpay' || $channel_type == 'wechat') { //微信添加分账账号
$data = [
'account_type' => $item['account_type'],
'account' => $item['account'],
'realname' => $item['realname']
];
$res = $dividemoneyAccount->BingWechatAccount($site_id, $data);
if ($res['code'] < 0) { //如果错误
cache('alipayTradeBatchquery' . $site_id, null);
cache('is_pay_error' . $site_id, '微信未签约分账产品');
$dividemoneyAccount->where(['id' => $item['id']])->update(['states' => 0, 'refuse' => $res['message']]);
}
}
}
}
if ($saveData) { //保存分账账号
$dividemoneyAccount->saveAll(array_values($saveData));
}
if (count($balance_parameters) > 0) { //余额分账
$this->StartBalanceSettle($site_id, $order_id, $balance_parameters);
}
if (!$is_video_number || $out_order_id || $states != 0 || $settlement) {
switch ($channel_type) {
case 'cypay':
$trade_no = model('pay')->getValue(['out_trade_no' => $out_trade_no], 'pay_no');
$res = $this->StartCyPaySettle($site_id, $order_id, $trade_no, array_values($royalty_parameters));
break;
case 'weapp':
case 'wechat':
case 'wechatpay':
$res = $this->StartWechatSettle($site_id, $order_id, $out_trade_no, array_values($royalty_parameters));
break;
case 'aliapp':
case 'alipay': //开始分账
if ($receiver_list['receiver_list']) { //绑定支付宝分账账号
$receiver_list['out_request_no'] = $this->outRequestNo($site_id, 'no');
$res = $this->BindAliAccount($site_id, $receiver_list); //绑定分账关系
$w = [
['site_id', '=', $site_id],
['account', 'in', array_column($receiver_list['receiver_list'], 'account')],
];
if ($res['code'] == 10000) {
$dividemoneyAccount->where($w)->update(['states' => 1, 'sub_msg' => '', 'sub_code' => '']);
} else {
$this->where($where)->update(['refuse' => $res['sub_msg'] ?? '分账绑定账号失败', 'update_time' => time(), 'states' => 2]);//记录分账执行时间
if ($res['sub_code'] == 'aop.no-product-reg-by-partner') {
cache('alipayTradeBatchquery' . $site_id, null);
cache('is_pay_error' . $site_id, '支付宝未签约分账产品');
}
$dividemoneyAccount->where($w)->update(['refuse' => $res['sub_msg'], 'sub_code' => $res['sub_code']]);
return $this->error($res['sub_msg']);
}
}
$paySettle['out_request_no'] = $this->outRequestNo($site_id);
$paySettle['trade_no'] = model('pay')->getValue(['out_trade_no' => $out_trade_no], 'trade_no');
$paySettle['royalty_parameters'] = array_values($royalty_parameters);
if (!$is_video_number) {
$paySettle['royalty_mode'] = 'async';
}
$paySettle['extend_params'] = [ //标准分账是否完结
'royalty_finish' => true,
];
$res = $this->AliapySettle($site_id, $order_id, $paySettle, $settlement); //开始分账
break;
default:
$res = ['msg' => '不存在的分账类型'];
}
} else {
$res = ['msg' => '公域不在结算期'];
}
return $res;
}
}
/**
* 开始分账现金余额
* @param $site_id
* @param $order_id
* @param $balance_parameters
* @param $extendupdata
* @return array|void
*/
public function StartBalanceSettle($site_id, $order_id, $balance_parameters, $extendupdata = [])
{
try {
$member = new MemberAccount();
$updata = [
'update_time' => time(),
'settle_num' => Db::raw('settle_num +1'),
'out_request_no' => time(),
];
foreach ($balance_parameters as $k => $v) {
$info = $member->addMemberAccount($site_id, $v['member_id'], 'balance_money', $v['amount'], 'order', 0, $v['desc']);
$w = [
['site_id', '=', $site_id],
['order_id', '=', $order_id],
];
if ($info['code'] == 0) {
$updata['refuse'] = '自动结算';
$updata['states'] = 1;
$updata['settle_no'] = time();
$w[] = ['account', '=', $v['member_id']];
$w[] = ['account_type', '=', 'balance'];
$this->where($w)->update(array_merge($updata, $extendupdata));
}
}
return $this->success();
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}
/***
* 结算积分
* @param $site_id
* @param $order_id
* @param $royalty_parameters
* @return array
*/
public function StartIntegralSettle($site_id, $order_id, $royalty_parameters)
{
$Integral = new ShopAccount();
try {
$updata = [
'update_time' => time(),
'settle_num' => Db::raw('settle_num +1'),
'out_request_no' => $site_id
];
foreach ($royalty_parameters as $k => $v) {
$params = [
'site_id' => $v['account'],
'join_id' => $order_id,
'type' => 'legumes_integral_community',
'money' => $v['amount'],
'remark' => $v['description']
];
$id = $Integral->addPendingSettlement($params);
if ($id) {
$Integral->accountSettlement($id, $order_id);
$w = [
'site_id' => $v['amount'],
'order_id' => $order_id,
];
$updata['states'] = 1;
$updata['settle_no'] = $id;
$this->where($w)->update($updata);
}
}
return success('integral_community');
} catch (\Exception $e) {
$updata['states'] = 2;
$updata['refuse'] = $e->getMessage();
$this->where(['site_id' => $site_id, 'order_id' => $order_id])->update($updata);
return $this->error('', $e->getMessage());
}
}
/***
* 开始畅捷分账
* @param $site_id
* @param $order_id
* @param $out_order_no
* @param $royalty_parameters
* @param $unfreeze_unsplit
* @return void
*/
public function StartCyPaySettle($site_id, $order_id, $out_order_no, $royalty_parameters, $unfreeze_unsplit = true, $extendupdata = [])
{
$payModel = new cyPayModel($site_id);
$out_order_id = $this->outRequestNo($site_id);
try {
$w = [
['site_id', '=', $site_id],
['order_id', '=', $order_id],
];
$updata = [
'update_time' => time(),
'settle_num' => Db::raw('settle_num +1'),
'out_request_no' => $out_order_id
];
$res = $payModel->profitsharing($out_order_no, $out_order_id, $royalty_parameters, $unfreeze_unsplit);
if ($res['code'] < 0) {
$updata['states'] = 2;
$updata['refuse'] = $res['message'];
} else {
$updata['states'] = 1;
$updata['settle_no'] = $out_order_id;
$updata['refuse'] = '自动结算';
}
$u = $this->where($w)->update(array_merge($updata, $extendupdata));
return $u;
} catch (\Exception $e) {
return $this->error('', $e->getMessage());
}
}
/***
* 结算处理
* @param $biz_content
* @return void
*/
public function aliSettleSplitAccount($biz_content)
{
$res = $this->startSplitAccount('', $biz_content['out_order_id'], 0, true);
return $res;
}
/***
* 支付宝分账提交结算
* @param $site_id
* @param $order_id
* @param $paySettle 结算数据
* @param $settlement 是否重启任务
* @param $updata 自定义更新数据
* @return mixed
*/
public function AliapySettle($site_id, $order_id, $paySettle, $settlement = false, $extendupdata = [])
{
try {
$micode = new MinCode($site_id);
$res = $micode->requestApi('alipay.trade.order.settle', $paySettle)['alipay_trade_order_settle_response'];
$w = [
['site_id', '=', $site_id],
['order_id', '=', $order_id],
];
$updata = [
'update_time' => time(),
'settle_num' => Db::raw('settle_num +1'),
'out_request_no' => $paySettle['out_request_no']
];
if ($res['code'] == 10000) {
$updata['states'] = 1;
$updata['settle_no'] = $res['settle_no'];
$updata['refuse'] = '支付宝自动结算';
} else {
$updata['refuse'] = $res['sub_msg'];
switch ($res['sub_code']) {
case 'ACQ.INVALID_PARAMETER':
if (isset($extendupdata['is_order_account_locking']) && in_array($extendupdata['is_order_account_locking'], [1, 2])) {
$updata['refuse'] = '订单已完结';
$updata['states'] = 5;
}
break;
case 'ACQ.TRADE_STATUS_ERROR':
if ($res['sub_msg'] == '交易已关闭') {
$updata['refuse'] = '交易已关闭';
$updata['states'] = 5;
break;
}
case 'ACQ.TXN_RESULT_ACCOUNT_BALANCE_NOT_ENOUGH'://余额不足
$updata['states'] = 3;
break;
case 'ACQ.ALLOC_REFUSE_BEFORE_SETTLE': //资金未在结算期
$updata['states'] = 0;
break;
case 'ACQ.ALLOC_REFUSE_AFTER_REFUND':
$updata['refuse'] = '订单退款';
$updata['states'] = 5;
break;
case 'ACQ.SETTLE_TO_CARD_NOT_SUPPORT':
cache('alipayTradeBatchquery' . $site_id, null);
cache('is_pay_error' . $site_id, '签约到银行禁止分账');
default:
$updata['states'] = 2;
if (!$settlement) {
$cron = new Cron();
$cron->addCron(1, 1, '重启执行自动分账', 'ResetCronOrderDividemoney', strtotime('+1 day'), $order_id);
}
}
$updata['sub_code'] = $res['sub_code'];
}
$this->where($w)->update(array_merge($updata, $extendupdata));
return $res;
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}
/***
* 解除订单结算限制
* @param $site_id
* @return void
*/
public function SecureCronOrderMoney($site_id)
{
$where = [
['site_id', '=', $site_id],
['is_order_account_locking', '=', 1],
['states', '=', 1],
];
$info = $this->where($where)->limit(5)->select()->toArray();
if ($info) {
$cron = new Cron();
try {
foreach ($info as $item) {
$updata = [
'is_order_account_locking' => 2
];
switch ($item['channel_type']) {
case 'weapp':
case 'wechat':
case 'wechatpay':
$this->WechatUnfreeze($site_id, $item['order_id'], $item['out_trade_no'], $updata); //开始分账
break;
case 'aliapp':
case 'alipay': //开始分账
$paySettle['out_request_no'] = $this->outRequestNo($site_id);
$paySettle['trade_no'] = $item['trade_no'] ?: model('pay')->getValue(['out_trade_no' => $item['out_trade_no']], 'trade_no');
$paySettle['extend_params'] = [ //标准分账是否完结
'royalty_finish' => true,
];
$this->AliapySettle($site_id, $item['order_id'], $paySettle, true, $updata); //开始分账
break;
}
}
$cron->addCron(1, 1, '解除货款结算限制', 'SecureCronOrderDividemoney', time(), $site_id);
return $this->success('成功');
} catch (\Exception $e) {
$cron->addCron(1, 1, '解除货款结算限制', 'SecureCronOrderDividemoney', time(), $site_id);
return $this->error($e->getMessage());
}
}
return $this->error('无效数据');
}
/****
* 开始微信分账
* @param $site_id
* @param $out_order_no
* @param $royalty_parameters
* @param $unfreeze_unsplit
* @return void
* @throws \app\exception\ApiException
*/
public function StartWechatSettle($site_id, $order_id, $out_order_no, $royalty_parameters, $unfreeze_unsplit = true)
{
$PayModel = new PayModel(1, $site_id, 'v3');
$model = $PayModel->getApp();
$config = $PayModel->getPayConfig();
$BodyData = [
'appid' => $config['app_id'],
'transaction_id' => model('pay')->getValue(['out_trade_no' => $out_order_no], 'trade_no'),
'out_order_no' => $this->outRequestNo($site_id),
'receivers' => array_map(function ($item) use ($model) {
$item['name'] = $model->encryptor($item['name']);
return $item;
}, $royalty_parameters),
'unfreeze_unsplit' => $unfreeze_unsplit //标记分账结束
];
if ($config['is_isp']) {
$BodyData['sub_appid'] = $config['sub_appid'];
$BodyData['sub_mchid'] = $config['sub_mchid'];
}
try {
$res = $model->requestApi('/v3/profitsharing/orders', $BodyData);
if ($res['code'] != 0) {
return $this->error('', $res['message']);
} else {
$updata = [
'update_time' => time(),
'settle_num' => Db::raw('settle_num +1'),
'out_request_no' => $BodyData['out_order_no']
];
if ($res['code'] >= 0) {
$updata['states'] = 1;
$updata['settle_no'] = $res['data']['order_id'];
$updata['refuse'] = '微信自动结算';
$receivers = $res['data']['receivers'];
$arrInfo = [
'ACCOUNT_ABNORMAL' => '分账接收账户异常',
'NO_RELATION' => '分账关系已解除',
'RECEIVER_HIGH_RISK' => '高风险接收方',
'RECEIVER_REAL_NAME_NOT_VERIFIED' => '接收方未实名',
'NO_AUTH' => '分账权限已解除',
'RECEIVER_RECEIPT_LIMIT' => '超出用户月收款限额',
'PAYER_ACCOUNT_ABNORMAL' => '分出方账户异常',
'INVALID_REQUEST' => '描述参数设置失败',
];
foreach ($receivers as $item) {
$updata['trade_no'] = $item['detail_id'];
if (isset($item['fail_reason']) && $item['fail_reason']) {
$updata['sub_code'] = $item['fail_reason'];
$updata['refuse'] = $arrInfo[$item['fail_reason']];
$updata['states'] = 3;
}
$this->where(
['site_id' => $site_id,
'order_id' => $order_id,
'account' => $item['account']
])
->update($updata);
}
}
return $this->success();
}
} catch (\Exception $e) {
return $this->error('', $e->getMessage());
}
}
/****
* 微信解除订单结算限制
* @param $site_id
* @param $order_id
* @param $out_order_no
* @param $unfreeze_unsplit
* @param $updata
* @return void
*/
public function WechatUnfreeze($site_id, $order_id, $out_trade_no, $updata = [])
{
$PayModel = new PayModel(1, $site_id, 'v3');
$model = $PayModel->getApp();
$config = $PayModel->getPayConfig();
$bodyData = [
'appid' => $config['app_id'],
'transaction_id' => model('pay')->getValue(['out_trade_no' => $out_trade_no], 'trade_no'),
'out_order_no' => $this->outRequestNo($site_id),
'description' => '解除分账',
];
if ($config['is_isp']) {
$bodyData['sub_appid'] = $config['sub_appid'];
$bodyData['sub_mchid'] = $config['sub_mchid'];
}
$res = $model->requestApi('/v3/profitsharing/orders/unfreeze', $bodyData);
if ($res['code'] != 0) {
return $this->error('', $res['message']);
} else {
$this->where(['site_id' => $site_id, 'order_id' => $order_id,])->update($updata);
return $this->success();
}
}
/***
* 分账绑定账号查询
* @param $site_id
* @return mixed
*/
public function AliBindQuery($site_id)
{
$micode = new MinCode($site_id);
$res = $micode->requestApi('alipay.trade.royalty.relation.batchquery', [
'page_num' => 1,
'page_size' => 100,
'out_request_no' => $this->outRequestNo($site_id)]);
return $res['alipay_trade_royalty_relation_batchquery_response'];
}
/***
* 查询分账状态
* @param $site_id
* @param $trade_no
* @param $out_request_no
* @return mixed
*/
public function AliSettleQuery($site_id, $trade_no, $out_request_no)
{
$micode = new MinCode($site_id);
$res = $micode->requestApi('alipay.trade.order.settle.query', [
'trade_no' => $trade_no,
'out_request_no' => $out_request_no,
]);
return $res['alipay_trade_order_settle_query_response'];
}
/***
* 查询分账余额
* @param $site_id
* @param $trade_no
* @return mixed
*/
public function AlionSettleQuery($site_id, $trade_no)
{
$micode = new MinCode($site_id);
$res = $micode->requestApi('alipay.trade.order.onsettle.query', [
'trade_no' => $trade_no,
]);
return $res['alipay_trade_order_onsettle_query_response'];
}
public function AlipaytradeRoyaltyrateQuery($site_id, $out_request_no)
{
$micode = new MinCode($site_id);
$res = $micode->requestApi('alipay.trade.royalty.rate.query', [
'out_request_no' => $out_request_no,
]);
return $res['alipay_trade_royalty_rate_query_response'];
}
/***
* 绑定支付宝账号
* @param $site_id
* @param $data
* @return mixed
*/
public function BindAliAccount($site_id, $data)
{
$micode = new MinCode($site_id);
$res = $micode->requestApi('alipay.trade.royalty.relation.bind', $data);
return $res['alipay_trade_royalty_relation_bind_response'];
}
/***
* 结算交易号
* @param $siteId
* @param $prefix
* @return string
*/
public function outRequestNo($siteId, $prefix = '')
{
// 基础内容
$order_no = $prefix . $siteId . date('YmdHis') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
return $order_no;
}
}