WechatPayV2Default.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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 ($this->auth->isEmpty()) {
  48. if (!empty($this->payOrder['openid'])) {
  49. $this->auth = ['openid' => $this->payOrder['openid']];
  50. } else {
  51. throw new \Exception('获取授权信息失败');
  52. }
  53. }
  54. $result = $this->pay->order->unify($this->getAttributes($this->payAttach, $this->payOrder));
  55. $this->checkResultFail($result);
  56. return $this->pay->jssdk->bridgeConfig($result['prepay_id'], false);
  57. }
  58. function payOa()
  59. {
  60. return $this->payMnp();
  61. }
  62. function payIos()
  63. {
  64. $result = $this->pay->order->unify($this->getAttributes($this->payAttach, $this->payOrder));
  65. $this->checkResultFail($result);
  66. return $this->pay->jssdk->appConfig($result['prepay_id']);
  67. }
  68. function payAndroid()
  69. {
  70. return $this->payIos();
  71. }
  72. function payH5()
  73. {
  74. $result = $this->pay->order->unify($this->getAttributes($this->payAttach, $this->payOrder));
  75. $this->checkResultFail($result);
  76. $redirect_url = request()->domain() . '/mobile/bundle/pages/h5_pay_query/h5_pay_query?pay_way='.PayEnum::WECHAT_PAY;
  77. $redirect_url = urlencode($redirect_url);
  78. return $result['mweb_url'] . '&redirect_url=' . $redirect_url;
  79. }
  80. function payPc()
  81. {
  82. $result = $this->pay->order->unify($this->getAttributes($this->payAttach, $this->payOrder));
  83. $this->checkResultFail($result);
  84. // 返回信息由前端生成支付二维码
  85. return [
  86. 'code_url' => $result['code_url'],
  87. 'order_amount' => $this->payOrder['order_amount']
  88. ];
  89. }
  90. function getAttributes($from, $order): array
  91. {
  92. $fromArray = [
  93. 'order' => [
  94. 'body' => '商品',
  95. 'attach' => 'order',
  96. ],
  97. 'recharge' => [
  98. 'body' => '充值',
  99. 'attach' => 'recharge',
  100. ],
  101. 'integral' => [
  102. 'body' => '积分商城',
  103. 'attach' => 'integral',
  104. ],
  105. ];
  106. $terminalTradeArray = [
  107. UserTerminalEnum::ANDROID => 'APP',
  108. UserTerminalEnum::IOS => 'APP',
  109. UserTerminalEnum::H5 => 'MWEB',
  110. UserTerminalEnum::PC => 'NATIVE',
  111. ];
  112. $attributes = [
  113. 'trade_type' => $terminalTradeArray[$this->terminal] ?? 'JSAPI',
  114. 'body' => $fromArray[$from]['body'],
  115. 'total_fee' => (int) bcmul($order['order_amount'], 100), // 单位:分
  116. 'openid' => $this->auth['openid'],
  117. 'attach' => $fromArray[$from]['attach'],
  118. ];
  119. //NATIVE模式设置
  120. if ($this->terminal == UserTerminalEnum::PC) {
  121. $attributes['product_id'] = $order['sn'];
  122. }
  123. //修改微信统一下单,订单编号 -> 支付回调时截取前面的单号 18个
  124. //修改原因:回调时使用了不同的回调地址,导致跨客户端支付时(例如小程序,公众号)可能出现201,商户订单号重复错误
  125. $suffix = mb_substr(time(), -4);
  126. $attributes['out_trade_no'] = $order['sn'] . $attributes['trade_type'] . $this->terminal . $suffix;
  127. return $attributes;
  128. }
  129. function checkResultFail($result) : void
  130. {
  131. if ($result['return_code'] != 'SUCCESS' || $result['result_code'] != 'SUCCESS') {
  132. if (isset($result['return_code']) && $result['return_code'] == 'FAIL') {
  133. throw new \Exception($result['return_msg']);
  134. }
  135. if (isset($result['err_code_des'])) {
  136. throw new \Exception($result['err_code_des']);
  137. }
  138. throw new \Exception('未知原因');
  139. }
  140. }
  141. function payNotify()
  142. {
  143. $app = new Application(WeChatConfigService::getWechatConfigByTerminal($this->terminal));
  144. $response = $app->handlePaidNotify(function ($message, $fail) {
  145. if ($message['return_code'] !== 'SUCCESS') {
  146. return $fail('通信失败');
  147. }
  148. // 用户是否支付成功
  149. if ($message['result_code'] === 'SUCCESS') {
  150. $extra['transaction_id'] = $message['transaction_id'];
  151. $message['out_trade_no'] = mb_substr($message['out_trade_no'], 0, 18);
  152. $this->paySuccess([
  153. 'extra' => $extra,
  154. 'from' => $message['attach'],
  155. 'out_trade_no' => $message['out_trade_no'],
  156. 'transaction_id' => $message['transaction_id'],
  157. ]);
  158. } elseif ($message['result_code'] === 'FAIL') {
  159. // 用户支付失败
  160. }
  161. return true; // 返回处理完成
  162. });
  163. return $response->send();
  164. }
  165. function queryPay()
  166. {
  167. }
  168. function refund()
  169. {
  170. $data = $this->params['refund_data'];
  171. $result = $this->pay->refund->byTransactionId(
  172. $data['transaction_id'],
  173. $data['refund_sn'],
  174. (int) bcmul($data['total_fee'], 100),
  175. (int) bcmul($data['refund_fee'], 100),
  176. );
  177. if (($result['return_code'] ?? '') == 'SUCCESS' && ($result['result_code'] ?? '') == 'SUCCESS') {
  178. outFileLog($result,'refund_money','$result-refund');
  179. return true;
  180. }
  181. Log::write($result, 'wechatRefundError');
  182. $this->setStatus(false, $result['return_msg'] ?? $result['err_code_des'] ?? '发起退款失败');
  183. return false;
  184. }
  185. function queryRefund()
  186. {
  187. $refund = $this->params['query_refund_data'];
  188. $result = $this->pay->refund->queryByOutRefundNumber($refund['sn']);
  189. if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS' && $result['refund_status_0'] == 'SUCCESS') {
  190. // 退款成功
  191. return true;
  192. }
  193. if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS' && $result['refund_status_0'] == 'REFUNDCLOSE') {
  194. Log::write($result, 'wechatQueryRefundError');
  195. // 退款失败
  196. return false;
  197. }
  198. return $result;
  199. }
  200. }