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; } }