WechatShare.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. <?php
  2. /**
  3. * 重庆赤晓店信息科技有限公司
  4. * https://www.chixiaodian.com
  5. * Copyright (c) 2023 赤店商城 All rights reserved.
  6. */
  7. namespace app\utils\Wechat;
  8. use app\models\Order;
  9. use app\models\WechatConfig;
  10. use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
  11. use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
  12. use Think\Exception;
  13. /**
  14. * 微信分账相关方法
  15. * Class WechatShare
  16. * @package app\utils\Wechat
  17. */
  18. class WechatShare extends WechatProfit
  19. {
  20. /**
  21. * 与分账方关系类型枚举:服务商
  22. */
  23. const RELATION_SERVICE_PROVIDER = 'SERVICE_PROVIDER';
  24. /**
  25. * 与分账方关系类型枚举:门店
  26. */
  27. const RELATION_STORE = 'STORE';
  28. /**
  29. * 与分账方关系类型枚举:员工
  30. */
  31. const RELATION_STAFF = 'STAFF';
  32. /**
  33. * 与分账方关系类型枚举:店主
  34. */
  35. const RELATION_STORE_OWNER = 'STORE_OWNER';
  36. /**
  37. * 与分账方关系类型枚举:合作伙伴
  38. */
  39. const RELATION_PARTNER = 'PARTNER';
  40. /**
  41. * 与分账方关系类型枚举:总部
  42. */
  43. const RELATION_HEADQUARTER = 'HEADQUARTER';
  44. /**
  45. * 与分账方关系类型枚举:品牌方
  46. */
  47. const RELATION_BRAND = 'BRAND';
  48. /**
  49. * 与分账方关系类型枚举:分销商
  50. */
  51. const RELATION_DISTRIBUTOR = 'DISTRIBUTOR';
  52. /**
  53. * 与分账方关系类型枚举:用户
  54. */
  55. const RELATION_USER = 'USER';
  56. /**
  57. * 与分账方关系类型枚举:供应商
  58. */
  59. const RELATION_SUPPLIER = 'SUPPLIER';
  60. public static $validRelation = [
  61. self::RELATION_BRAND,
  62. self::RELATION_SERVICE_PROVIDER,
  63. self::RELATION_STORE,
  64. self::RELATION_STAFF,
  65. self::RELATION_STORE_OWNER,
  66. self::RELATION_PARTNER,
  67. self::RELATION_HEADQUARTER,
  68. self::RELATION_DISTRIBUTOR,
  69. self::RELATION_USER,
  70. self::RELATION_SUPPLIER
  71. ];
  72. /**
  73. * 分账接收方类型枚举:商户号 mch_id或者sub_mch_id
  74. */
  75. const RECEIVE_MERCHANT_ID = 'MERCHANT_ID';
  76. /**
  77. * 分账接收方类型枚举:个人openid
  78. */
  79. const RECEIVE_PERSONAL_OPENID = 'PERSONAL_OPENID';
  80. /**
  81. * 分账接收方类型枚举:个人sub_openid
  82. */
  83. const RECEIVE_PERSONAL_SUB_OPENID = 'PERSONAL_SUB_OPENID';
  84. public static $validReceive = [
  85. self::RECEIVE_MERCHANT_ID,
  86. self::RECEIVE_PERSONAL_OPENID,
  87. self::RECEIVE_PERSONAL_SUB_OPENID
  88. ];
  89. /**
  90. * 添加分账接收方
  91. * @param string $account
  92. * @param string $name
  93. * @param string $relation_type
  94. * @param string $type
  95. * @param boolean $is_app
  96. * @return array
  97. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  98. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  99. * @throws \GuzzleHttp\Exception\GuzzleException
  100. */
  101. public static function addReceivers($account, $relation_type, $type, $name = null, $is_app = false, $store_id = null, $is_platform = 0, $order = null)
  102. {
  103. if (!in_array($type, self::$validReceive)) {
  104. return [
  105. 'code' => 1,
  106. 'msg' => '非法的分账接收方类型'
  107. ];
  108. }
  109. if (!in_array($relation_type, self::$validRelation)) {
  110. return [
  111. 'code' => 1,
  112. 'msg' => '非法的与分账方关系类型'
  113. ];
  114. }
  115. if (empty($account)) {
  116. return [
  117. 'code' => 1,
  118. 'msg' => '分账接收方信息不能为空'
  119. ];
  120. }
  121. try {
  122. if ($order->order_origin == Order::ORDER_SOURCE_APP) {
  123. parent::init($store_id, $is_platform, 0, WechatConfig::TYPE_CONFIG_APP);
  124. } else if ($order->order_origin == Order::ORDER_SOURCE_MP) {
  125. parent::init($store_id, $is_platform, 0, WechatConfig::TYPE_CONFIG_MP);
  126. } else if ($order->order_origin == Order::ORDER_SOURCE_WEB) {
  127. parent::init($store_id, $is_platform, 0, WechatConfig::TYPE_CONFIG_H5);
  128. } else {
  129. parent::init($store_id, $is_platform);
  130. }
  131. $receiver = [
  132. "type" => $type,
  133. "account" => $account,
  134. "relation_type" => $relation_type
  135. ];
  136. if ($name) {
  137. $receiver['name'] = $name;
  138. }
  139. $res = self::$wechat_pay->profit_sharing->addReceiver($receiver);
  140. if (!$res) {
  141. return [
  142. 'code' => 1,
  143. 'msg' => '调用失败',
  144. ];
  145. }
  146. if ($res['return_code'] != 'SUCCESS') {
  147. return [
  148. 'code' => 1,
  149. 'msg' => '添加分账接收方失败:,' . (isset($res['return_msg']) ? $res['return_msg'] : ''),
  150. 'res' => $res,
  151. ];
  152. }
  153. if ($res['result_code'] != 'SUCCESS') {
  154. return [
  155. 'code' => 1,
  156. 'msg' => '添加分账接收方结果失败:,' . (isset($res['err_code_des']) ? $res['err_code_des'] : ''),
  157. 'res' => $res,
  158. ];
  159. }
  160. return [
  161. 'code' => 0,
  162. 'msg' => '添加成功',
  163. 'res' => $res
  164. ];
  165. } catch (InvalidArgumentException $e) {
  166. return [
  167. 'code' => 1,
  168. 'msg' => $e->getMessage()
  169. ];
  170. } catch (InvalidConfigException $e) {
  171. return [
  172. 'code' => 1,
  173. 'msg' => $e->getMessage()
  174. ];
  175. } catch (\Exception $e) {
  176. return [
  177. 'code' => 1,
  178. 'msg' => $e->getMessage()
  179. ];
  180. }
  181. }
  182. /**
  183. * 删除分账接收方
  184. * @param string $account
  185. * @param string $name
  186. * @param string $relation_type
  187. * @param string $type
  188. * @param boolean $is_app
  189. * @return array
  190. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  191. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  192. * @throws \GuzzleHttp\Exception\GuzzleException
  193. */
  194. public static function deleteReceivers($account, $name, $relation_type, $type, $is_app = false)
  195. {
  196. if (!in_array($type, self::$validReceive)) {
  197. return [
  198. 'code' => 1,
  199. 'msg' => '非法的分账接收方类型'
  200. ];
  201. }
  202. if (!in_array($relation_type, self::$validRelation)) {
  203. return [
  204. 'code' => 1,
  205. 'msg' => '非法的与分账方关系类型'
  206. ];
  207. }
  208. if (empty($account) || empty($name)) {
  209. return [
  210. 'code' => 1,
  211. 'msg' => '分账接收方信息不能为空'
  212. ];
  213. }
  214. try {
  215. parent::init();
  216. $receiver = [
  217. "type" => $type,
  218. "account" => $account,
  219. "name" => $name,
  220. "relation_type" => $relation_type
  221. ];
  222. $res = self::$wechat_pay->profit_sharing->deleteReceiver($receiver);
  223. if (!$res) {
  224. return [
  225. 'code' => 1,
  226. 'msg' => '调用失败',
  227. ];
  228. }
  229. if ($res['return_code'] != 'SUCCESS') {
  230. return [
  231. 'code' => 1,
  232. 'msg' => '删除分账接收方失败:,' . (isset($res['return_msg']) ? $res['return_msg'] : ''),
  233. 'res' => $res,
  234. ];
  235. }
  236. if ($res['result_code'] != 'SUCCESS') {
  237. return [
  238. 'code' => 1,
  239. 'msg' => '删除分账接收方结果失败:,' . (isset($res['err_code_des']) ? $res['err_code_des'] : ''),
  240. 'res' => $res,
  241. ];
  242. }
  243. return [
  244. 'code' => 0,
  245. 'msg' => '删除成功',
  246. 'res' => $res
  247. ];
  248. } catch (InvalidArgumentException $e) {
  249. return [
  250. 'code' => 1,
  251. 'msg' => $e->getMessage()
  252. ];
  253. } catch (InvalidConfigException $e) {
  254. return [
  255. 'code' => 1,
  256. 'msg' => $e->getMessage()
  257. ];
  258. } catch (\Exception $e) {
  259. return [
  260. 'code' => 1,
  261. 'msg' => $e->getMessage()
  262. ];
  263. }
  264. }
  265. /**
  266. * 请求分账(单次或多次)
  267. * @param string $transaction_id
  268. * @param string $out_trade_no
  269. * @param array $receivers
  270. * @param boolean $single
  271. * @param boolean $is_app
  272. * @return array
  273. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  274. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  275. * @throws \GuzzleHttp\Exception\GuzzleException
  276. */
  277. public static function share($transaction_id, $out_trade_no, $receivers, $single = true, $is_app = false, $store_id = null, $is_platform = 0, $order = null)
  278. {
  279. if (!$transaction_id) {
  280. return [
  281. 'code' => 1,
  282. 'msg' => '微信支付订单号不能为空'
  283. ];
  284. }
  285. if (!$out_trade_no) {
  286. return [
  287. 'code' => 1,
  288. 'msg' => '商户订单号不能为空'
  289. ];
  290. }
  291. if (empty($receivers) || !is_array($receivers)) {
  292. return [
  293. 'code' => 1,
  294. 'msg' => '分账接收人信息不能为空'
  295. ];
  296. }
  297. $send_receivers = [];
  298. foreach ($receivers as $key => $receiver) {
  299. if (!in_array($receiver['type'], [1, 2, 3])) {
  300. return [
  301. 'code' => 1,
  302. 'msg' => '非法的分账接收方类型'
  303. ];
  304. }
  305. if (empty($receiver['account'])) {
  306. return [
  307. 'code' => 1,
  308. 'msg' => '分账接收方信息不能为空'
  309. ];
  310. }
  311. if ($receiver['amount'] <= 0) {
  312. return [
  313. 'code' => 1,
  314. 'msg' => '分账金额非法'
  315. ];
  316. }
  317. switch ($receiver['type']) {
  318. case 1:
  319. $receivers[$key]['type'] = self::RECEIVE_PERSONAL_OPENID;
  320. $receivers[$key]['description'] = '分账到个人';
  321. break;
  322. case 2:
  323. $receivers[$key]['type'] = self::RECEIVE_MERCHANT_ID;
  324. $receivers[$key]['description'] = '分账到商户';
  325. break;
  326. case 3:
  327. $receivers[$key]['type'] = self::RECEIVE_PERSONAL_SUB_OPENID;
  328. $receivers[$key]['description'] = '分账到个人';
  329. break;
  330. default:;
  331. }
  332. $receivers[$key]['amount'] = intval($receivers[$key]['amount'] * 100);
  333. $send_receivers[] = [
  334. 'type' => $receivers[$key]['type'],
  335. 'account' => $receivers[$key]['account'],
  336. 'amount' => $receivers[$key]['amount'],
  337. 'description' => $receivers[$key]['description'],
  338. // 'name' => $receivers[$key]['name'],
  339. ];
  340. }
  341. try {
  342. if ($order->order_origin == Order::ORDER_SOURCE_APP) {
  343. parent::init($store_id, $is_platform, 0, WechatConfig::TYPE_CONFIG_APP);
  344. } else if ($order->order_origin == Order::ORDER_SOURCE_MP) {
  345. parent::init($store_id, $is_platform, 0, WechatConfig::TYPE_CONFIG_MP);
  346. } else if ($order->order_origin == Order::ORDER_SOURCE_WEB) {
  347. parent::init($store_id, $is_platform, 0, WechatConfig::TYPE_CONFIG_H5);
  348. } else {
  349. parent::init($store_id, $is_platform);
  350. }
  351. if ($single) {
  352. $res = self::$wechat_pay->profit_sharing->share($transaction_id, $out_trade_no, $receivers);
  353. } else {
  354. \Yii::error([$transaction_id, $out_trade_no, $receivers]);
  355. $res = self::$wechat_pay->profit_sharing->multiShare($transaction_id, $out_trade_no, $send_receivers);
  356. }
  357. if (!$res) {
  358. return [
  359. 'code' => 1,
  360. 'msg' => '调用失败',
  361. ];
  362. }
  363. if ($res['return_code'] != 'SUCCESS') {
  364. return [
  365. 'code' => 1,
  366. 'msg' => isset($res['return_msg']) ? $res['return_msg'] : '',
  367. 'res' => $res,
  368. ];
  369. }
  370. if ($res['result_code'] != 'SUCCESS') {
  371. return [
  372. 'code' => 1,
  373. 'msg' => isset($res['err_code_des']) ? $res['err_code_des'] : '',
  374. 'res' => $res,
  375. ];
  376. }
  377. return [
  378. 'code' => 0,
  379. 'msg' => '成功',
  380. 'res' => $res
  381. ];
  382. } catch (InvalidArgumentException $e) {
  383. return [
  384. 'code' => 1,
  385. 'msg' => $e->getMessage()
  386. ];
  387. } catch (InvalidConfigException $e) {
  388. return [
  389. 'code' => 1,
  390. 'msg' => $e->getMessage()
  391. ];
  392. } catch (\Exception $e) {
  393. return [
  394. 'code' => 1,
  395. 'msg' => $e->getMessage()
  396. ];
  397. }
  398. }
  399. /**
  400. * 分账查询
  401. * @param string $transaction_id
  402. * @param string $out_trade_no
  403. * @param boolean $is_app
  404. * @return array
  405. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  406. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  407. * @throws \GuzzleHttp\Exception\GuzzleException
  408. */
  409. public static function query($transaction_id, $out_trade_no, $is_app = false)
  410. {
  411. if (!$transaction_id) {
  412. return [
  413. 'code' => 1,
  414. 'msg' => '微信支付订单号不能为空'
  415. ];
  416. }
  417. if (!$out_trade_no) {
  418. return [
  419. 'code' => 1,
  420. 'msg' => '商户订单号不能为空'
  421. ];
  422. }
  423. try {
  424. parent::init();
  425. $res = self::$wechat_pay->profit_sharing->query($transaction_id, $out_trade_no);
  426. if (!$res) {
  427. return [
  428. 'code' => 1,
  429. 'msg' => '调用失败',
  430. ];
  431. }
  432. if ($res['return_code'] != 'SUCCESS') {
  433. return [
  434. 'code' => 1,
  435. 'msg' => '查询分账调用失败:,' . (isset($res['return_msg']) ? $res['return_msg'] : ''),
  436. 'res' => $res,
  437. ];
  438. }
  439. if ($res['result_code'] != 'SUCCESS') {
  440. return [
  441. 'code' => 1,
  442. 'msg' => '查询分账结果失败:,' . (isset($res['err_code_des']) ? $res['err_code_des'] : ''),
  443. 'res' => $res,
  444. ];
  445. }
  446. return [
  447. 'code' => 0,
  448. 'msg' => '成功',
  449. 'res' => $res
  450. ];
  451. } catch (InvalidArgumentException $e) {
  452. return [
  453. 'code' => 1,
  454. 'msg' => $e->getMessage()
  455. ];
  456. } catch (InvalidConfigException $e) {
  457. return [
  458. 'code' => 1,
  459. 'msg' => $e->getMessage()
  460. ];
  461. } catch (\Exception $e) {
  462. return [
  463. 'code' => 1,
  464. 'msg' => $e->getMessage()
  465. ];
  466. }
  467. }
  468. /**
  469. * 分账退回
  470. * @param string $out_trade_no
  471. * @param string $out_return_no
  472. * @param string $return_amount
  473. * @param string $return_account
  474. * @param string $description
  475. * @param boolean $is_app
  476. * @return array
  477. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  478. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  479. * @throws \GuzzleHttp\Exception\GuzzleException
  480. */
  481. public static function returnShare($out_trade_no, $out_return_no, $return_amount, $return_account, $is_app = false, $description = '订单取消')
  482. {
  483. if (empty($out_trade_no)) {
  484. return [
  485. 'code' => 1,
  486. 'msg' => '退款订单号不能为空'
  487. ];
  488. }
  489. if (empty($out_return_no)) {
  490. return [
  491. 'code' => 1,
  492. 'msg' => '商户回退单号不能为空'
  493. ];
  494. }
  495. if (intval($return_amount) <= 0) {
  496. return [
  497. 'code' => 1,
  498. 'msg' => '退款金额非法'
  499. ];
  500. }
  501. if (empty($return_account)) {
  502. return [
  503. 'code' => 1,
  504. 'msg' => '回退方账户账号不能为空'
  505. ];
  506. }
  507. try {
  508. parent::init();
  509. $res = self::$wechat_pay->profit_sharing->returnShare($out_trade_no, $out_return_no, $return_amount, $return_account, $description);
  510. if (!$res) {
  511. return [
  512. 'code' => 1,
  513. 'msg' => '调用失败',
  514. ];
  515. }
  516. if ($res['return_code'] != 'SUCCESS') {
  517. return [
  518. 'code' => 1,
  519. 'msg' => '分账退回调用失败:,' . (isset($res['return_msg']) ? $res['return_msg'] : ''),
  520. 'res' => $res,
  521. ];
  522. }
  523. // 如果返回为处理中,请勿变更商户回退单号,使用相同的参数再次发起分账回退
  524. if ($res['result'] == 'PROCESSING') {
  525. return self::returnShare($out_trade_no, $out_return_no, $return_amount, $return_account, $description);
  526. }
  527. if ($res['result'] == 'FAILED') {
  528. if ($res['fail_reason'] == 'ACCOUNT_ABNORMAL') {
  529. $msg = '分账接收方账户异常';
  530. }
  531. if ($res['fail_reason'] == 'TIME_OUT_CLOSED') {
  532. $msg = '超时关单';
  533. }
  534. return [
  535. 'code' => 1,
  536. 'msg' => $msg
  537. ];
  538. }
  539. return [
  540. 'code' => 0,
  541. 'msg' => '成功',
  542. 'res' => $res
  543. ];
  544. } catch (InvalidArgumentException $e) {
  545. return [
  546. 'code' => 1,
  547. 'msg' => $e->getMessage()
  548. ];
  549. } catch (InvalidConfigException $e) {
  550. return [
  551. 'code' => 1,
  552. 'msg' => $e->getMessage()
  553. ];
  554. } catch (\Exception $e) {
  555. return [
  556. 'code' => 1,
  557. 'msg' => $e->getMessage()
  558. ];
  559. }
  560. }
  561. /**
  562. * 分账完结
  563. * @param string $transaction_id
  564. * @param string $out_trade_no
  565. * @param string $description
  566. * @param boolean $is_app
  567. * @return array
  568. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  569. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  570. * @throws \GuzzleHttp\Exception\GuzzleException
  571. */
  572. public static function complete($transaction_id, $out_trade_no, $is_app = false, $description = '分账完结', $store_id = null, $is_platform = 0)
  573. {
  574. if (!$transaction_id) {
  575. return [
  576. 'code' => 1,
  577. 'msg' => '微信支付订单号不能为空'
  578. ];
  579. }
  580. if (!$out_trade_no) {
  581. return [
  582. 'code' => 1,
  583. 'msg' => '商户订单号不能为空'
  584. ];
  585. }
  586. try {
  587. parent::init($store_id, $is_platform);
  588. $params = [
  589. "transaction_id" => $transaction_id,
  590. "out_order_no" => $out_trade_no,
  591. "description" => $description
  592. ];
  593. $res = self::$wechat_pay->profit_sharing->markOrderAsFinished($params);
  594. if (!$res) {
  595. return [
  596. 'code' => 1,
  597. 'msg' => '调用失败',
  598. ];
  599. }
  600. if ($res['return_code'] != 'SUCCESS') {
  601. return [
  602. 'code' => 1,
  603. 'msg' => '分账完结调用失败:,' . (isset($res['return_msg']) ? $res['return_msg'] : ''),
  604. 'res' => $res,
  605. ];
  606. }
  607. if ($res['result_code'] != 'SUCCESS') {
  608. return [
  609. 'code' => 1,
  610. 'msg' => '分账完结结果失败:,' . (isset($res['err_code_des']) ? $res['err_code_des'] : ''),
  611. 'res' => $res,
  612. ];
  613. }
  614. return [
  615. 'code' => 0,
  616. 'msg' => '请求成功',
  617. 'res' => $res
  618. ];
  619. } catch (InvalidArgumentException $e) {
  620. return [
  621. 'code' => 1,
  622. 'msg' => $e->getMessage()
  623. ];
  624. } catch (InvalidConfigException $e) {
  625. return [
  626. 'code' => 1,
  627. 'msg' => $e->getMessage()
  628. ];
  629. } catch (\Exception $e) {
  630. return [
  631. 'code' => 1,
  632. 'msg' => $e->getMessage()
  633. ];
  634. }
  635. }
  636. }