Wechatpay.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. <?php
  2. namespace core\pay;
  3. use app\dict\pay\OnlinePayDict;
  4. use app\dict\pay\RefundDict;
  5. use app\dict\pay\TransferDict;
  6. use core\exception\PayException;
  7. use Psr\Http\Message\MessageInterface;
  8. use Psr\Http\Message\ResponseInterface;
  9. use think\Response;
  10. use Throwable;
  11. use Yansongda\Pay\Pay;
  12. use Yansongda\Supports\Collection;
  13. /**
  14. * 微信支付管理驱动类 todo 注意:暂时不考虑合单类业务
  15. * Class FileDriver
  16. * @package core\file
  17. */
  18. class Wechatpay extends BasePay
  19. {
  20. /**
  21. * @param array $config
  22. * @return void
  23. */
  24. protected function initialize(array $config = [])
  25. {
  26. $this->config = $config;
  27. $config['mch_secret_cert'] = url_to_path($config['mch_secret_cert'] ?? '');
  28. $config['mch_public_cert_path'] = url_to_path($config['mch_public_cert_path'] ?? '');
  29. // 选填-默认为正常模式。可选为: MODE_NORMAL, MODE_SERVICE
  30. $config['mode'] = Pay::MODE_NORMAL;
  31. Pay::config($this->payConfig($config, 'wechat'));
  32. }
  33. /**
  34. * 公众号支付
  35. * @param array $params
  36. * @return mixed|Collection
  37. */
  38. public function mp(array $params)
  39. {
  40. $result = $this->returnFormat(Pay::wechat()->mp([
  41. 'out_trade_no' => $params['out_trade_no'],
  42. 'description' => $params['body'],
  43. 'amount' => [
  44. 'total' => $params['money'],
  45. ],
  46. 'payer' => [
  47. 'openid' => $params['openid'],
  48. ],
  49. ]));
  50. $code = $result['code'] ?? 0;
  51. if ($code == 0) return $result;
  52. //支付错误抛出
  53. throw new PayException($result['message']);
  54. }
  55. /**
  56. * 手机网页支付
  57. * @param array $params
  58. * @return mixed
  59. */
  60. public function wap(array $params)
  61. {
  62. $order = [
  63. 'out_trade_no' => $params['out_trade_no'],
  64. 'description' => $params['body'],
  65. 'amount' => [
  66. 'total' => $params['money'],
  67. ],
  68. 'scene_info' => [
  69. 'payer_client_ip' => request()->ip(),
  70. 'h5_info' => [
  71. 'type' => 'Wap',
  72. ]
  73. ],
  74. ];
  75. //这儿有些特殊, 默认情况下,H5 支付所使用的 appid 是微信公众号的 appid,即配置文件中的 mp_app_id 参数,如果想使用关联的小程序的 appid,则只需要在调用参数中增加 ['_type' => 'mini'] 即可
  76. if (!empty($order['type'])) {
  77. $order['_type'] = 'mini'; // 注意这一行
  78. }
  79. return $this->returnFormat(Pay::wechat()->h5($order));
  80. }
  81. public function web(array $params)
  82. {
  83. }
  84. /**
  85. * app支付
  86. * @param array $params
  87. * @return mixed|ResponseInterface
  88. */
  89. public function app(array $params)
  90. {
  91. return $this->returnFormat(Pay::wechat()->app([
  92. 'out_trade_no' => $params['out_trade_no'],
  93. 'description' => $params['body'],
  94. 'amount' => [
  95. 'total' => $params['money'],
  96. ],
  97. ]));
  98. }
  99. /**
  100. * 小程序支付
  101. * @param array $params
  102. * @return mixed|ResponseInterface
  103. */
  104. public function mini(array $params)
  105. {
  106. return $this->returnFormat(Pay::wechat()->mini([
  107. 'out_trade_no' => $params['out_trade_no'],
  108. 'description' => $params['body'],
  109. 'amount' => [
  110. 'total' => $params['money'],
  111. 'currency' => 'CNY',//一般是人民币
  112. ],
  113. 'payer' => [
  114. 'openid' => $params['openid'],
  115. ]
  116. ]));
  117. }
  118. /**
  119. * 付款码支付
  120. * @param array $params
  121. * @return mixed|Collection
  122. */
  123. public function pos(array $params)
  124. {
  125. $order = [
  126. 'out_trade_no' => $params['out_trade_no'],
  127. 'body' => $params['body'],
  128. 'total_fee' => $params['money'],
  129. 'spbill_create_ip' => request()->ip(),
  130. 'auth_code' => $params["auth_code"],
  131. ];
  132. $result = Pay::wechat()->pos($order);
  133. return $this->returnFormat($result);
  134. }
  135. /**
  136. * 扫码支付
  137. * @param array $params
  138. * @return mixed|Collection
  139. */
  140. public function scan(array $params)
  141. {
  142. return $this->returnFormat(Pay::wechat()->scan([
  143. 'out_trade_no' => $params['out_trade_no'],
  144. 'description' => $params['body'],
  145. 'amount' => [
  146. 'total' => $params['money'],
  147. ],
  148. ]));
  149. }
  150. /**
  151. * 转账(微信的转账是很多笔的)
  152. * @param array $params
  153. * @return array
  154. */
  155. public function transfer(array $params)
  156. {
  157. //这儿的批次信息可能是这儿生成的,但依然需要记录
  158. $order = [
  159. 'out_batch_no' => time() . '',//
  160. 'batch_name' => $params['remark'],
  161. 'batch_remark' => $params['remark'],
  162. ];
  163. $transfer_list = $params['transfer_list'];
  164. //单笔转账
  165. if (empty($transfer_list)) {
  166. $transfer_list = array(
  167. [
  168. 'transfer_no' => $params['transfer_no'] . '1',
  169. 'money' => (int)$params['money'],
  170. 'remark' => $params['remark'],
  171. 'openid' => $params['to_no']
  172. ]
  173. );
  174. }
  175. $total_amount = 0;
  176. $total_num = 0;
  177. foreach ($transfer_list as $v) {
  178. $item_transfer = [
  179. 'out_detail_no' => time() . '1',
  180. 'transfer_amount' => (int)$v['money'],
  181. 'transfer_remark' => $v['remark'],
  182. 'openid' => $v['openid'],
  183. ];
  184. $total_amount += (int)$v['money'];
  185. $total_num++;
  186. if (!empty($v['user_name'])) {
  187. $item_transfer['user_name'] = $v['user_name'];// 明文传参即可,sdk 会自动加密
  188. }
  189. $order['transfer_detail_list'][] = $item_transfer;
  190. }
  191. $order['total_amount'] = $total_amount;
  192. $order['total_num'] = $total_num;
  193. $result = $this->returnFormat(Pay::wechat()->transfer($order));
  194. if (!empty($result['code'])) {
  195. // if($result['code'] == 'PARAM_ERROR'){
  196. // throw new PayException();
  197. // }else if($result['code'] == 'INVALID_REQUEST'){
  198. // throw new PayException();
  199. // }
  200. if ($result['code'] == 'INVALID_REQUEST') {
  201. throw new PayException(700010);
  202. }
  203. throw new PayException($result['message']);
  204. }
  205. return ['batch_id' => $result['batch_id'], 'out_batch_no' => $result['out_batch_no']];
  206. }
  207. /**
  208. * 支付关闭
  209. * @param string $out_trade_no
  210. * @return void
  211. */
  212. public function close(string $out_trade_no)
  213. {
  214. try {
  215. $result = Pay::wechat()->close([
  216. 'out_trade_no' => $out_trade_no,
  217. ]);
  218. return $this->returnFormat($result);
  219. }catch(Throwable $e){
  220. return false;
  221. }
  222. return true;
  223. }
  224. /**
  225. * 退款
  226. * @param array $params
  227. * @return array
  228. */
  229. public function refund(array $params)
  230. {
  231. $out_trade_no = $params['out_trade_no'];
  232. $money = $params['money'];
  233. $total = $params['total'];
  234. $refund_no = $params['refund_no'];
  235. $result = Pay::wechat()->refund([
  236. 'out_trade_no' => $out_trade_no,
  237. 'out_refund_no' => $refund_no,
  238. 'amount' => [
  239. 'refund' => $money,
  240. 'total' => $total,
  241. 'currency' => 'CNY',
  242. ],
  243. ]);
  244. $result = $this->returnFormat($result);
  245. $refund_status_array = [
  246. 'SUCCESS' => RefundDict::SUCCESS,
  247. 'CLOSED' => RefundDict::FAIL,
  248. 'PROCESSING' => RefundDict::DEALING,
  249. 'ABNORMAL' => RefundDict::FAIL,
  250. ];
  251. return [
  252. 'status' => $refund_status_array[$result['status']],
  253. 'refund_no' => $refund_no,
  254. 'out_trade_no' => $out_trade_no,
  255. 'pay_refund_no' => $result['refund_id']
  256. ];
  257. }
  258. /**
  259. * 异步回调
  260. * @param string $action
  261. * @param callable $callback
  262. * @return ResponseInterface|Response
  263. */
  264. public function notify(string $action, callable $callback)
  265. {
  266. try {
  267. $result = $this->returnFormat(Pay::wechat()->callback());
  268. if ($action == 'pay') {//支付
  269. if ($result['event_type'] == 'TRANSACTION.SUCCESS') {
  270. $pay_trade_data = $result['resource']['ciphertext'];
  271. $temp_params = [
  272. 'trade_no' => $pay_trade_data['transaction_id'],
  273. 'mch_id' => $pay_trade_data['mchid'],
  274. 'status' => OnlinePayDict::getWechatPayStatus($pay_trade_data['trade_state'])
  275. ];
  276. $callback_result = $callback($pay_trade_data['out_trade_no'], $temp_params);
  277. if (is_bool($callback_result) && $callback_result) {
  278. return Pay::wechat()->success();
  279. }
  280. }
  281. } else if ($action == 'refund') {//退款
  282. if ($result['event_type'] == 'REFUND.SUCCESS') {
  283. $refund_trade_data = $result['resource']['ciphertext'];
  284. $refund_status_array = [
  285. 'SUCCESS' => RefundDict::SUCCESS,
  286. 'CLOSED' => RefundDict::FAIL,
  287. 'PROCESSING' => RefundDict::DEALING,
  288. 'ABNORMAL' => RefundDict::FAIL,
  289. ];
  290. $temp_params = [
  291. 'trade_no' => $refund_trade_data['transaction_id'],
  292. 'mch_id' => $refund_trade_data['mchid'],
  293. 'refund_no' => $refund_trade_data['out_refund_no'],
  294. 'status' => $refund_status_array[$refund_trade_data['refund_status']],
  295. ];
  296. $callback_result = $callback($refund_trade_data['out_trade_no'], $temp_params);
  297. if (is_bool($callback_result) && $callback_result) {
  298. return Pay::wechat()->success();
  299. }
  300. }
  301. }
  302. return $this->fail();
  303. } catch ( Throwable $e ) {
  304. // throw new PayException($e->getMessage());
  305. return $this->fail($e->getMessage());
  306. }
  307. }
  308. /**
  309. * 查询普通支付订单
  310. * @param array $params
  311. * @return array|MessageInterface|Collection|null
  312. */
  313. public function getOrder(array $params = [])
  314. {
  315. $out_trade_no = $params['out_trade_no'];
  316. $transaction_id = $params['transaction_id'] ?? '';
  317. $order = [
  318. ];
  319. if (!empty($out_trade_no)) {
  320. $order['out_trade_no'] = $out_trade_no;
  321. }
  322. if (!empty($transaction_id)) {
  323. $order['transaction_id'] = $transaction_id;
  324. }
  325. $result = Pay::wechat()->query($order);
  326. if (empty($result))
  327. return $result;
  328. $result = $this->returnFormat($result);
  329. return [
  330. 'status' => OnlinePayDict::getWechatPayStatus($result['trade_state']),
  331. ];
  332. }
  333. /**
  334. * 查询退款单据
  335. * @param string|null $out_trade_no
  336. * @param string|null $refund_no
  337. * @return array|Collection|MessageInterface|null
  338. */
  339. public function getRefund(?string $out_trade_no, ?string $refund_no = '')
  340. {
  341. $order = [
  342. '_action' => 'refund',
  343. 'transaction_id' => $out_trade_no,
  344. 'out_refund_no' => $refund_no
  345. ];
  346. $result = Pay::wechat()->query($order);
  347. if (empty($result))
  348. return $result;
  349. $result = $this->returnFormat($result);
  350. $refund_status_array = [
  351. 'SUCCESS' => RefundDict::SUCCESS,
  352. 'CLOSED' => RefundDict::FAIL,
  353. 'PROCESSING' => RefundDict::DEALING,
  354. 'ABNORMAL' => RefundDict::FAIL,
  355. ];
  356. return [
  357. 'status' => $refund_status_array[$result['status']],
  358. 'refund_no' => $refund_no,
  359. 'out_trade_no' => $out_trade_no
  360. ];
  361. }
  362. /**
  363. * 获取转账订单(todo 切勿调用)
  364. * @param string $transfer_no
  365. * @return array
  366. */
  367. public function getTransfer(string $transfer_no, $out_transfer_no = '')
  368. {
  369. $order = [
  370. 'out_batch_no' => $out_transfer_no,
  371. 'out_detail_no' => $transfer_no,
  372. '_action' => 'transfer',
  373. ];
  374. $result = Pay::wechat()->query($order);
  375. $result = $this->returnFormat($result);
  376. //微信转账状态
  377. $transfer_status_array = [
  378. 'INIT' => TransferDict::DEALING,//初始态。 系统转账校验中
  379. 'WAIT_PAY' => TransferDict::DEALING,
  380. 'PROCESSING' => TransferDict::DEALING,
  381. 'FAIL' => TransferDict::FAIL,
  382. 'SUCCESS' => TransferDict::SUCCESS,
  383. ];
  384. return [
  385. 'status' => $transfer_status_array[$result['status']],
  386. 'transfer_no' => $transfer_no
  387. ];
  388. }
  389. public function fail($message = '')
  390. {
  391. $response = [
  392. 'code' => 'FAIL',
  393. 'message' => $message ?: '失败',
  394. ];
  395. return response($response, 400, [], 'json');
  396. }
  397. }