1]))->notifyIndex($attach['store_id'], (object)$msg); } public static function getMinProfitRate(){ $rate = Option::get('platform_wechat_b2b_store_min_rate', 0, 'saas'); if($rate < self::PROFIT_RATE){ $rate = self::PROFIT_RATE; } return $rate; } public static function getMchid($store_id){ $userAuditSetting = UserAuditSetting::findOne(['store_id' => $store_id]); $mchid = $userAuditSetting['wx_b2b_pay_mchid']; return $mchid; } public static function getAppkey($app, $mchid = '') { $client = new BaseClient($app); $data = [ 'mchid' => $mchid, ]; /** * { ["appkey"]=> string(32) "cVEjHiCnJNNG1wXa6dRczWd8J8hFT9AD" ["sandbox_appkey"]=> string(32) "Kf4vjwnr4a0CLpQ33Ft30vxbJFCtcoy6" ["errcode"]=> int(0) ["errmsg"]=> string(2) "OK" } */ $result = $client->httpPostJson("/retail/B2b/getappkey", $data); if (!empty($result)) { if ($result['errcode'] == '0') { return $result['appkey']; } } \Yii::warning(['--------wxOrderGet-error---- ', $result]); return ''; } public static function calcPaySig(string $uri, string $post_body, string $appkey): string { $need_sign_msg = $uri . '&' . $post_body; return hash_hmac('sha256', $need_sign_msg, $appkey); } public static function calcSignature(string $post_body, string $session_key): string { return hash_hmac('sha256', $post_body, $session_key); } public static function pay($session_key, $order, $type, $goods_names = null, $balance_price = 0) { $store_id = $order->store_id; $mchid = self::getMchid($store_id); $app = WechatMini::getWechatConfig($store_id); $appkey = self::getAppkey($app, $mchid); $pay_price = $order->pay_price; $post_body = [ 'attach' => json_encode([$store_id, $order->id]), "mchid" => $mchid, "out_trade_no" => $order->order_no, 'description' => $goods_names, 'amount' => [ 'order_amount' => (int)($balance_price > 0 ? floatval($pay_price - $balance_price) * 100 : $pay_price * 100), ], 'env' => 0, ]; // 是否走分账 if (is_profit_sharing()) { $post_body['need_profit_sharing'] = 1; } // $session = $app->auth->session($this->code); // $session_key = $session['session_key']; $res = [ 'signData' => json_encode($post_body, JSON_UNESCAPED_UNICODE), 'mode' => 'retail_pay_goods', 'paySig' => self::calcPaySig('requestCommonPayment', json_encode($post_body, JSON_UNESCAPED_UNICODE), $appkey), 'signature' => self::calcSignature(json_encode($post_body, JSON_UNESCAPED_UNICODE), $session_key), ]; return [ 'code' => 0, 'data' => $res, 'res' => $res, 'body' => $goods_names ]; } public static function orderRefund($order, $type, $refundFee, $orderRefundNo, $refund_account = null) { try { $store_id = $order->store_id; $mchid = self::getMchid($store_id); $app = WechatMini::getWechatConfig($store_id); $appkey = self::getAppkey($app, $mchid); $uri = '/retail/B2b/refund'; if ($type != OrderNo::ORDER_UNION) { if ($order->is_combine_pay == 1 && $order->combine_money > 0) { $payPrice = round($order->pay_price - $order->combine_money, 2); } else { $payPrice = $order->pay_price; } } else { // 联合订单支付的总额 // $payPrice = $order->price; } if ($order->is_combine_pay == 1 && $order->combine_money > 0) { $refundFee = round($refundFee - $order->combine_money, 2); } $post_body = [ "mchid" => $mchid, "out_trade_no" => $order->order_no, 'out_refund_no' => $orderRefundNo, 'refund_amount' => intval(bcmul($refundFee, 100)), 'refund_from' => 1, ]; $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey); $client = new BaseClient($app); $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]); debug_log([$result], __CLASS__ . '.log'); if (empty($result) || $result['errcode']) { \Yii::warning(['--------wxOrderGet-error---- ', $result]); return [ 'code' => 1, 'msg' => "获取失败" . $result['errmsg'], 'res' => $result, ]; } // 联合支付,退余额 if ($order->is_combine_pay == 1 && $order->combine_money > 0) { AccountLog::saveLog($order->user_id, $order->combine_money, AccountLog::TYPE_BALANCE, AccountLog::LOG_TYPE_INCOME, AccountLog::TYPE_PLATFORM_REFUND_ORDER, $order->id, "商城订单退款,订单号:{$order->order_no}"); } return [ 'code' => 0, 'msg' => "获取成功", 'res' => $result, ]; } catch (\Exception $e) { return [ 'code' => 1, 'msg' => $e->getMessage() ]; } } public static function find($store_id, $order_no) { if (!$order_no) { return [ 'code' => 1, 'msg' => '缺少必要参数' ]; } try { $mchid = self::getMchid($store_id); $app = WechatMini::getWechatConfig($store_id); $appkey = self::getAppkey($app, $mchid); $uri = '/retail/B2b/getorder'; $client = new BaseClient($app); $post_body = [ 'mchid' => $mchid, 'out_trade_no' => $order_no, ]; $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey); $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]); if (!empty($result)) { if ($result['errcode'] == '0') { return [ 'code' => 0, 'msg' => "获取成功", 'data' => $result, ]; } } \Yii::warning(['--------wxOrderGet-error---- ', $result]); return [ 'code' => 1, 'msg' => "获取失败" . $result['errmsg'], 'data' => $result, ]; } catch (Exception $e) { \Yii::warning(['WECHAT QUERY ORDER <==========>', $order_no, $e->getMessage()]); } return [ 'code' => 1, 'msg' => 'fail', 'data' => false ]; } public static function setmchprofitrate($store_id, $profit_rate) { try { $mchid = self::getMchid($store_id); $app = WechatMini::getWechatConfig($store_id); $appkey = self::getAppkey($app, $mchid); $uri = '/retail/B2b/setmchprofitrate'; $client = new BaseClient($app); $post_body = [ 'sub_mchid' => $mchid, 'profit_rate' => $profit_rate, ]; $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey); $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]); if (!empty($result)) { if ($result['errcode'] == '0') { return [ 'code' => 0, 'msg' => "获取成功", 'data' => $result, '$post_body' => $post_body, ]; } } \Yii::warning(['--------setmchprofitrate-error---- ', $store_id, $profit_rate, $post_body, $result]); return [ 'code' => 1, 'msg' => "获取失败" . $result['errmsg'], 'data' => $result, '$post_body' => $post_body, ]; } catch (Exception $e) { \Yii::warning(['setmchprofitrate <==========>', $store_id, $profit_rate, $e->getMessage()]); } return [ 'code' => 1, 'msg' => 'fail', 'data' => false ]; } public static function getretailopenidlist($store_id, $params = [], $page_context = '', $pageSize = 5) { if($params['mobile']){ return [ 'code' => 0, 'msg' => "获取成功", 'data' => [[ 'data' => self::getretailinfo($store_id, 0, trim($params['mobile'])), ]], 'page_context' => '', 'result' => [], ]; } $app = WechatMini::getWechatConfig($store_id); $uri = '/wxa/business/getretailopenidlist'; $client = new BaseClient($app); $post_body = [ 'limit' => $pageSize, ]; $page_context && $post_body['page_context'] = $page_context; $result = $client->httpPostJson($uri, $post_body); if (!empty($result)) { if ($result['errcode'] == '0') { $data = []; foreach($result['openid_list'] as $openid){ $data[] = [ 'openid' => $openid, 'data' => self::getretailinfo($store_id, $openid), ]; } return [ 'code' => 0, 'msg' => "获取成功", 'data' => $data, 'page_context' => $result['page_context'], 'result' => $result, // '_queryprofitsharingremainamt' => self::_queryprofitsharingremainamt($store_id, Order::findOne(15176)), // '_queryprofitsharingorder' => self::_queryprofitsharingorder($store_id, Order::findOne(15176), [ // "receiver_type" => "PAYEE_TYPE_EXTERNAL_MERCHANT", // "receiver_account" => "1508102711" // ]), // 'b2bProfitSharing' => (new \app\utils\OrderUtil())->b2bProfitSharing(Order::findOne(15176)), // 'sharingaccountDel' => self::sharingaccountDel($store_id, self::sharingaccountSaas()), // 'sharingaccountAdd' => self::sharingaccountAdd($store_id, self::sharingaccountSaas()), // '_queryprofitsharingaccount' => self::_queryprofitsharingaccount($store_id), ]; } } \Yii::warning(['--------wxOrderGet-error---- ', $result]); return [ 'code' => 1, 'msg' => "获取失败" . $result['errmsg'], // '$result' => $result, ]; } public static function getretailinfo($store_id, $openid = '', $mobile_phone = '') { $app = WechatMini::getWechatConfig($store_id); $uri = '/wxa/business/getretailinfo'; $client = new BaseClient($app); $post_body = []; $openid && $post_body['openid'] = $openid; $mobile_phone && $post_body['mobile_phone'] = $mobile_phone; $result = $client->httpPostJson($uri, $post_body); if (!empty($result)) { if ($result['errcode'] == '0') { return [ 'code' => 0, 'msg' => "获取成功", 'data' => $result['info'], // '$result' => $result, ]; } } \Yii::warning(['--------wxOrderGet-error---- ', $result]); return [ 'code' => 1, 'msg' => "获取失败" . $result['errmsg'], // '$result' => $result, ]; } //查询商户号开通状态--当前店铺配置的商户号 public static function mchorder($store_id) { $retailgetmchorder = self::_retailgetmchorder($store_id); if($retailgetmchorder['code']){ return $retailgetmchorder; } $data = null; $result = $retailgetmchorder['data']; $mchid = self::getMchid($store_id); if($result['list']){ foreach($result['list'] as $item){ if($mchid == $item['inner_resp']['sub_mchid']){ $data = $item; break; } if($item['inner_resp']['sub_merchant_registration_status'] && $mchid == $item['inner_resp']['sub_merchant_registration_status']['sub_mchid']){ $data = $item; break; } } } return [ 'code' => 0, 'msg' => "获取成功", 'data' => $data, ]; } //查询商户号开通状态 public static function _retailgetmchorder($store_id) { try { $app = WechatMini::getWechatConfig($store_id); $uri = '/retail/B2b/retailgetmchorder'; $client = new BaseClient($app); $post_body = [ ]; $result = $client->httpPostJson($uri, $post_body); if (!empty($result)) { if ($result['errcode'] == '0') { return [ 'code' => 0, 'msg' => "获取成功", 'data' => $result, ]; } } \Yii::warning(['--------retailgetmchorder-error---- ', $result]); return [ 'code' => 1, 'msg' => "获取失败" . $result['errmsg'], 'data' => $result, ]; } catch (Exception $e) { \Yii::warning(['retailgetmchorder <==========>', $store_id, $e->getMessage()]); } return [ 'code' => 1, 'msg' => 'fail', 'data' => false ]; } public static function _getRid($store_id, $rid) { try { $app = WechatMini::getWechatConfig($store_id); $uri = '/cgi-bin/openapi/rid/get'; $client = new BaseClient($app); $post_body = [ 'rid' => $rid ]; $result = $client->httpPostJson($uri, $post_body, ['rid' => $rid]); if (!empty($result)) { if ($result['errcode'] == '0') { return [ 'code' => 0, 'msg' => "获取成功", 'data' => $result, ]; } } \Yii::warning(['--------getRid-error---- ', $result]); return [ 'code' => 1, 'msg' => "获取失败" . $result['errmsg'], 'data' => $result, ]; } catch (Exception $e) { \Yii::warning(['getRid <==========>', $store_id, $e->getMessage()]); } return [ 'code' => 1, 'msg' => 'fail', 'data' => false ]; } //查询分账方 public static function _queryprofitsharingaccount($store_id) { try { $app = WechatMini::getWechatConfig($store_id); $uri = '/retail/B2b/queryprofitsharingaccount'; $client = new BaseClient($app); $post_body = []; $mchid = self::getMchid($store_id); $appkey = self::getAppkey($app, $mchid); $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey); $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]); if (!empty($result)) { if ($result['errcode'] == '0') { return [ 'code' => 0, 'msg' => "获取成功", 'data' => $result, ]; } } \Yii::warning(['--------queryprofitsharingaccount-error---- ', $result]); return [ 'code' => 1, 'msg' => "获取失败" . $result['errmsg'], 'data' => $result, ]; } catch (Exception $e) { \Yii::warning(['queryprofitsharingaccount <==========>', $store_id, $e->getMessage()]); } return [ 'code' => 1, 'msg' => 'fail', 'data' => false ]; } //添加分账方 public static function _addprofitsharingaccount($store_id, $account = []) { try { $app = WechatMini::getWechatConfig($store_id); $uri = '/retail/B2b/addprofitsharingaccount'; $client = new BaseClient($app); /** * { "profit_sharing_relation_type": "RELATION_TYPE_SERVICE_PROVIDER", "payee_type": "PAYEE_TYPE_EXTERNAL_MERCHANT", "payee_id": "1234567890", "payee_name": "XXX公司" } */ $post_body = $account; $mchid = self::getMchid($store_id); $appkey = self::getAppkey($app, $mchid); $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey); $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]); /** * errcorde errmsg 210001 添加分账方频繁 210002 添加分账方传入环境参数错误 210003 添加分账方传入账户类型错误 210004 重复添加分账方 210006 单个小程序绑定的分账方超过上限 */ if (!empty($result)) { if (in_array($result['errcode'], [0, 210004])) { return [ 'code' => 0, 'msg' => "获取成功", 'data' => $result, ]; } } \Yii::warning(['--------addprofitsharingaccount-error---- ', $result]); return [ 'code' => $result['errcode'] ?? 1, 'msg' => "获取失败" . $result['errmsg'], 'data' => $result, ]; } catch (Exception $e) { \Yii::warning(['addprofitsharingaccount <==========>', $store_id, $e->getMessage()]); } return [ 'code' => 1, 'msg' => 'fail', 'data' => false ]; } //删除分账方 public static function _delprofitsharingaccount($store_id, $account = []) { try { $app = WechatMini::getWechatConfig($store_id); $uri = '/retail/B2b/delprofitsharingaccount'; $client = new BaseClient($app); /* { "payee_type": "PAYEE_TYPE_EXTERNAL_MERCHANT", "payee_id": "1234567890" } */ $post_body = $account; $mchid = self::getMchid($store_id); $appkey = self::getAppkey($app, $mchid); $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey); $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]); if (!empty($result)) { if ($result['errcode'] == '0') { return [ 'code' => 0, 'msg' => "获取成功", 'data' => $result, ]; } } \Yii::warning(['--------delprofitsharingaccount-error---- ', $result]); return [ 'code' => 1, 'msg' => "获取失败" . $result['errmsg'], 'data' => $result, ]; } catch (Exception $e) { \Yii::warning(['delprofitsharingaccount <==========>', $store_id, $e->getMessage()]); } return [ 'code' => 1, 'msg' => 'fail', 'data' => false ]; } //请求分账 public static function _createprofitsharingorder($store_id, $order, $account = []) { try { $app = WechatMini::getWechatConfig($store_id); $uri = '/retail/B2b/createprofitsharingorder'; $client = new BaseClient($app); /* { "mchid": "1654806452", "out_trade_no": "400128912435668818922107515929", "profit_fee": 100, "receiver_type": "PAYEE_TYPE_EXTERNAL_MERCHANT", "receiver_account": "1654806451" } */ $mchid = self::getMchid($store_id); $appkey = self::getAppkey($app, $mchid); $post_body = $account; $post_body['mchid'] = $mchid; $post_body['out_trade_no'] = $order['order_no']; $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey); $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]); if (!empty($result)) { if ($result['errcode'] == '0') { return [ 'code' => 0, 'msg' => "获取成功", 'data' => $result, ]; } } \Yii::warning(['--------createprofitsharingorder-error---- ', $result]); return [ 'code' => $result['errcode'] ?? 1, 'msg' => $result['errmsg'], 'data' => $result, ]; } catch (Exception $e) { \Yii::warning(['createprofitsharingorder <==========>', $store_id, $e->getMessage()]); } return [ 'code' => 1, 'msg' => 'fail', 'data' => false ]; } //可用于查询某笔支付单的某笔分账单是否已经分账完成 public static function _queryprofitsharingorder($store_id, $order, $account = []) { try { $app = WechatMini::getWechatConfig($store_id); $uri = '/retail/B2b/queryprofitsharingorder'; $client = new BaseClient($app); /* { "mchid": "1654806452", "out_trade_no": "400128912435668818922107515929", "receiver_type": "PAYEE_TYPE_EXTERNAL_MERCHANT", "receiver_account": "1654806451" } */ $mchid = self::getMchid($store_id); $appkey = self::getAppkey($app, $mchid); $post_body = $account; $post_body['mchid'] = $mchid; $post_body['out_trade_no'] = $order['order_no']; $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey); $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]); if (!empty($result)) { if ($result['errcode'] == '0') { return [ 'code' => 0, 'msg' => "获取成功", 'data' => $result, ]; } } \Yii::warning(['--------queryprofitsharingorder-error---- ', $result]); return [ 'code' => 1, 'msg' => "获取失败" . $result['errmsg'], 'data' => $result, ]; } catch (Exception $e) { \Yii::warning(['queryprofitsharingorder <==========>', $store_id, $e->getMessage()]); } return [ 'code' => 1, 'msg' => 'fail', 'data' => false ]; } //查询分账剩余金额 public static function _queryprofitsharingremainamt($store_id, $order) { try { $app = WechatMini::getWechatConfig($store_id); $uri = '/retail/B2b/queryprofitsharingremainamt'; $client = new BaseClient($app); /* { "mchid": "1654806452", "out_trade_no": "400128912435668818922107515929", } */ $mchid = self::getMchid($store_id); $appkey = self::getAppkey($app, $mchid); $post_body = []; $post_body['mchid'] = $mchid; $post_body['out_trade_no'] = $order['order_no']; $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey); $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]); if (!empty($result)) { if ($result['errcode'] == '0') { return [ 'code' => 0, 'msg' => "获取成功", 'data' => $result, ]; } } \Yii::warning(['--------queryprofitsharingremainamt-error---- ', $result]); return [ 'code' => 1, 'msg' => "获取失败" . $result['errmsg'], 'data' => $result, ]; } catch (Exception $e) { \Yii::warning(['queryprofitsharingremainamt <==========>', $store_id, $e->getMessage()]); } return [ 'code' => 1, 'msg' => 'fail', 'data' => false ]; } //完成分账 public static function _finishprofitsharingorder($store_id, $order) { try { $app = WechatMini::getWechatConfig($store_id); $uri = '/retail/B2b/finishprofitsharingorder'; $client = new BaseClient($app); /* { "mchid": "1654806452", "out_trade_no": "400128912435668818922107515929", } */ $mchid = self::getMchid($store_id); $appkey = self::getAppkey($app, $mchid); $post_body = []; $post_body['mchid'] = $mchid; $post_body['out_trade_no'] = $order['order_no']; $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey); $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]); if (!empty($result)) { if ($result['errcode'] == '0') { return [ 'code' => 0, 'msg' => "获取成功", 'data' => $result, ]; } } \Yii::warning(['--------finishprofitsharingorder-error---- ', $result]); return [ 'code' => 1, 'msg' => "获取失败" . $result['errmsg'], 'data' => $result, ]; } catch (Exception $e) { \Yii::warning(['finishprofitsharingorder <==========>', $store_id, $e->getMessage()]); } return [ 'code' => 1, 'msg' => 'fail', 'data' => false ]; } //分账方 是否是 saas平台 public static function sharingaccountIsSaas($account) { return self::sharingaccountSaas()['sp_mch_id'] == $account; } //分账方 saas平台 public static function sharingaccountSaas() { $wechat_config = Option::getSaasWechat(); return [ 'profit_sharing_relation_type' => 'RELATION_TYPE_SERVICE_PROVIDER', 'payee_type' => 'PAYEE_TYPE_EXTERNAL_MERCHANT', 'payee_id' => (string)$wechat_config['sp_mch_id'], 'payee_name' => (string)$wechat_config['sp_name'], ]; } //分账方添加 public static function sharingaccountAdd($store_id, $account) { return self::_addprofitsharingaccount($store_id, $account); } //分账方删除 public static function sharingaccountDel($store_id, $account) { return self::_delprofitsharingaccount($store_id, $account); } public static function shareFinish($order) { $store_id = $order->store_id; $share = self::_finishprofitsharingorder($store_id, $order); return $share; } public static function share($order, $receiver) { $store_id = $order->store_id; $receivers_share = [ "profit_fee" => intval($receiver['amount'] * 100), "receiver_type" => "PAYEE_TYPE_EXTERNAL_USER", "receiver_account" => $receiver['account'], ]; $account = [ 'profit_sharing_relation_type' => 'RELATION_TYPE_DISTRIBUTOR', 'payee_type' => 'PAYEE_TYPE_EXTERNAL_USER', 'payee_id' => (string)$receiver['account'], 'payee_name' => (string)$receiver['name'], ]; if($receiver['type'] == 2){ $receivers_share['receiver_type'] = 'PAYEE_TYPE_EXTERNAL_MERCHANT'; $account['payee_type'] = 'PAYEE_TYPE_EXTERNAL_MERCHANT'; if(self::sharingaccountIsSaas($receiver['account'])){ $account['profit_sharing_relation_type'] = 'RELATION_TYPE_SERVICE_PROVIDER'; } } $sharingaccountAdd = self::sharingaccountAdd($store_id, $account); if($sharingaccountAdd['code']){ return $sharingaccountAdd; } return self::_createprofitsharingorder($store_id, $order, $receivers_share); } }