B2bPay.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830
  1. <?php
  2. /**
  3. * 重庆赤晓店信息科技有限公司
  4. * https://www.chixiaodian.com
  5. * Copyright (c) 2023 赤店商城 All rights reserved.
  6. */
  7. namespace app\utils\Wechat\B2b;
  8. use app\models\LevelOrder;
  9. use app\models\Option;
  10. use app\models\Order;
  11. use app\models\OrderUnion;
  12. use app\models\ReOrder;
  13. use app\models\SaasUser;
  14. use app\models\Store;
  15. use app\models\WechatConfig;
  16. use app\modules\client\models\v1\OpenidForm;
  17. use app\utils\DataTransform;
  18. use app\utils\OrderNo;
  19. use EasyWeChat\Kernel\Exceptions\Exception;
  20. use GuzzleHttp\Exception\GuzzleException;
  21. use yii\base\BaseObject;
  22. use function GuzzleHttp\Psr7\str;
  23. use app\models\DeliveryInfo;
  24. use app\models\AccountLog;
  25. use app\utils\Wechat\WechatProfit;
  26. use app\utils\Wechat\WechatMini;
  27. use EasyWeChat\Kernel\BaseClient;
  28. use app\modules\admin\models\UserAuditForm;
  29. use app\models\UserAuditSetting;
  30. /**
  31. * 微信支付统一入口
  32. * Class WechatPay
  33. * @package app\utils\Wechat
  34. */
  35. class B2bPay extends WechatProfit
  36. {
  37. public const PROFIT_RATE = 22;
  38. public static function notify($msg)
  39. {
  40. // $msg = json_decode('{"ToUserName":"gh_ec8671eadad1","FromUserName":"oEZ6k7WyFRJiWfqY4z-VGI4ve6G8","CreateTime":"1742262661","MsgType":"event","Event":"retail_pay_notify","appid":"wxc5187d826bd7883d","mchid":"1709890446","out_trade_no":"ML20250318094814299906","order_id":"o2025031809503260924345","pay_status":"ORDER_PAY_SUCC","pay_time":"2025-03-18 09:51:00","attach":null,"payer_openid":"oEZ6k7WyFRJiWfqY4z-VGI4ve6G8","amount":{"order_amount":"1","payer_amount":"1","currency":"CNY"},"wxpay_transaction_id":"4200002654202503180894609298","env":"0","pay_channel":"0"}', true);
  41. // $msg = json_decode('{"ToUserName":"gh_ec8671eadad1","FromUserName":"oEZ6k7XjoGPuS7GdZ8tFhXsM9UkY","CreateTime":"1742373383","MsgType":"event","Event":"retail_pay_notify","appid":"wxc5187d826bd7883d","mchid":"1709890446","out_trade_no":"ML20250319163553225267","order_id":"o202503191636134166762951","pay_status":"ORDER_PAY_SUCC","pay_time":"2025-03-19 16:36:22","attach":"[297,14560]","payer_openid":"oEZ6k7XjoGPuS7GdZ8tFhXsM9UkY","amount":{"order_amount":"1","payer_amount":"1","currency":"CNY"},"wxpay_transaction_id":"4200002651202503194212990177","env":"0","pay_channel":"0"}', true);
  42. $attach = $msg['attach'] ? json_decode($msg['attach']) : [];
  43. $msg['transaction_id'] = $msg['wxpay_transaction_id'];
  44. return (new \app\modules\common\models\WechatNotifyForm(['b2b' => 1]))->notifyIndex($attach['store_id'], (object)$msg);
  45. }
  46. public static function getMinProfitRate(){
  47. $rate = Option::get('platform_wechat_b2b_store_min_rate', 0, 'saas');
  48. if($rate < self::PROFIT_RATE){
  49. $rate = self::PROFIT_RATE;
  50. }
  51. return $rate;
  52. }
  53. public static function getMchid($store_id){
  54. $userAuditSetting = UserAuditSetting::findOne(['store_id' => $store_id]);
  55. $mchid = $userAuditSetting['wx_b2b_pay_mchid'];
  56. return $mchid;
  57. }
  58. public static function getAppkey($app, $mchid = '')
  59. {
  60. $client = new BaseClient($app);
  61. $data = [
  62. 'mchid' => $mchid,
  63. ];
  64. /**
  65. * {
  66. ["appkey"]=>
  67. string(32) "cVEjHiCnJNNG1wXa6dRczWd8J8hFT9AD"
  68. ["sandbox_appkey"]=>
  69. string(32) "Kf4vjwnr4a0CLpQ33Ft30vxbJFCtcoy6"
  70. ["errcode"]=>
  71. int(0)
  72. ["errmsg"]=>
  73. string(2) "OK"
  74. }
  75. */
  76. $result = $client->httpPostJson("/retail/B2b/getappkey", $data);
  77. if (!empty($result)) {
  78. if ($result['errcode'] == '0') {
  79. return $result['appkey'];
  80. }
  81. }
  82. \Yii::warning(['--------wxOrderGet-error---- ', $result]);
  83. return '';
  84. }
  85. public static function calcPaySig(string $uri, string $post_body, string $appkey): string {
  86. $need_sign_msg = $uri . '&' . $post_body;
  87. return hash_hmac('sha256', $need_sign_msg, $appkey);
  88. }
  89. public static function calcSignature(string $post_body, string $session_key): string {
  90. return hash_hmac('sha256', $post_body, $session_key);
  91. }
  92. public static function pay($session_key, $order, $type, $goods_names = null, $balance_price = 0)
  93. {
  94. $store_id = $order->store_id;
  95. $mchid = self::getMchid($store_id);
  96. $app = WechatMini::getWechatConfig($store_id);
  97. $appkey = self::getAppkey($app, $mchid);
  98. $pay_price = $order->pay_price;
  99. $post_body = [
  100. 'attach' => json_encode([$store_id, $order->id]),
  101. "mchid" => $mchid,
  102. "out_trade_no" => $order->order_no,
  103. 'description' => $goods_names,
  104. 'amount' => [
  105. 'order_amount' => (int)($balance_price > 0 ? floatval($pay_price - $balance_price) * 100 : $pay_price * 100),
  106. ],
  107. 'env' => 0,
  108. ];
  109. // 是否走分账
  110. if (is_profit_sharing()) {
  111. $post_body['need_profit_sharing'] = 1;
  112. }
  113. // $session = $app->auth->session($this->code);
  114. // $session_key = $session['session_key'];
  115. $res = [
  116. 'signData' => json_encode($post_body, JSON_UNESCAPED_UNICODE),
  117. 'mode' => 'retail_pay_goods',
  118. 'paySig' => self::calcPaySig('requestCommonPayment', json_encode($post_body, JSON_UNESCAPED_UNICODE), $appkey),
  119. 'signature' => self::calcSignature(json_encode($post_body, JSON_UNESCAPED_UNICODE), $session_key),
  120. ];
  121. return [
  122. 'code' => 0,
  123. 'data' => $res,
  124. 'res' => $res,
  125. 'body' => $goods_names
  126. ];
  127. }
  128. public static function orderRefund($order, $type, $refundFee, $orderRefundNo, $refund_account = null) {
  129. try {
  130. $store_id = $order->store_id;
  131. $mchid = self::getMchid($store_id);
  132. $app = WechatMini::getWechatConfig($store_id);
  133. $appkey = self::getAppkey($app, $mchid);
  134. $uri = '/retail/B2b/refund';
  135. if ($type != OrderNo::ORDER_UNION) {
  136. if ($order->is_combine_pay == 1 && $order->combine_money > 0) {
  137. $payPrice = round($order->pay_price - $order->combine_money, 2);
  138. } else {
  139. $payPrice = $order->pay_price;
  140. }
  141. } else {
  142. // 联合订单支付的总额
  143. // $payPrice = $order->price;
  144. }
  145. if ($order->is_combine_pay == 1 && $order->combine_money > 0) {
  146. $refundFee = round($refundFee - $order->combine_money, 2);
  147. }
  148. $post_body = [
  149. "mchid" => $mchid,
  150. "out_trade_no" => $order->order_no,
  151. 'out_refund_no' => $orderRefundNo,
  152. 'refund_amount' => intval(bcmul($refundFee, 100)),
  153. 'refund_from' => 1,
  154. ];
  155. $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey);
  156. $client = new BaseClient($app);
  157. $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]);
  158. debug_log([$result], __CLASS__ . '.log');
  159. if (empty($result) || $result['errcode']) {
  160. \Yii::warning(['--------wxOrderGet-error---- ', $result]);
  161. return [
  162. 'code' => 1,
  163. 'msg' => "获取失败" . $result['errmsg'],
  164. 'res' => $result,
  165. ];
  166. }
  167. // 联合支付,退余额
  168. if ($order->is_combine_pay == 1 && $order->combine_money > 0) {
  169. 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}");
  170. }
  171. return [
  172. 'code' => 0,
  173. 'msg' => "获取成功",
  174. 'res' => $result,
  175. ];
  176. } catch (\Exception $e) {
  177. return [
  178. 'code' => 1,
  179. 'msg' => $e->getMessage()
  180. ];
  181. }
  182. }
  183. public static function find($store_id, $order_no) {
  184. if (!$order_no) {
  185. return [
  186. 'code' => 1,
  187. 'msg' => '缺少必要参数'
  188. ];
  189. }
  190. try {
  191. $mchid = self::getMchid($store_id);
  192. $app = WechatMini::getWechatConfig($store_id);
  193. $appkey = self::getAppkey($app, $mchid);
  194. $uri = '/retail/B2b/getorder';
  195. $client = new BaseClient($app);
  196. $post_body = [
  197. 'mchid' => $mchid,
  198. 'out_trade_no' => $order_no,
  199. ];
  200. $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey);
  201. $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]);
  202. if (!empty($result)) {
  203. if ($result['errcode'] == '0') {
  204. return [
  205. 'code' => 0,
  206. 'msg' => "获取成功",
  207. 'data' => $result,
  208. ];
  209. }
  210. }
  211. \Yii::warning(['--------wxOrderGet-error---- ', $result]);
  212. return [
  213. 'code' => 1,
  214. 'msg' => "获取失败" . $result['errmsg'],
  215. 'data' => $result,
  216. ];
  217. } catch (Exception $e) {
  218. \Yii::warning(['WECHAT QUERY ORDER <==========>', $order_no, $e->getMessage()]);
  219. }
  220. return [
  221. 'code' => 1,
  222. 'msg' => 'fail',
  223. 'data' => false
  224. ];
  225. }
  226. public static function setmchprofitrate($store_id, $profit_rate) {
  227. try {
  228. $mchid = self::getMchid($store_id);
  229. $app = WechatMini::getWechatConfig($store_id);
  230. $appkey = self::getAppkey($app, $mchid);
  231. $uri = '/retail/B2b/setmchprofitrate';
  232. $client = new BaseClient($app);
  233. $post_body = [
  234. 'sub_mchid' => $mchid,
  235. 'profit_rate' => $profit_rate,
  236. ];
  237. $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey);
  238. $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]);
  239. if (!empty($result)) {
  240. if ($result['errcode'] == '0') {
  241. return [
  242. 'code' => 0,
  243. 'msg' => "获取成功",
  244. 'data' => $result,
  245. '$post_body' => $post_body,
  246. ];
  247. }
  248. }
  249. \Yii::warning(['--------setmchprofitrate-error---- ', $store_id, $profit_rate, $post_body, $result]);
  250. return [
  251. 'code' => 1,
  252. 'msg' => "获取失败" . $result['errmsg'],
  253. 'data' => $result,
  254. '$post_body' => $post_body,
  255. ];
  256. } catch (Exception $e) {
  257. \Yii::warning(['setmchprofitrate <==========>', $store_id, $profit_rate, $e->getMessage()]);
  258. }
  259. return [
  260. 'code' => 1,
  261. 'msg' => 'fail',
  262. 'data' => false
  263. ];
  264. }
  265. public static function getretailopenidlist($store_id, $params = [], $page_context = '', $pageSize = 5) {
  266. if($params['mobile']){
  267. return [
  268. 'code' => 0,
  269. 'msg' => "获取成功",
  270. 'data' => [[
  271. 'data' => self::getretailinfo($store_id, 0, trim($params['mobile'])),
  272. ]],
  273. 'page_context' => '',
  274. 'result' => [],
  275. ];
  276. }
  277. $app = WechatMini::getWechatConfig($store_id);
  278. $uri = '/wxa/business/getretailopenidlist';
  279. $client = new BaseClient($app);
  280. $post_body = [
  281. 'limit' => $pageSize,
  282. ];
  283. $page_context && $post_body['page_context'] = $page_context;
  284. $result = $client->httpPostJson($uri, $post_body);
  285. if (!empty($result)) {
  286. if ($result['errcode'] == '0') {
  287. $data = [];
  288. foreach($result['openid_list'] as $openid){
  289. $data[] = [
  290. 'openid' => $openid,
  291. 'data' => self::getretailinfo($store_id, $openid),
  292. ];
  293. }
  294. return [
  295. 'code' => 0,
  296. 'msg' => "获取成功",
  297. 'data' => $data,
  298. 'page_context' => $result['page_context'],
  299. 'result' => $result,
  300. // '_queryprofitsharingremainamt' => self::_queryprofitsharingremainamt($store_id, Order::findOne(15176)),
  301. // '_queryprofitsharingorder' => self::_queryprofitsharingorder($store_id, Order::findOne(15176), [
  302. // "receiver_type" => "PAYEE_TYPE_EXTERNAL_MERCHANT",
  303. // "receiver_account" => "1508102711"
  304. // ]),
  305. // 'b2bProfitSharing' => (new \app\utils\OrderUtil())->b2bProfitSharing(Order::findOne(15176)),
  306. // 'sharingaccountDel' => self::sharingaccountDel($store_id, self::sharingaccountSaas()),
  307. // 'sharingaccountAdd' => self::sharingaccountAdd($store_id, self::sharingaccountSaas()),
  308. // '_queryprofitsharingaccount' => self::_queryprofitsharingaccount($store_id),
  309. ];
  310. }
  311. }
  312. \Yii::warning(['--------wxOrderGet-error---- ', $result]);
  313. return [
  314. 'code' => 1,
  315. 'msg' => "获取失败" . $result['errmsg'],
  316. // '$result' => $result,
  317. ];
  318. }
  319. public static function getretailinfo($store_id, $openid = '', $mobile_phone = '') {
  320. $app = WechatMini::getWechatConfig($store_id);
  321. $uri = '/wxa/business/getretailinfo';
  322. $client = new BaseClient($app);
  323. $post_body = [];
  324. $openid && $post_body['openid'] = $openid;
  325. $mobile_phone && $post_body['mobile_phone'] = $mobile_phone;
  326. $result = $client->httpPostJson($uri, $post_body);
  327. if (!empty($result)) {
  328. if ($result['errcode'] == '0') {
  329. return [
  330. 'code' => 0,
  331. 'msg' => "获取成功",
  332. 'data' => $result['info'],
  333. // '$result' => $result,
  334. ];
  335. }
  336. }
  337. \Yii::warning(['--------wxOrderGet-error---- ', $result]);
  338. return [
  339. 'code' => 1,
  340. 'msg' => "获取失败" . $result['errmsg'],
  341. // '$result' => $result,
  342. ];
  343. }
  344. //查询商户号开通状态--当前店铺配置的商户号
  345. public static function mchorder($store_id) {
  346. $retailgetmchorder = self::_retailgetmchorder($store_id);
  347. if($retailgetmchorder['code']){
  348. return $retailgetmchorder;
  349. }
  350. $data = null;
  351. $result = $retailgetmchorder['data'];
  352. $mchid = self::getMchid($store_id);
  353. if($result['list']){
  354. foreach($result['list'] as $item){
  355. if($mchid == $item['inner_resp']['sub_mchid']){
  356. $data = $item;
  357. break;
  358. }
  359. if($item['inner_resp']['sub_merchant_registration_status'] && $mchid == $item['inner_resp']['sub_merchant_registration_status']['sub_mchid']){
  360. $data = $item;
  361. break;
  362. }
  363. }
  364. }
  365. return [
  366. 'code' => 0,
  367. 'msg' => "获取成功",
  368. 'data' => $data,
  369. ];
  370. }
  371. //查询商户号开通状态
  372. public static function _retailgetmchorder($store_id) {
  373. try {
  374. $app = WechatMini::getWechatConfig($store_id);
  375. $uri = '/retail/B2b/retailgetmchorder';
  376. $client = new BaseClient($app);
  377. $post_body = [
  378. ];
  379. $result = $client->httpPostJson($uri, $post_body);
  380. if (!empty($result)) {
  381. if ($result['errcode'] == '0') {
  382. return [
  383. 'code' => 0,
  384. 'msg' => "获取成功",
  385. 'data' => $result,
  386. ];
  387. }
  388. }
  389. \Yii::warning(['--------retailgetmchorder-error---- ', $result]);
  390. return [
  391. 'code' => 1,
  392. 'msg' => "获取失败" . $result['errmsg'],
  393. 'data' => $result,
  394. ];
  395. } catch (Exception $e) {
  396. \Yii::warning(['retailgetmchorder <==========>', $store_id, $e->getMessage()]);
  397. }
  398. return [
  399. 'code' => 1,
  400. 'msg' => 'fail',
  401. 'data' => false
  402. ];
  403. }
  404. public static function _getRid($store_id, $rid) {
  405. try {
  406. $app = WechatMini::getWechatConfig($store_id);
  407. $uri = '/cgi-bin/openapi/rid/get';
  408. $client = new BaseClient($app);
  409. $post_body = [
  410. 'rid' => $rid
  411. ];
  412. $result = $client->httpPostJson($uri, $post_body, ['rid' => $rid]);
  413. if (!empty($result)) {
  414. if ($result['errcode'] == '0') {
  415. return [
  416. 'code' => 0,
  417. 'msg' => "获取成功",
  418. 'data' => $result,
  419. ];
  420. }
  421. }
  422. \Yii::warning(['--------getRid-error---- ', $result]);
  423. return [
  424. 'code' => 1,
  425. 'msg' => "获取失败" . $result['errmsg'],
  426. 'data' => $result,
  427. ];
  428. } catch (Exception $e) {
  429. \Yii::warning(['getRid <==========>', $store_id, $e->getMessage()]);
  430. }
  431. return [
  432. 'code' => 1,
  433. 'msg' => 'fail',
  434. 'data' => false
  435. ];
  436. }
  437. //查询分账方
  438. public static function _queryprofitsharingaccount($store_id) {
  439. try {
  440. $app = WechatMini::getWechatConfig($store_id);
  441. $uri = '/retail/B2b/queryprofitsharingaccount';
  442. $client = new BaseClient($app);
  443. $post_body = [];
  444. $mchid = self::getMchid($store_id);
  445. $appkey = self::getAppkey($app, $mchid);
  446. $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey);
  447. $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]);
  448. if (!empty($result)) {
  449. if ($result['errcode'] == '0') {
  450. return [
  451. 'code' => 0,
  452. 'msg' => "获取成功",
  453. 'data' => $result,
  454. ];
  455. }
  456. }
  457. \Yii::warning(['--------queryprofitsharingaccount-error---- ', $result]);
  458. return [
  459. 'code' => 1,
  460. 'msg' => "获取失败" . $result['errmsg'],
  461. 'data' => $result,
  462. ];
  463. } catch (Exception $e) {
  464. \Yii::warning(['queryprofitsharingaccount <==========>', $store_id, $e->getMessage()]);
  465. }
  466. return [
  467. 'code' => 1,
  468. 'msg' => 'fail',
  469. 'data' => false
  470. ];
  471. }
  472. //添加分账方
  473. public static function _addprofitsharingaccount($store_id, $account = []) {
  474. try {
  475. $app = WechatMini::getWechatConfig($store_id);
  476. $uri = '/retail/B2b/addprofitsharingaccount';
  477. $client = new BaseClient($app);
  478. /**
  479. * {
  480. "profit_sharing_relation_type": "RELATION_TYPE_SERVICE_PROVIDER",
  481. "payee_type": "PAYEE_TYPE_EXTERNAL_MERCHANT",
  482. "payee_id": "1234567890",
  483. "payee_name": "XXX公司"
  484. }
  485. */
  486. $post_body = $account;
  487. $mchid = self::getMchid($store_id);
  488. $appkey = self::getAppkey($app, $mchid);
  489. $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey);
  490. $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]);
  491. /**
  492. * errcorde errmsg
  493. 210001 添加分账方频繁
  494. 210002 添加分账方传入环境参数错误
  495. 210003 添加分账方传入账户类型错误
  496. 210004 重复添加分账方
  497. 210006 单个小程序绑定的分账方超过上限
  498. */
  499. if (!empty($result)) {
  500. if (in_array($result['errcode'], [0, 210004])) {
  501. return [
  502. 'code' => 0,
  503. 'msg' => "获取成功",
  504. 'data' => $result,
  505. ];
  506. }
  507. }
  508. \Yii::warning(['--------addprofitsharingaccount-error---- ', $result]);
  509. return [
  510. 'code' => $result['errcode'] ?? 1,
  511. 'msg' => "获取失败" . $result['errmsg'],
  512. 'data' => $result,
  513. ];
  514. } catch (Exception $e) {
  515. \Yii::warning(['addprofitsharingaccount <==========>', $store_id, $e->getMessage()]);
  516. }
  517. return [
  518. 'code' => 1,
  519. 'msg' => 'fail',
  520. 'data' => false
  521. ];
  522. }
  523. //删除分账方
  524. public static function _delprofitsharingaccount($store_id, $account = []) {
  525. try {
  526. $app = WechatMini::getWechatConfig($store_id);
  527. $uri = '/retail/B2b/delprofitsharingaccount';
  528. $client = new BaseClient($app);
  529. /* {
  530. "payee_type": "PAYEE_TYPE_EXTERNAL_MERCHANT",
  531. "payee_id": "1234567890"
  532. }
  533. */
  534. $post_body = $account;
  535. $mchid = self::getMchid($store_id);
  536. $appkey = self::getAppkey($app, $mchid);
  537. $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey);
  538. $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]);
  539. if (!empty($result)) {
  540. if ($result['errcode'] == '0') {
  541. return [
  542. 'code' => 0,
  543. 'msg' => "获取成功",
  544. 'data' => $result,
  545. ];
  546. }
  547. }
  548. \Yii::warning(['--------delprofitsharingaccount-error---- ', $result]);
  549. return [
  550. 'code' => 1,
  551. 'msg' => "获取失败" . $result['errmsg'],
  552. 'data' => $result,
  553. ];
  554. } catch (Exception $e) {
  555. \Yii::warning(['delprofitsharingaccount <==========>', $store_id, $e->getMessage()]);
  556. }
  557. return [
  558. 'code' => 1,
  559. 'msg' => 'fail',
  560. 'data' => false
  561. ];
  562. }
  563. //请求分账
  564. public static function _createprofitsharingorder($store_id, $order, $account = []) {
  565. try {
  566. $app = WechatMini::getWechatConfig($store_id);
  567. $uri = '/retail/B2b/createprofitsharingorder';
  568. $client = new BaseClient($app);
  569. /* {
  570. "mchid": "1654806452",
  571. "out_trade_no": "400128912435668818922107515929",
  572. "profit_fee": 100,
  573. "receiver_type": "PAYEE_TYPE_EXTERNAL_MERCHANT",
  574. "receiver_account": "1654806451"
  575. }
  576. */
  577. $mchid = self::getMchid($store_id);
  578. $appkey = self::getAppkey($app, $mchid);
  579. $post_body = $account;
  580. $post_body['mchid'] = $mchid;
  581. $post_body['out_trade_no'] = $order['order_no'];
  582. $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey);
  583. $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]);
  584. if (!empty($result)) {
  585. if ($result['errcode'] == '0') {
  586. return [
  587. 'code' => 0,
  588. 'msg' => "获取成功",
  589. 'data' => $result,
  590. ];
  591. }
  592. }
  593. \Yii::warning(['--------createprofitsharingorder-error---- ', $result]);
  594. return [
  595. 'code' => $result['errcode'] ?? 1,
  596. 'msg' => $result['errmsg'],
  597. 'data' => $result,
  598. ];
  599. } catch (Exception $e) {
  600. \Yii::warning(['createprofitsharingorder <==========>', $store_id, $e->getMessage()]);
  601. }
  602. return [
  603. 'code' => 1,
  604. 'msg' => 'fail',
  605. 'data' => false
  606. ];
  607. }
  608. //可用于查询某笔支付单的某笔分账单是否已经分账完成
  609. public static function _queryprofitsharingorder($store_id, $order, $account = []) {
  610. try {
  611. $app = WechatMini::getWechatConfig($store_id);
  612. $uri = '/retail/B2b/queryprofitsharingorder';
  613. $client = new BaseClient($app);
  614. /* {
  615. "mchid": "1654806452",
  616. "out_trade_no": "400128912435668818922107515929",
  617. "receiver_type": "PAYEE_TYPE_EXTERNAL_MERCHANT",
  618. "receiver_account": "1654806451"
  619. }
  620. */
  621. $mchid = self::getMchid($store_id);
  622. $appkey = self::getAppkey($app, $mchid);
  623. $post_body = $account;
  624. $post_body['mchid'] = $mchid;
  625. $post_body['out_trade_no'] = $order['order_no'];
  626. $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey);
  627. $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]);
  628. if (!empty($result)) {
  629. if ($result['errcode'] == '0') {
  630. return [
  631. 'code' => 0,
  632. 'msg' => "获取成功",
  633. 'data' => $result,
  634. ];
  635. }
  636. }
  637. \Yii::warning(['--------queryprofitsharingorder-error---- ', $result]);
  638. return [
  639. 'code' => 1,
  640. 'msg' => "获取失败" . $result['errmsg'],
  641. 'data' => $result,
  642. ];
  643. } catch (Exception $e) {
  644. \Yii::warning(['queryprofitsharingorder <==========>', $store_id, $e->getMessage()]);
  645. }
  646. return [
  647. 'code' => 1,
  648. 'msg' => 'fail',
  649. 'data' => false
  650. ];
  651. }
  652. //查询分账剩余金额
  653. public static function _queryprofitsharingremainamt($store_id, $order) {
  654. try {
  655. $app = WechatMini::getWechatConfig($store_id);
  656. $uri = '/retail/B2b/queryprofitsharingremainamt';
  657. $client = new BaseClient($app);
  658. /* {
  659. "mchid": "1654806452",
  660. "out_trade_no": "400128912435668818922107515929",
  661. }
  662. */
  663. $mchid = self::getMchid($store_id);
  664. $appkey = self::getAppkey($app, $mchid);
  665. $post_body = [];
  666. $post_body['mchid'] = $mchid;
  667. $post_body['out_trade_no'] = $order['order_no'];
  668. $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey);
  669. $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]);
  670. if (!empty($result)) {
  671. if ($result['errcode'] == '0') {
  672. return [
  673. 'code' => 0,
  674. 'msg' => "获取成功",
  675. 'data' => $result,
  676. ];
  677. }
  678. }
  679. \Yii::warning(['--------queryprofitsharingremainamt-error---- ', $result]);
  680. return [
  681. 'code' => 1,
  682. 'msg' => "获取失败" . $result['errmsg'],
  683. 'data' => $result,
  684. ];
  685. } catch (Exception $e) {
  686. \Yii::warning(['queryprofitsharingremainamt <==========>', $store_id, $e->getMessage()]);
  687. }
  688. return [
  689. 'code' => 1,
  690. 'msg' => 'fail',
  691. 'data' => false
  692. ];
  693. }
  694. //完成分账
  695. public static function _finishprofitsharingorder($store_id, $order) {
  696. try {
  697. $app = WechatMini::getWechatConfig($store_id);
  698. $uri = '/retail/B2b/finishprofitsharingorder';
  699. $client = new BaseClient($app);
  700. /* {
  701. "mchid": "1654806452",
  702. "out_trade_no": "400128912435668818922107515929",
  703. }
  704. */
  705. $mchid = self::getMchid($store_id);
  706. $appkey = self::getAppkey($app, $mchid);
  707. $post_body = [];
  708. $post_body['mchid'] = $mchid;
  709. $post_body['out_trade_no'] = $order['order_no'];
  710. $pay_sig = self::calcPaySig($uri, json_encode($post_body), $appkey);
  711. $result = $client->httpPostJson($uri, $post_body, ['pay_sig' => $pay_sig]);
  712. if (!empty($result)) {
  713. if ($result['errcode'] == '0') {
  714. return [
  715. 'code' => 0,
  716. 'msg' => "获取成功",
  717. 'data' => $result,
  718. ];
  719. }
  720. }
  721. \Yii::warning(['--------finishprofitsharingorder-error---- ', $result]);
  722. return [
  723. 'code' => 1,
  724. 'msg' => "获取失败" . $result['errmsg'],
  725. 'data' => $result,
  726. ];
  727. } catch (Exception $e) {
  728. \Yii::warning(['finishprofitsharingorder <==========>', $store_id, $e->getMessage()]);
  729. }
  730. return [
  731. 'code' => 1,
  732. 'msg' => 'fail',
  733. 'data' => false
  734. ];
  735. }
  736. //分账方 是否是 saas平台
  737. public static function sharingaccountIsSaas($account) {
  738. return self::sharingaccountSaas()['sp_mch_id'] == $account;
  739. }
  740. //分账方 saas平台
  741. public static function sharingaccountSaas() {
  742. $wechat_config = Option::getSaasWechat();
  743. return [
  744. 'profit_sharing_relation_type' => 'RELATION_TYPE_SERVICE_PROVIDER',
  745. 'payee_type' => 'PAYEE_TYPE_EXTERNAL_MERCHANT',
  746. 'payee_id' => (string)$wechat_config['sp_mch_id'],
  747. 'payee_name' => (string)$wechat_config['sp_name'],
  748. ];
  749. }
  750. //分账方添加
  751. public static function sharingaccountAdd($store_id, $account) {
  752. return self::_addprofitsharingaccount($store_id, $account);
  753. }
  754. //分账方删除
  755. public static function sharingaccountDel($store_id, $account) {
  756. return self::_delprofitsharingaccount($store_id, $account);
  757. }
  758. public static function shareFinish($order) {
  759. $store_id = $order->store_id;
  760. $share = self::_finishprofitsharingorder($store_id, $order);
  761. return $share;
  762. }
  763. public static function share($order, $receiver) {
  764. $store_id = $order->store_id;
  765. $receivers_share = [
  766. "profit_fee" => intval($receiver['amount'] * 100),
  767. "receiver_type" => "PAYEE_TYPE_EXTERNAL_USER",
  768. "receiver_account" => $receiver['account'],
  769. ];
  770. $account = [
  771. 'profit_sharing_relation_type' => 'RELATION_TYPE_DISTRIBUTOR',
  772. 'payee_type' => 'PAYEE_TYPE_EXTERNAL_USER',
  773. 'payee_id' => (string)$receiver['account'],
  774. 'payee_name' => (string)$receiver['name'],
  775. ];
  776. if($receiver['type'] == 2){
  777. $receivers_share['receiver_type'] = 'PAYEE_TYPE_EXTERNAL_MERCHANT';
  778. $account['payee_type'] = 'PAYEE_TYPE_EXTERNAL_MERCHANT';
  779. if(self::sharingaccountIsSaas($receiver['account'])){
  780. $account['profit_sharing_relation_type'] = 'RELATION_TYPE_SERVICE_PROVIDER';
  781. }
  782. }
  783. $sharingaccountAdd = self::sharingaccountAdd($store_id, $account);
  784. if($sharingaccountAdd['code']){
  785. return $sharingaccountAdd;
  786. }
  787. return self::_createprofitsharingorder($store_id, $order, $receivers_share);
  788. }
  789. }