WechatPay.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. <?php namespace App\Http\Controllers\Api;
  2. use App\Http\Controllers\Api\Api;
  3. use App\Models\Custom;
  4. use App\Models\Orders;
  5. use App\Facades\Servers\Logs\Log;
  6. use App\Models\OrdersProduct;
  7. use EasyWeChat\Factory;
  8. use Illuminate\Http\Request;
  9. use App\Servers\WechatPay\Payment;
  10. use App\Facades\Servers\WechatMini\Mini;
  11. use WeChatPay\Formatter;
  12. use WeChatPay\Crypto\AesGcm;
  13. use WeChatPay\Crypto\Rsa;
  14. /**
  15. * 微信支付接口
  16. *
  17. * @author JUN
  18. *
  19. * */
  20. class WechatPay extends Api{
  21. /**
  22. * 小程序微信支付下单 /api/wechat_pay/pay
  23. *
  24. * */
  25. public function pay(Custom $Custom)
  26. {
  27. // 检查登录
  28. $uid = $this->checkLogin();
  29. $code = request('code','');
  30. $orderId = request('order_id','');
  31. $orderInfo = Orders::query()->where('id','=',$orderId)->first()->toArray();
  32. if (empty($orderInfo))
  33. return json_send(['code'=>'error','msg'=>'订单不存在','data'=>['id'=>null]]);
  34. if ($orderInfo['custom_uid'] != $uid)
  35. return json_send(['code'=>'error','msg'=>'无权操作','data'=>['id'=>null]]);
  36. if ($orderInfo['status'] != 1)
  37. return json_send(['code'=>'error','msg'=>'订单已支付或取消','data'=>['id'=>null]]);
  38. //获取openid
  39. $result = Mini::jscode2session($code);
  40. if (!$result['openid']) return json_send(['code'=>'error','msg'=>'获取openid失败','data'=>$result['error']]);
  41. $openid = $result['openid'];
  42. $payment = new Payment();
  43. return $payment->pay(['out_trade_no' => $orderInfo['snowflake_id'],'openid' => $openid,'description' => '开邻智教课程','total_price' => $orderInfo['pay_total']]);
  44. }
  45. /**
  46. * 小程序微信支付回调 /api/wechat_pay/notify
  47. *
  48. * */
  49. public function notify(Custom $Custom)
  50. {
  51. $content = file_get_contents("php://input");
  52. if (!empty($content)) {
  53. //直接json字符串
  54. $params = $content;
  55. } elseif (!empty($_POST)) {
  56. //直接POST数据
  57. $params = $_POST;
  58. } else {
  59. $params = [];
  60. }
  61. $post_data = $params;
  62. Log::log('notify_wechat_pay', 'post_data:' . $post_data);
  63. //获取headers参数
  64. $headers = request()->header();
  65. Log::log('notify_wechat_pay', '微信支付回调返回headers参数:' . json_encode($headers));
  66. $inWechatpaySignature = $headers['wechatpay-signature'][0];
  67. $inWechatpayTimestamp = $headers['wechatpay-timestamp'][0];
  68. $inWechatpaySerial = $headers['wechatpay-serial'][0];
  69. $inWechatpayNonce = $headers['wechatpay-nonce'][0];
  70. $inBody = $post_data;
  71. Log::log('notify_wechat_pay', 'wechatpay-timestamp:' . $inWechatpayTimestamp);
  72. $apiv3Key = Config('wechatpay.APIV3');// 在商户平台上设置的APIv3密钥
  73. // 根据通知的平台证书序列号,查询本地平台证书文件,
  74. $platformCertificateFilePath = Config('wechatpay.platformCertificate');
  75. $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
  76. try {
  77. // 检查通知时间偏移量,允许5分钟之内的偏移
  78. $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
  79. Log::log('notify_wechat_pay', '时间偏移量:' . $timeOffsetStatus);
  80. $verifiedStatus = Rsa::verify(
  81. // 构造验签名串
  82. Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
  83. $inWechatpaySignature,
  84. $platformPublicKeyInstance
  85. );
  86. }catch (\Exception $e){
  87. Log::log('notify_wechat_pay', '错误getMessage:' . $e->getMessage());
  88. }
  89. Log::log('notify_wechat_pay', '验签:' . $verifiedStatus.'验签inWechatpayTimestamp:' . $inWechatpayTimestamp.'验签verifiedStatus:' . $inWechatpayNonce);
  90. if ($timeOffsetStatus && $verifiedStatus) {
  91. // 转换通知的JSON文本消息为PHP Array数组
  92. $inBodyArray = (array)json_decode($inBody, true);
  93. // 使用PHP7的数据解构语法,从Array中解构并赋值变量
  94. ['resource' => [
  95. 'ciphertext' => $ciphertext,
  96. 'nonce' => $nonce,
  97. 'associated_data' => $aad
  98. ]] = $inBodyArray;
  99. // 加密文本消息解密
  100. $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
  101. // 把解密后的文本转换为PHP Array数组
  102. $inBodyResourceArray = (array)json_decode($inBodyResource, true);
  103. Log::log('notify_wechat_pay', '打印解密后的结果:' . json_encode($inBodyResourceArray));
  104. Log::log('notify_wechat_pay', '参数:' . $inBodyResourceArray['trade_state'] . '订单号' . $inBodyResourceArray['out_trade_no'] . '微信支付号' . $inBodyResourceArray['transaction_id']);
  105. if ($inBodyResourceArray['trade_state'] == "SUCCESS") {
  106. Log::log('notify_wechat_pay', '通知订单');
  107. //更新订单支付状态
  108. $orderData = [
  109. 'pay_time' => time(),
  110. 'transaction_id'=>$inBodyResourceArray['transaction_id'],
  111. 'status'=>2,
  112. ];
  113. try {
  114. $res = Orders::query()->where('snowflake_id','=',$inBodyResourceArray['out_trade_no'])->update($orderData);
  115. if (!$res) {
  116. Log::log('notify_wechat_pay', '更新订单失败' . json_encode($res));
  117. return json_send(['code'=>'FAIL']);
  118. }
  119. $orderInfo = Orders::query()->where('snowflake_id','=',$inBodyResourceArray['out_trade_no'])->first()->toArray();
  120. //更新子订单
  121. $res = OrdersProduct::query()->where('order_id','=',$orderInfo['id'])->update(['status'=>2]);
  122. if (!$res) {
  123. Log::log('notify_wechat_pay', '更新子订单失败' . json_encode($res));
  124. return json_send(['code'=>'FAIL']);
  125. }
  126. }catch (\Exception $e){
  127. Log::log('notify_wechat_pay', '更新订单失败' . json_encode($e));
  128. return json_send(['code'=>'FAIL']);
  129. }
  130. Log::log('notify_wechat_pay', '支付回调完成 通知返回' . json_encode($res));
  131. return json_send(['code'=>'SUCCESS']);
  132. }
  133. return json_send(['code'=>'SUCCESS']);
  134. }else{
  135. return json_send(['code'=>'FAIL']);
  136. }
  137. }
  138. }