|
@@ -0,0 +1,156 @@
|
|
|
+<?php namespace App\Servers\WechatPay;
|
|
|
+
|
|
|
+use DateTime;
|
|
|
+use DateTimeZone;
|
|
|
+use App\Facades\Servers\Logs\Log;
|
|
|
+use WeChatPay\Builder;
|
|
|
+use WeChatPay\Crypto\Rsa;
|
|
|
+use WeChatPay\Util\PemUtil;
|
|
|
+use WeChatPay\Formatter;
|
|
|
+
|
|
|
+class Payment
|
|
|
+{
|
|
|
+ private $appid;
|
|
|
+ private $mchid;
|
|
|
+ private $instance;
|
|
|
+ private $merchantPrivateKeyInstance;
|
|
|
+ private $merchantCertificateSerial;
|
|
|
+
|
|
|
+ function __construct()
|
|
|
+ {
|
|
|
+ // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
|
|
|
+ $merchantPrivateKeyFilePath = Config('wechat.pay.key');
|
|
|
+ $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
|
|
|
+ // 「商户API证书」的「证书序列号」
|
|
|
+ // 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
|
|
|
+ $platformCertificateFilePath = Config('wechat.platformCertificate');
|
|
|
+ $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
|
|
|
+ // 从「微信支付平台证书」中获取「证书序列号」
|
|
|
+ $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
|
|
|
+ // 构造一个 APIv3 客户端实例
|
|
|
+ $instance = Builder::factory([
|
|
|
+ 'mchid' => Config('wechat.pay.mch_id'),
|
|
|
+ 'serial' => Config('wechat.pay.certificate'),
|
|
|
+ 'privateKey' => $merchantPrivateKeyInstance,
|
|
|
+ 'certs' => [
|
|
|
+ $platformCertificateSerial => $platformPublicKeyInstance,
|
|
|
+ ],
|
|
|
+ ]);
|
|
|
+ $this->instance = $instance;
|
|
|
+ $this->appid = Config('wechat.pay.appid');
|
|
|
+ $this->mchid = Config('wechat.pay.mch_id');
|
|
|
+ $this->merchantPrivateKeyInstance = $merchantPrivateKeyInstance;
|
|
|
+ $this->merchantCertificateSerial = Config('wechat.pay.certificate');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 拉起小程序支付
|
|
|
+ */
|
|
|
+ public function pay($params)
|
|
|
+ {
|
|
|
+ $notify_url = Config('wechat.pay.notify_url');
|
|
|
+
|
|
|
+ //调用微信JSAPI下单获取预支付交易会话标识
|
|
|
+ try {
|
|
|
+ $resp = $this->instance
|
|
|
+ ->chain('v3/pay/transactions/jsapi')
|
|
|
+ ->post(['json' => [
|
|
|
+ 'mchid' => $this->mchid,
|
|
|
+ 'out_trade_no' => $params['out_trade_no'],
|
|
|
+ 'appid' => $this->appid,
|
|
|
+ 'description' => $params['description'],
|
|
|
+ 'time_expire' => $this->timestampToRfc3339(time() + env('ORDER_OUT_TIME')*60),
|
|
|
+ 'notify_url' => $notify_url,
|
|
|
+ 'amount' => [
|
|
|
+ 'total' => $params['total_price'],
|
|
|
+ 'currency' => 'CNY'
|
|
|
+ ],
|
|
|
+ 'payer' => [
|
|
|
+ 'openid' => $params['openid']
|
|
|
+ ]
|
|
|
+ ]]);
|
|
|
+ $result = json_decode($resp->getBody(),true);
|
|
|
+ $prepay_id = $result['prepay_id'];
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
|
|
|
+ $r = $e->getResponse();
|
|
|
+ // 记录错误信息
|
|
|
+ Log::error('wechat/Payment','拉起小程序支付,出现错误'.$r->getBody().'参数'.json_encode($params));
|
|
|
+ }
|
|
|
+ return response()->json(['error' => '拉起支付失败'], 500);
|
|
|
+ }
|
|
|
+ //根据返回的预支付交易会话标识,组装调起小程序支付参数
|
|
|
+ $data = [
|
|
|
+ 'appId' => $this->appid,
|
|
|
+ 'timeStamp' => (string)Formatter::timestamp(),
|
|
|
+ 'nonceStr' => Formatter::nonce(),
|
|
|
+ 'package' => 'prepay_id='.$prepay_id,
|
|
|
+ ];
|
|
|
+ $data += ['paySign' => Rsa::sign(
|
|
|
+ Formatter::joinedByLineFeed(...array_values($data)),
|
|
|
+ $this->merchantPrivateKeyInstance
|
|
|
+ ), 'signType' => 'RSA'];
|
|
|
+ return response()->json($data);
|
|
|
+ }
|
|
|
+
|
|
|
+ //查询订单
|
|
|
+ public function query($params)
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ $promise = $this->instance
|
|
|
+ ->chain('v3/pay/transactions/id/'.$params[''])
|
|
|
+ ->get(['json' => [
|
|
|
+ 'mchid' => $this->mchid,
|
|
|
+ ]]);
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
|
|
|
+ $r = $e->getResponse();
|
|
|
+ // 记录错误信息
|
|
|
+ Log::error('wechat/Payment','查询订单,出现错误'.$r->getBody().'参数'.json_encode($params));
|
|
|
+ }
|
|
|
+ return response()->json(['error' => '查询订单支付失败'], 500);
|
|
|
+ }
|
|
|
+ return response()->json($promise);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订单微信退款
|
|
|
+ */
|
|
|
+ public function refund($params)
|
|
|
+ {
|
|
|
+ $notify_url = Config('wechat.course_refund_notify_url');
|
|
|
+ try {
|
|
|
+ $resp = $this->instance
|
|
|
+ ->chain('v3/refund/domestic/refunds')
|
|
|
+ ->post(['json' => [
|
|
|
+ 'transaction_id' => $params['transaction_id'],
|
|
|
+ 'out_refund_no' => $params['out_refund_no'],
|
|
|
+ 'reason' => $params['reason'] ?? '',
|
|
|
+ 'notify_url' => $notify_url,
|
|
|
+ 'amount' => [
|
|
|
+ 'refund' => $params['refund'],
|
|
|
+ 'total' => $params['total'],
|
|
|
+ 'currency' => 'CNY'
|
|
|
+ ],
|
|
|
+ ]]);
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
|
|
|
+ $r = $e->getResponse();
|
|
|
+ Log::error('wechat/Payment','订单微信退款,出现错误'.$r->getBody());
|
|
|
+ }
|
|
|
+ return response()->json(['error' => '订单微信退款失败'], 500);
|
|
|
+ }
|
|
|
+ return response()->json($resp);
|
|
|
+ }
|
|
|
+ //Rfc3339时间日期格式
|
|
|
+ public function timestampToRfc3339($timestamp)
|
|
|
+ {
|
|
|
+ // 转换为DateTime对象
|
|
|
+ $dateTime = new DateTime();
|
|
|
+ $dateTime->setTimestamp($timestamp);
|
|
|
+ // 设置时区,默认为UTC
|
|
|
+ $dateTime->setTimezone(new DateTimeZone('UTC'));
|
|
|
+ // 格式化为RFC3339
|
|
|
+ return $dateTime->format(DateTime::RFC3339);
|
|
|
+ }
|
|
|
+}
|