Transfer.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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 Transfer
  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. //调用微信JSAPI下单获取预支付交易会话标识
  52. try {
  53. $resp = $this->instance
  54. ->chain('/v3/fund-app/mch-transfer/transfer-bills')
  55. ->post(['json' => [
  56. 'appid' => $this->appid,
  57. 'out_bill_no' => (string)$params['out_bill_no'],
  58. 'transfer_scene_id' => '1000',
  59. 'openid' => $params['openid'],
  60. 'transfer_amount' => 10,
  61. 'transfer_remark' => '余额提现',
  62. 'user_recv_perception' => '现金奖励',
  63. 'transfer_scene_report_infos' => [
  64. [
  65. 'info_type' => '活动名称',
  66. 'info_content' => '下单红包',
  67. ],
  68. [
  69. 'info_type' => '奖励说明',
  70. 'info_content' => '预约下单得现金红包',
  71. ],
  72. ],
  73. 'notify_url' => env('APP_URL').'/api/wechat_transfer/notify'
  74. /*'time_expire' => $this->timestampToRfc3339(time() + env('ORDER_OUT_TIME')*60),*/
  75. ]]);
  76. $result = json_decode($resp->getBody(),true);
  77. } catch (\Exception $e) {
  78. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  79. $r = $e->getResponse();
  80. // 记录错误信息
  81. Log::error('wechat/transfer','微信商户转账失败,出现错误'.$r->getBody().'参数'.json_encode($params));
  82. }
  83. return false;
  84. }
  85. return $result;
  86. }
  87. //查询订单
  88. public function query($params)
  89. {
  90. try {
  91. $promise = $this->instance
  92. ->chain('v3/pay/transactions/id/'.$params[''])
  93. ->get(['json' => [
  94. 'mchid' => $this->mchid,
  95. ]]);
  96. } catch (\Exception $e) {
  97. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  98. $r = $e->getResponse();
  99. // 记录错误信息
  100. Log::error('wechat/Payment','查询订单,出现错误'.$r->getBody().'参数'.json_encode($params));
  101. }
  102. return response()->json(['error' => '查询订单支付失败'], 500);
  103. }
  104. return response()->json($promise);
  105. }
  106. /**
  107. * 订单微信退款
  108. */
  109. public function refund($params)
  110. {
  111. $notify_url = Config('wechat.course_refund_notify_url');
  112. try {
  113. $resp = $this->instance
  114. ->chain('v3/refund/domestic/refunds')
  115. ->post(['json' => [
  116. 'transaction_id' => $params['transaction_id'],
  117. 'out_refund_no' => $params['out_refund_no'],
  118. 'reason' => $params['reason'] ?? '',
  119. 'notify_url' => $notify_url,
  120. 'amount' => [
  121. 'refund' => $params['refund'],
  122. 'total' => $params['total'],
  123. 'currency' => 'CNY'
  124. ],
  125. ]]);
  126. } catch (\Exception $e) {
  127. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  128. $r = $e->getResponse();
  129. Log::error('wechat/Payment','订单微信退款,出现错误'.$r->getBody());
  130. }
  131. return response()->json(['error' => '订单微信退款失败'], 500);
  132. }
  133. return response()->json($resp);
  134. }
  135. //Rfc3339时间日期格式
  136. public function timestampToRfc3339($timestamp)
  137. {
  138. // 转换为DateTime对象
  139. $dateTime = new DateTime();
  140. $dateTime->setTimestamp($timestamp);
  141. // 设置时区,默认为UTC
  142. $dateTime->setTimezone(new DateTimeZone('UTC'));
  143. // 格式化为RFC3339
  144. return $dateTime->format(DateTime::RFC3339);
  145. }
  146. }