Payment.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. <?php namespace App\Servers\WechatPay;
  2. use DateTime;
  3. use DateTimeZone;
  4. use App\Facades\Servers\Logs\Log;
  5. use WeChatPay\Builder;
  6. use WeChatPay\Crypto\Rsa;
  7. use WeChatPay\Util\PemUtil;
  8. use WeChatPay\Formatter;
  9. class Payment
  10. {
  11. private $appid;
  12. private $mchid;
  13. private $instance;
  14. private $merchantPrivateKeyInstance;
  15. private $merchantCertificateSerial;
  16. function __construct()
  17. {
  18. // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
  19. $merchantPrivateKeyFilePath = Config('wechatpay.private_key');
  20. //$merchantPrivateKeyFilePath = 'file://D:\phpstudy_pro\WWW\mall\kailin\resources\1612111355_20241118_cert\apiclient_key.pem';
  21. $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
  22. // 「商户API证书」的「证书序列号」
  23. $merchantCertificateSerial = '4A5F7CFCC3280CF8C09735D5FC2DEFBC9CCD6F46';
  24. // 从本地文件中加载「微信支付平台证书」或者「微信支付平台公钥」,用来验证微信支付应答的签名
  25. $platformCertificateOrPublicKeyFilePath = Config('wechatpay.platformCertificate');
  26. $platformPublicKeyInstance = Rsa::from($platformCertificateOrPublicKeyFilePath, Rsa::KEY_TYPE_PUBLIC);
  27. // 「微信支付平台证书」的「证书序列号」或者是「微信支付平台公钥ID」
  28. // 「平台证书序列号」及/或「平台公钥ID」可以从 商户平台 -> 账户中心 -> API安全 直接查询到
  29. $platformCertificateSerialOrPublicKeyId = 'PUB_KEY_ID_0116121113552024111800218500000334';
  30. // 构造一个 APIv3 客户端实例
  31. $instance = Builder::factory([
  32. 'mchid' => Config('wechatpay.mchid'),
  33. 'serial' => $merchantCertificateSerial,
  34. 'privateKey' => $merchantPrivateKeyInstance,
  35. 'certs' => [
  36. $platformCertificateSerialOrPublicKeyId => $platformPublicKeyInstance,
  37. ],
  38. ]);
  39. $this->instance = $instance;
  40. $this->appid = Config('wechatpay.appid');
  41. $this->mchid = Config('wechatpay.mchid');
  42. $this->merchantPrivateKeyInstance = $merchantPrivateKeyInstance;
  43. $this->merchantCertificateSerial = Config('wechat.certificate');
  44. return;
  45. }
  46. /**
  47. * 拉起小程序支付
  48. */
  49. public function pay($params)
  50. {
  51. $notify_url = Config('wechatpay.notify_url');
  52. //调用微信JSAPI下单获取预支付交易会话标识
  53. //try {
  54. $resp = $this->instance
  55. ->chain('v3/pay/transactions/jsapi')
  56. ->post(['json' => [
  57. 'mchid' => $this->mchid,
  58. 'out_trade_no' => (string)$params['out_trade_no'],
  59. 'appid' => $this->appid,
  60. 'description' => $params['description'],
  61. /*'time_expire' => $this->timestampToRfc3339(time() + env('ORDER_OUT_TIME')*60),*/
  62. 'notify_url' => env('APP_URL').'/api/wechat_pay/notify',
  63. 'amount' => [
  64. 'total' => 1,
  65. //'total' => $params['total_price'],
  66. 'currency' => 'CNY'
  67. ],
  68. 'payer' => [
  69. 'openid' => $params['openid']
  70. ]
  71. ]]);
  72. $result = json_decode($resp->getBody(),true);
  73. $prepay_id = $result['prepay_id'];
  74. /*} catch (\Exception $e) {
  75. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  76. $r = $e->getResponse();
  77. // 记录错误信息
  78. Log::error('wechat/Payment','拉起小程序支付,出现错误'.$r->getBody().'参数'.json_encode($params));
  79. }
  80. return response()->json(['error' => '拉起支付失败'], 500);
  81. }*/
  82. //根据返回的预支付交易会话标识,组装调起小程序支付参数
  83. $data = [
  84. 'appId' => $this->appid,
  85. 'timeStamp' => (string)Formatter::timestamp(),
  86. 'nonceStr' => Formatter::nonce(),
  87. 'package' => 'prepay_id='.$prepay_id,
  88. ];
  89. $data += ['paySign' => Rsa::sign(
  90. Formatter::joinedByLineFeed(...array_values($data)),
  91. $this->merchantPrivateKeyInstance
  92. ), 'signType' => 'RSA'];
  93. return json_send(['code'=>'success','msg'=>'成功','data'=>$data]);
  94. }
  95. //查询订单
  96. public function query($params)
  97. {
  98. try {
  99. $promise = $this->instance
  100. ->chain('v3/pay/transactions/id/'.$params[''])
  101. ->get(['json' => [
  102. 'mchid' => $this->mchid,
  103. ]]);
  104. } catch (\Exception $e) {
  105. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  106. $r = $e->getResponse();
  107. // 记录错误信息
  108. Log::error('wechat/Payment','查询订单,出现错误'.$r->getBody().'参数'.json_encode($params));
  109. }
  110. return response()->json(['error' => '查询订单支付失败'], 500);
  111. }
  112. return response()->json($promise);
  113. }
  114. /**
  115. * 订单微信退款
  116. */
  117. public function refund($params)
  118. {
  119. $notify_url = Config('wechat.course_refund_notify_url');
  120. try {
  121. $resp = $this->instance
  122. ->chain('v3/refund/domestic/refunds')
  123. ->post(['json' => [
  124. 'transaction_id' => $params['transaction_id'],
  125. 'out_refund_no' => $params['out_refund_no'],
  126. 'reason' => $params['reason'] ?? '',
  127. 'notify_url' => $notify_url,
  128. 'amount' => [
  129. 'refund' => $params['refund'],
  130. 'total' => $params['total'],
  131. 'currency' => 'CNY'
  132. ],
  133. ]]);
  134. } catch (\Exception $e) {
  135. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  136. $r = $e->getResponse();
  137. Log::error('wechat/Payment','订单微信退款,出现错误'.$r->getBody());
  138. }
  139. return response()->json(['error' => '订单微信退款失败'], 500);
  140. }
  141. return response()->json($resp);
  142. }
  143. //Rfc3339时间日期格式
  144. public function timestampToRfc3339($timestamp)
  145. {
  146. // 转换为DateTime对象
  147. $dateTime = new DateTime();
  148. $dateTime->setTimestamp($timestamp);
  149. // 设置时区,默认为UTC
  150. $dateTime->setTimezone(new DateTimeZone('UTC'));
  151. // 格式化为RFC3339
  152. return $dateTime->format(DateTime::RFC3339);
  153. }
  154. }