WechatPayV2Default.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. <?php
  2. namespace app\common\service\pay\wechat;
  3. use app\common\enum\AfterSaleLogEnum;
  4. use app\common\enum\OrderEnum;
  5. use app\common\enum\PayEnum;
  6. use app\common\enum\UserTerminalEnum;
  7. use app\common\logic\PayNotifyLogic;
  8. use app\common\model\IntegralOrder;
  9. use app\common\model\Order;
  10. use app\common\model\RechargeOrder;
  11. use app\common\model\UserAuth;
  12. use app\common\service\after_sale\AfterSaleService;
  13. use app\common\service\pay\base\BasePayService;
  14. use app\common\service\WeChatConfigService;
  15. use EasyWeChat\Factory;
  16. use EasyWeChat\Payment\Application;
  17. use think\facade\Log;
  18. /**
  19. * @notes 微信支付 v2
  20. * author lbzy
  21. * @datetime 2024-11-11 13:55:22
  22. * @class WechatPayV2Default
  23. * @package app\common\service\pay\wechat
  24. */
  25. class WechatPayV2Default extends BasePayService
  26. {
  27. /**
  28. * 授权信息
  29. * @var UserAuth|array|\think\Model
  30. */
  31. protected $auth;
  32. /**
  33. * easyWeChat实例
  34. * @var
  35. */
  36. protected $pay;
  37. function __construct($params)
  38. {
  39. parent::__construct($params);
  40. $this->pay = Factory::payment(WeChatConfigService::getWechatConfigByTerminal($this->terminal));
  41. if ($this->user_id) {
  42. $this->auth = UserAuth::where([ 'user_id' => $this->user_id, 'terminal' => $this->terminal ])->findOrEmpty();
  43. }
  44. }
  45. function payMnp()
  46. {
  47. if (empty($this->auth['id'])) {
  48. throw new \Exception('获取授权信息失败');
  49. }
  50. $result = $this->pay->order->unify($this->getAttributes($this->payAttach, $this->payOrder));
  51. $this->checkResultFail($result);
  52. return $this->pay->jssdk->bridgeConfig($result['prepay_id'], false);
  53. }
  54. function payOa()
  55. {
  56. return $this->payMnp();
  57. }
  58. function payIos()
  59. {
  60. $result = $this->pay->order->unify($this->getAttributes($this->payAttach, $this->payOrder));
  61. $this->checkResultFail($result);
  62. return $this->pay->jssdk->appConfig($result['prepay_id']);
  63. }
  64. function payAndroid()
  65. {
  66. return $this->payIos();
  67. }
  68. function payH5()
  69. {
  70. $result = $this->pay->order->unify($this->getAttributes($this->payAttach, $this->payOrder));
  71. $this->checkResultFail($result);
  72. $redirect_url = request()->domain() . '/mobile/bundle/pages/h5_pay_query/h5_pay_query?pay_way='.PayEnum::WECHAT_PAY;
  73. $redirect_url = urlencode($redirect_url);
  74. return $result['mweb_url'] . '&redirect_url=' . $redirect_url;
  75. }
  76. function payPc()
  77. {
  78. $result = $this->pay->order->unify($this->getAttributes($this->payAttach, $this->payOrder));
  79. $this->checkResultFail($result);
  80. // 返回信息由前端生成支付二维码
  81. return [
  82. 'code_url' => $result['code_url'],
  83. 'order_amount' => $this->payOrder['order_amount']
  84. ];
  85. }
  86. function getAttributes($from, $order): array
  87. {
  88. $fromArray = [
  89. 'order' => [
  90. 'body' => '商品',
  91. 'attach' => 'order',
  92. ],
  93. 'recharge' => [
  94. 'body' => '充值',
  95. 'attach' => 'recharge',
  96. ],
  97. 'integral' => [
  98. 'body' => '积分商城',
  99. 'attach' => 'integral',
  100. ],
  101. ];
  102. $terminalTradeArray = [
  103. UserTerminalEnum::ANDROID => 'APP',
  104. UserTerminalEnum::IOS => 'APP',
  105. UserTerminalEnum::H5 => 'MWEB',
  106. UserTerminalEnum::PC => 'NATIVE',
  107. ];
  108. $attributes = [
  109. 'trade_type' => $terminalTradeArray[$this->terminal] ?? 'JSAPI',
  110. 'body' => $fromArray[$from]['body'],
  111. 'total_fee' => (int) bcmul($order['order_amount'], 100), // 单位:分
  112. 'openid' => $this->auth['openid'],
  113. 'attach' => $fromArray[$from]['attach'],
  114. ];
  115. //NATIVE模式设置
  116. if ($this->terminal == UserTerminalEnum::PC) {
  117. $attributes['product_id'] = $order['sn'];
  118. }
  119. //修改微信统一下单,订单编号 -> 支付回调时截取前面的单号 18个
  120. //修改原因:回调时使用了不同的回调地址,导致跨客户端支付时(例如小程序,公众号)可能出现201,商户订单号重复错误
  121. $suffix = mb_substr(time(), -4);
  122. $attributes['out_trade_no'] = $order['sn'] . $attributes['trade_type'] . $this->terminal . $suffix;
  123. return $attributes;
  124. }
  125. function checkResultFail($result) : void
  126. {
  127. if ($result['return_code'] != 'SUCCESS' || $result['result_code'] != 'SUCCESS') {
  128. if (isset($result['return_code']) && $result['return_code'] == 'FAIL') {
  129. throw new \Exception($result['return_msg']);
  130. }
  131. if (isset($result['err_code_des'])) {
  132. throw new \Exception($result['err_code_des']);
  133. }
  134. throw new \Exception('未知原因');
  135. }
  136. }
  137. function payNotify()
  138. {
  139. $app = new Application(WeChatConfigService::getWechatConfigByTerminal($this->terminal));
  140. $response = $app->handlePaidNotify(function ($message, $fail) {
  141. if ($message['return_code'] !== 'SUCCESS') {
  142. return $fail('通信失败');
  143. }
  144. // 用户是否支付成功
  145. if ($message['result_code'] === 'SUCCESS') {
  146. $extra['transaction_id'] = $message['transaction_id'];
  147. $message['out_trade_no'] = mb_substr($message['out_trade_no'], 0, 18);
  148. $this->paySuccess([
  149. 'extra' => $extra,
  150. 'from' => $message['attach'],
  151. 'out_trade_no' => $message['out_trade_no'],
  152. 'transaction_id' => $message['transaction_id'],
  153. ]);
  154. } elseif ($message['result_code'] === 'FAIL') {
  155. // 用户支付失败
  156. }
  157. return true; // 返回处理完成
  158. });
  159. return $response->send();
  160. }
  161. function queryPay()
  162. {
  163. }
  164. function refund()
  165. {
  166. $data = $this->params['refund_data'];
  167. $result = $this->pay->refund->byTransactionId(
  168. $data['transaction_id'],
  169. $data['refund_sn'],
  170. (int) bcmul($data['total_fee'], 100),
  171. (int) bcmul($data['refund_fee'], 100),
  172. );
  173. if (($result['return_code'] ?? '') == 'SUCCESS' && ($result['result_code'] ?? '') == 'SUCCESS') {
  174. return true;
  175. }
  176. Log::write($result, 'wechatRefundError');
  177. $this->setStatus(false, $result['return_msg'] ?? $result['err_code_des'] ?? '发起退款失败');
  178. return false;
  179. }
  180. function queryRefund()
  181. {
  182. $refund = $this->params['query_refund_data'];
  183. $result = $this->pay->refund->queryByOutRefundNumber($refund['sn']);
  184. if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS' && $result['refund_status_0'] == 'SUCCESS') {
  185. // 退款成功
  186. return true;
  187. }
  188. if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS' && $result['refund_status_0'] == 'REFUNDCLOSE') {
  189. Log::write($result, 'wechatQueryRefundError');
  190. // 退款失败
  191. return false;
  192. }
  193. return $result;
  194. }
  195. }