AlipayUnion.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. <?php
  2. /**
  3. * 重庆赤晓店信息科技有限公司
  4. * https://www.chixiaodian.com
  5. * Copyright (c) 2023 赤店商城 All rights reserved.
  6. */
  7. namespace app\utils\Alipay;
  8. use app\models\Option;
  9. use app\models\OrderUnion;
  10. use app\models\SharingReceiver;
  11. use app\models\StoreAliMini;
  12. use app\models\StoreMiniToken;
  13. use app\models\User;
  14. use app\utils\OrderNo;
  15. use Symfony\Component\HttpFoundation\Response;
  16. use Yansongda\Pay\Exceptions\GatewayException;
  17. use Yansongda\Pay\Exceptions\InvalidConfigException;
  18. use Yansongda\Pay\Exceptions\InvalidSignException;
  19. use Yansongda\Pay\Pay;
  20. use Yansongda\Supports\Collection;
  21. use yii\base\BaseObject;
  22. use yii\helpers\Json;
  23. /**
  24. * Class Alipay 证书公钥模式
  25. * 支付宝支付相关操作
  26. * @package app\utils
  27. */
  28. class AlipayUnion
  29. {
  30. const FLAG_REFUND = 'refund';
  31. const FLAG_NORMAL = 'wap';
  32. const FLAG_TRANSFER = 'transfer';
  33. private static $validFlag = [
  34. self::FLAG_NORMAL,
  35. self::FLAG_REFUND,
  36. self::FLAG_TRANSFER
  37. ];
  38. /**
  39. * @var \Yansongda\Pay\Gateways\Alipay
  40. */
  41. public static $alipay;
  42. public static $store_mini;
  43. public static $seller_id;
  44. /**
  45. * @var string
  46. * 回调url
  47. */
  48. private static $notify_url = 'alipay/notify';
  49. /**
  50. * @param $return_url
  51. * @param $level
  52. * 获取支付宝实例
  53. */
  54. private function __construct($return_url = 'www.baidu.com', $is_app = false, $store_id = 0)
  55. {
  56. if (!$is_app) {
  57. $store_mini = StoreAliMini::find()->where(['store_id' => $store_id ?: get_store_id()])->select('id, auth_token, userid')->orderBy('id desc')->asArray()->one();
  58. if (empty($store_mini)) {
  59. $store_mini = StoreAliMini::find()->where(['and', ['<>', 'auth_token', ''], ['<>', 'userid', '']])->orderBy('id desc')->asArray()->one();
  60. if (empty($store_mini)) {
  61. return [
  62. 'code' => 1,
  63. 'msg' => "获取不到商户的支付信息=-="
  64. ];
  65. }
  66. }
  67. self::$store_mini = $store_mini;
  68. }
  69. // if ($is_app) {
  70. // $store_token = StoreMiniToken::find()->where(['batch_status' => [2, 6], 'store_id' => get_store_id()])->select('app_auth_token')->asArray()->one();
  71. // $store_mini['auth_token'] = $store_token['app_auth_token'];
  72. // }
  73. $alipay_appid = Option::get('alipay_appid', '0', 'saas')['value'];
  74. $alipay_public_key = Option::get('alipay_public_key', '0', 'saas')['value'];
  75. $alipay_app_private_key = Option::get('alipay_app_private_key', '0', 'saas')['value'];
  76. //三方平台信息
  77. $config = [
  78. 'app_id' => $alipay_appid,
  79. 'notify_url' => pay_notify_url(self::$notify_url),
  80. 'return_url' => $return_url,
  81. 'private_key' => $alipay_app_private_key,
  82. 'log' => [ // optional
  83. 'file' => \Yii::$app->basePath . '/runtime/logs/alipay.log',
  84. 'level' => 'debug', // 建议生产环境等级调整为 info,开发环境为 debug
  85. 'type' => 'single', // optional, 可选 daily.
  86. 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天
  87. ],
  88. 'ali_public_key' => $alipay_public_key,
  89. 'app_auth_token' => $store_mini['auth_token']
  90. // 'app_cert_public_key' => \Yii::$app->basePath. '/utils/Alipay/CRT/app_cert_public_key.crt', // 应用公钥证书路径
  91. // 'ali_public_key' => \Yii::$app->basePath. '/utils/Alipay/CRT/alipay_cert_public_key.crt', // 支付宝公钥证书路径
  92. // 'alipay_root_cert' => \Yii::$app->basePath. '/utils/Alipay/CRT/alipay_root_cert.crt', // 支付宝根证书路径
  93. // 'mode' => 'dev' // optional,设置此参数,将进入沙箱模式
  94. ];
  95. if ((int)$store_id === 0) {
  96. $keys = [
  97. 'alipay_platform'
  98. ];
  99. $data = Option::get($keys, 0, 'saas');
  100. if(isset($data[0]['value'])) {
  101. $alipay_config = json_decode($data[0]['value'],true);
  102. $alipay_appid = $alipay_config['app_id'];
  103. $alipay_public_key = $alipay_config['alipay_public_key'];
  104. $alipay_app_private_key = $alipay_config['app_private_key'];
  105. self::$seller_id = $alipay_config['user_id'];
  106. }
  107. $config = [
  108. 'app_id' => $alipay_appid,
  109. 'notify_url' => pay_notify_url(self::$notify_url),
  110. 'return_url' => $return_url,
  111. 'private_key' => $alipay_app_private_key,
  112. 'log' => [ // optional
  113. 'file' => \Yii::$app->basePath . '/runtime/logs/alipay.log',
  114. 'level' => 'debug', // 建议生产环境等级调整为 info,开发环境为 debug
  115. 'type' => 'single', // optional, 可选 daily.
  116. 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天
  117. ],
  118. 'ali_public_key' => $alipay_public_key,
  119. // 'app_auth_token' => $store_mini['auth_token']
  120. // 'app_cert_public_key' => \Yii::$app->basePath. '/utils/Alipay/CRT/app_cert_public_key.crt', // 应用公钥证书路径
  121. // 'ali_public_key' => \Yii::$app->basePath. '/utils/Alipay/CRT/alipay_cert_public_key.crt', // 支付宝公钥证书路径
  122. // 'alipay_root_cert' => \Yii::$app->basePath. '/utils/Alipay/CRT/alipay_root_cert.crt', // 支付宝根证书路径
  123. // 'mode' => 'dev' // optional,设置此参数,将进入沙箱模式
  124. ];
  125. }
  126. self::$alipay = Pay::alipay($config);
  127. }
  128. /**
  129. * 静态方法调用construct方法
  130. * @param $return_url
  131. */
  132. public static function init($return_url = '', $is_app = 'false', $store_id = 0) {
  133. new self($return_url, $is_app, $store_id);
  134. }
  135. /**
  136. * 扫码支付
  137. * @param $order
  138. * @param $subject
  139. * @param $return_url
  140. * @return Collection | array
  141. */
  142. public static function scan($order, $subject, $return_url = '', $user_id = null, $store_id = 0) {
  143. self::init($return_url, 0, $store_id);
  144. $store_mini = self::$store_mini;
  145. if ((int)$store_id !== 0) {
  146. $seller_id = $store_mini['userid'];
  147. } else {
  148. $seller_id = self::$seller_id;
  149. }
  150. /** 逻辑开始 **/
  151. $order = [
  152. 'body' => $subject,
  153. 'out_trade_no' => $order->order_no,
  154. 'total_amount' => $order->pay_price,
  155. 'subject' => $subject,
  156. 'buyer_id' => $user_id,
  157. 'seller_id' => $seller_id
  158. ];
  159. try {
  160. $res = self::$alipay->mini($order);
  161. if ($res && $res->code === '10000' && $res->msg === 'Success') {
  162. return [
  163. 'code' => 0,
  164. 'msg' => 'success',
  165. 'data' => $res->toArray()
  166. ];
  167. }
  168. return [
  169. 'code' => $res->code,
  170. 'msg' => $res->msg,
  171. ];
  172. } catch (GatewayException $e) {
  173. return [
  174. 'code' => 1,
  175. 'msg' => $e->getMessage()
  176. ];
  177. } catch (InvalidSignException $e) {
  178. return [
  179. 'code' => 1,
  180. 'msg' => $e->getMessage()
  181. ];
  182. } catch (InvalidConfigException $e) {
  183. return [
  184. 'code' => 1,
  185. 'msg' => $e->getMessage()
  186. ];
  187. }
  188. }
  189. /**
  190. * 转账
  191. * @param $order
  192. * @param $mail
  193. * @param $return_url
  194. * @return Collection | array
  195. */
  196. public static function transfer($order, $mail, $return_url = '') {
  197. if (empty($mail)) {
  198. return [
  199. 'code' => 1,
  200. 'msg' => 'Params are wrong'
  201. ];
  202. }
  203. self::init($return_url);
  204. /** 逻辑开始 **/
  205. $order = [
  206. 'out_biz_no' => $order->order_no,
  207. 'total_amount' => $order->pay_price,
  208. 'product_code' => 'TRANS_ACCOUNT_NO_PWD',
  209. 'payee_info' => [
  210. 'identity' => $mail,
  211. 'identity_type' => 'ALIPAY_LOGON_ID',
  212. ],
  213. ];
  214. try {
  215. $res = self::$alipay->transfer($order);
  216. if ($res && $res->code === '10000' && $res->msg === 'Success') {
  217. return [
  218. 'code' => 0,
  219. 'msg' => 'success',
  220. 'data' => $res->toArray()
  221. ];
  222. }
  223. return [
  224. 'code' => $res->code,
  225. 'msg' => $res->msg,
  226. ];
  227. } catch (GatewayException $e) {
  228. return [
  229. 'code' => 1,
  230. 'msg' => $e->getMessage()
  231. ];
  232. } catch (InvalidSignException $e) {
  233. return [
  234. 'code' => 1,
  235. 'msg' => $e->getMessage()
  236. ];
  237. } catch (InvalidConfigException $e) {
  238. return [
  239. 'code' => 1,
  240. 'msg' => $e->getMessage()
  241. ];
  242. }
  243. }
  244. /**
  245. * app支付
  246. * @param $order
  247. * @param User $user
  248. * @param $subject
  249. * @param $balance_price
  250. * @param $return_url
  251. * @return Response | false | string | array
  252. */
  253. public static function app($order, $subject, $user, $balance_price = 0, $return_url = '') {
  254. self::init($return_url, true);
  255. /** 逻辑开始 **/
  256. $order = [
  257. 'body' => $subject,
  258. 'out_trade_no' => $order->order_no,
  259. 'total_amount' => $balance_price > 0 ? ($order->pay_price - $balance_price) : $order->pay_price,
  260. 'subject' => $subject,
  261. 'extend_params' => [
  262. 'royalty_freeze' => 'true'
  263. ]
  264. ];
  265. \Yii::error($order);
  266. try {
  267. $res = self::$alipay->app($order)->getContent();
  268. } catch (\Exception $e) {
  269. return [
  270. 'code' => 1,
  271. 'msg' => $e->getMessage()
  272. ];
  273. }
  274. return [
  275. 'code' => 0,
  276. 'msg' => 'success',
  277. 'data' => $res,
  278. 'res' => $res
  279. ];
  280. }
  281. /**
  282. * 批量转账
  283. * @param $params
  284. * @return Collection | array
  285. */
  286. public static function batchTransfer($params) {
  287. self::init('');
  288. /** 逻辑开始 **/
  289. $order = [
  290. 'out_batch_no' => $params['out_batch_no'],
  291. 'product_code' => 'BATCH_API_TO_ACC_NO_PWD',
  292. 'biz_scene' => 'MESSAGE_BATCH_PAY',
  293. 'total_trans_amount' => $params['total_trans_amount'],
  294. 'total_count' => 1,
  295. 'trans_order_list' => [
  296. [
  297. 'out_biz_no' => $params['out_batch_no'],
  298. 'trans_amount' => $params['total_trans_amount'],
  299. 'payee_info' => [
  300. 'identity' => $params['account'],
  301. 'identity_type' => 'ALIPAY_LOGON_ID',
  302. 'name' => $params['name']
  303. ],
  304. 'remark' => $params['title']
  305. ],
  306. ],
  307. 'order_title' => $params['title'],
  308. 'remark' => '转账',
  309. ];
  310. try {
  311. $res = self::$alipay->batchtransfer($order);
  312. \Yii::error($res);
  313. if ($res && $res->code === '10000' && $res->msg === 'Success') {
  314. return [
  315. 'code' => 0,
  316. 'msg' => 'success',
  317. 'data' => $res->toArray()
  318. ];
  319. }
  320. return [
  321. 'code' => $res->code,
  322. 'msg' => $res->msg,
  323. ];
  324. } catch (GatewayException $e) {
  325. return [
  326. 'code' => 1,
  327. 'msg' => $e->getMessage()
  328. ];
  329. } catch (InvalidSignException $e) {
  330. return [
  331. 'code' => 1,
  332. 'msg' => $e->getMessage()
  333. ];
  334. } catch (InvalidConfigException $e) {
  335. return [
  336. 'code' => 1,
  337. 'msg' => $e->getMessage()
  338. ];
  339. }
  340. }
  341. /**
  342. * 退款
  343. * @param $order
  344. * @param $return_url
  345. * @param $refund_fee
  346. * @return Collection | array
  347. */
  348. public static function refund($order, $refund_fee, $return_url = '') {
  349. self::init($return_url);
  350. /** 逻辑开始 **/
  351. $data = [
  352. 'out_trade_no' => $order->order_no,
  353. 'refund_amount' => $refund_fee,
  354. ];
  355. try {
  356. $res = self::$alipay->refund($data);
  357. if ($res && $res->code === '10000' && $res->msg === 'Success') {
  358. return [
  359. 'code' => 0,
  360. 'msg' => 'success',
  361. 'data' => $res->toArray()
  362. ];
  363. }
  364. return [
  365. 'code' => $res->code,
  366. 'msg' => $res->msg,
  367. ];
  368. } catch (GatewayException $e) {
  369. return [
  370. 'code' => 1,
  371. 'msg' => $e->getMessage()
  372. ];
  373. } catch (InvalidSignException $e) {
  374. return [
  375. 'code' => 1,
  376. 'msg' => $e->getMessage()
  377. ];
  378. } catch (InvalidConfigException $e) {
  379. return [
  380. 'code' => 1,
  381. 'msg' => $e->getMessage()
  382. ];
  383. }
  384. }
  385. /**
  386. * 小程序支付
  387. * @param $order
  388. * @param $subject
  389. * @param User $user
  390. * @param $return_url
  391. * @param $is_food
  392. * @return Collection | array
  393. */
  394. public static function mini($order, $subject, $user, $return_url = '', $type = '', $total_pay_price = 0, $balance_price = 0) {
  395. self::init($return_url);
  396. if (!$order || ($type != OrderNo::ORDER_UNION && empty($subject)) || !$user) {
  397. return [
  398. 'code' => 1,
  399. 'msg' => '参数有误'
  400. ];
  401. }
  402. if ($type == OrderNo::ORDER_UNION) {
  403. $order = [
  404. 'body' => '订单合并支付',
  405. 'out_trade_no' => OrderNo::getOrderNo(OrderNo::ORDER_UNION),
  406. 'total_amount' => $balance_price > 0 ? $total_pay_price - $balance_price : $total_pay_price,
  407. 'subject' => count($order) . '笔订单合并支付',
  408. 'buyer_id' => $user->ali_user_id
  409. ];
  410. $order_union = new OrderUnion();
  411. $order_union->store_id = get_store_id();
  412. $order_union->user_id = get_saas_user()->id;
  413. $order_union->order_no = $order['out_trade_no'];
  414. $order_union->price = $order['total_amount'];
  415. $order_union->is_pay = 0;
  416. $order_union->created_at = time();
  417. $order_union->is_delete = 0;
  418. $order_id_list = [];
  419. foreach ($order as $value) {
  420. $order_id_list[] = $value->id;
  421. }
  422. $order_union->order_id_list = json_encode($order_id_list);
  423. if (!$order_union->save()) {
  424. foreach ($order_union->errors as $error) {
  425. return [
  426. 'code' => 1,
  427. 'msg' => $error
  428. ];
  429. }
  430. }
  431. } else {
  432. $order = [
  433. 'body' => $subject,
  434. 'out_trade_no' => $order->order_no,
  435. 'total_amount' => $balance_price > 0 ? ($order->pay_price - $balance_price) : $order->pay_price,
  436. 'subject' => $subject,
  437. 'buyer_id' => $user->ali_user_id
  438. ];
  439. }
  440. try {
  441. $res = self::$alipay->mini($order);
  442. if ($res && $res->code === '10000' && $res->msg === 'Success') {
  443. return [
  444. 'code' => 0,
  445. 'msg' => 'success',
  446. 'data' => $res->toArray()
  447. ];
  448. }
  449. return [
  450. 'code' => $res->code,
  451. 'msg' => $res->msg,
  452. ];
  453. } catch (GatewayException $e) {
  454. return [
  455. 'code' => 1,
  456. 'msg' => $e->getMessage()
  457. ];
  458. } catch (InvalidSignException $e) {
  459. return [
  460. 'code' => 1,
  461. 'msg' => $e->getMessage()
  462. ];
  463. } catch (InvalidConfigException $e) {
  464. return [
  465. 'code' => 1,
  466. 'msg' => $e->getMessage()
  467. ];
  468. }
  469. }
  470. /**
  471. * 绑定关系
  472. * @param string $out_trade_no
  473. * @param array $receiver_list
  474. * @return array
  475. */
  476. public static function bind($out_trade_no, $receiver_list) {
  477. // 绑定关系必须为真实姓名。个人账户为个人真实姓名,企业账户为企业名称
  478. \Yii::warning($receiver_list);
  479. $valid_receive_type = array_keys(SharingReceiver::$validAlipayReceiveType);
  480. foreach ($receiver_list as $index => $receiver) {
  481. if (empty($receiver['account']) || !in_array($receiver['type'], $valid_receive_type) || empty($receiver['name'])) {
  482. unset($receiver_list[$index]);
  483. \Yii::warning('<========================> 订单号:' . $out_trade_no . '添加分账关系校验参数错误:' . json_encode($receiver));
  484. continue;
  485. }
  486. $receiver_list[$index]['type'] = SharingReceiver::$validAlipayReceiveType[$receiver['type']];
  487. }
  488. if (empty($receiver_list)) {
  489. return [
  490. 'code' => 1,
  491. 'msg' => '接收方为空'
  492. ];
  493. }
  494. self::init(get_store_id());
  495. $params = [
  496. 'receiver_list' => $receiver_list,
  497. 'out_request_no' => $out_trade_no
  498. ];
  499. /** 逻辑开始 **/
  500. try {
  501. $res = self::$alipay->bind($params)->get('alipay_trade_royalty_relation_bind_response');
  502. \Yii::warning($res);
  503. if ($res && $res['code'] === '10000' && $res['msg'] === 'Success' && $res['result_code'] == 'SUCCESS') {
  504. return [
  505. 'code' => 0,
  506. 'msg' => '绑定关系成功'
  507. ];
  508. }
  509. return [
  510. 'code' => 1,
  511. 'msg' => $res['sub_msg'],
  512. 'res' => $res
  513. ];
  514. } catch (GatewayException $e) {
  515. return [
  516. 'code' => 1,
  517. 'msg' => $e->getMessage()
  518. ];
  519. } catch (InvalidSignException $e) {
  520. return [
  521. 'code' => 1,
  522. 'msg' => $e->getMessage()
  523. ];
  524. } catch (InvalidConfigException $e) {
  525. return [
  526. 'code' => 1,
  527. 'msg' => $e->getMessage()
  528. ];
  529. }
  530. }
  531. /**
  532. * settle 分账结算
  533. * @param integer $page
  534. * @param integer $page_size
  535. * @param string $out_trade_no
  536. * @param array $parameters
  537. * @param string $is_end
  538. * @return array
  539. */
  540. public static function settle($out_trade_no, $alipay_trade_no, $parameters, $store_id, $is_end = 'false') {
  541. if (empty($out_trade_no) || empty($alipay_trade_no) || empty($parameters)) {
  542. return [
  543. 'code' => 1,
  544. 'msg' => '参数有误'
  545. ];
  546. }
  547. $params = [
  548. 'out_request_no' => $out_trade_no,
  549. 'trade_no' => $alipay_trade_no,
  550. 'royalty_parameters' => $parameters,
  551. 'extend_params' => [
  552. 'royalty_finish' => $is_end
  553. ]
  554. ];
  555. self::init($store_id);
  556. /** 逻辑开始 **/
  557. try {
  558. $res = self::$alipay->settle($params)->get('alipay_trade_order_settle_response');
  559. \Yii::warning($res);
  560. if ($res && $res['code'] === '10000' && $res['msg'] === 'Success') {
  561. return [
  562. 'code' => 0,
  563. 'msg' => 'success'
  564. ];
  565. }
  566. return [
  567. 'code' => 1,
  568. 'msg' => $res['sub_msg'],
  569. 'res' => $res
  570. ];
  571. } catch (GatewayException $e) {
  572. return [
  573. 'code' => 1,
  574. 'msg' => $e->getMessage()
  575. ];
  576. } catch (InvalidSignException $e) {
  577. return [
  578. 'code' => 1,
  579. 'msg' => $e->getMessage()
  580. ];
  581. } catch (InvalidConfigException $e) {
  582. return [
  583. 'code' => 1,
  584. 'msg' => $e->getMessage()
  585. ];
  586. }
  587. }
  588. /**
  589. * 同步订单
  590. * @param array $params 订单信息
  591. * @return array
  592. */
  593. public static function order_sync($params, $store_id) {
  594. if (empty($params)) {
  595. return [
  596. 'code' => 1,
  597. 'msg' => '参数有误'
  598. ];
  599. }
  600. self::init($store_id);
  601. /** 逻辑开始 **/
  602. try {
  603. $res = self::$alipay->orderSync($params)->get('alipay_merchant_order_sync_response');
  604. \Yii::warning($res);
  605. if ($res && $res['code'] === '10000' && $res['msg'] === 'Success') {
  606. return [
  607. 'code' => 0,
  608. 'msg' => 'success',
  609. 'data' => $res
  610. ];
  611. }
  612. return [
  613. 'code' => 1,
  614. 'msg' => $res['sub_msg'],
  615. 'res' => $res
  616. ];
  617. } catch (GatewayException $e) {
  618. return [
  619. 'code' => 1,
  620. 'msg' => $e->getMessage()
  621. ];
  622. } catch (InvalidSignException $e) {
  623. return [
  624. 'code' => 1,
  625. 'msg' => $e->getMessage()
  626. ];
  627. } catch (InvalidConfigException $e) {
  628. return [
  629. 'code' => 1,
  630. 'msg' => $e->getMessage()
  631. ];
  632. }
  633. }
  634. }