180 lines
6.3 KiB
PHP
180 lines
6.3 KiB
PHP
![]() |
<?php
|
|||
|
|
|||
|
namespace app\common\service;
|
|||
|
|
|||
|
use app\common\service\Channel\wechat\WechatConstants;
|
|||
|
use app\constant\ResponseCode;
|
|||
|
use GuzzleHttp\Exception\RequestException;
|
|||
|
use think\Exception;
|
|||
|
use WeChatPay\Builder;
|
|||
|
use WeChatPay\Crypto\Rsa;
|
|||
|
use WeChatPay\Formatter;
|
|||
|
use WeChatPay\Util\PemUtil;
|
|||
|
|
|||
|
class PayService
|
|||
|
{
|
|||
|
protected $config;
|
|||
|
protected $instance;
|
|||
|
protected $platformPublicKeyInstance;
|
|||
|
protected $merchantPrivateKeyInstance;
|
|||
|
|
|||
|
public function __construct()
|
|||
|
{
|
|||
|
// 服务商 商户号
|
|||
|
$merchantId = getenv('MERCHANT_ID');
|
|||
|
// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
|
|||
|
$merchantPrivateKeyFilePath = 'file://' . base_path() . '/certificate/apiclient_key.pem';
|
|||
|
|
|||
|
$this->merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
|
|||
|
|
|||
|
// 「商户API证书」的「证书序列号」
|
|||
|
$merchantCertificateSerial = '3297EB3A1132A39DAFFD70A6C4A6C3078CD346F7';
|
|||
|
|
|||
|
// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
|
|||
|
$platformCertificateFilePath = 'file://' . base_path() . '/certificate/wechatpay_660FAD2D6E804E37BE9D77EF5882718CC1E3399F.pem';
|
|||
|
$this->platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
|
|||
|
|
|||
|
// 从「微信支付平台证书」中获取「证书序列号」
|
|||
|
$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
|
|||
|
|
|||
|
// 构造一个 APIv3 客户端实例
|
|||
|
$this->instance = Builder::factory([
|
|||
|
'mchid' => $merchantId,
|
|||
|
'serial' => $merchantCertificateSerial,
|
|||
|
'privateKey' => $this->merchantPrivateKeyInstance,
|
|||
|
'certs' => [
|
|||
|
$platformCertificateSerial => $this->platformPublicKeyInstance,
|
|||
|
],
|
|||
|
]);
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @desc jsapi支付
|
|||
|
* @param array $pay_data
|
|||
|
* @param $seller_channel
|
|||
|
* @return array
|
|||
|
*/
|
|||
|
public function payment(array $pay_data)
|
|||
|
{
|
|||
|
try {
|
|||
|
|
|||
|
$request_data = [
|
|||
|
'appid' => getenv('APP_ID'),// 公众号ID
|
|||
|
'mchid' => getenv('MERCHANT_ID'),//直连商户号
|
|||
|
'description' => $pay_data['desc'],//商品描述
|
|||
|
'out_trade_no' => $pay_data['out_trade_no'],//商户订单号
|
|||
|
'notify_url' => $pay_data['notify_url'],//通知地址
|
|||
|
'amount' => [
|
|||
|
'total' => round($pay_data['total_amount'] * 100),//总金额 单位为分
|
|||
|
],
|
|||
|
'payer' => [
|
|||
|
'openid' => $pay_data['openid'],// 用户在服务商AppID下的唯一标识
|
|||
|
]
|
|||
|
];
|
|||
|
|
|||
|
$resp = $this->instance
|
|||
|
->chain('/v3/pay/transactions/jsapi')
|
|||
|
->post([
|
|||
|
'json' => $request_data,
|
|||
|
// 'debug' => true
|
|||
|
]);
|
|||
|
|
|||
|
$result = json_decode($resp->getBody(), true);
|
|||
|
|
|||
|
raw_log('wechat_pay/transactions_jsapi', ['request_data' => $request_data, 'result' => $result]);
|
|||
|
|
|||
|
$pay_data = [
|
|||
|
'appId' => getenv('APP_ID'), //微信小程序appid
|
|||
|
'timeStamp' => strval(Formatter::timestamp()),
|
|||
|
'nonceStr' => Formatter::nonce(),
|
|||
|
'package' => 'prepay_id=' . $result['prepay_id'],
|
|||
|
];
|
|||
|
$pay_data['paySign'] = Rsa::sign(
|
|||
|
Formatter::joinedByLineFeed(...array_values($pay_data)),
|
|||
|
$this->merchantPrivateKeyInstance
|
|||
|
);
|
|||
|
$pay_data['signType'] = 'RSA';
|
|||
|
|
|||
|
return $pay_data;
|
|||
|
} catch (RequestException $exception) {
|
|||
|
$response = $exception->getResponse();
|
|||
|
$result = json_decode($response->getBody()->getContents(), true);
|
|||
|
|
|||
|
raw_log('wechat_pay/transactions_jsapi_failed', ['pay_data' => $pay_data, 'result' => $result]);
|
|||
|
return [
|
|||
|
'code' => ResponseCode::FAIL,
|
|||
|
'msg' => $result['message']
|
|||
|
];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @desc 退款
|
|||
|
* @param $refund_data
|
|||
|
* @return array
|
|||
|
*/
|
|||
|
public function refundPayment($refund_data)
|
|||
|
{
|
|||
|
try {
|
|||
|
//退款数据
|
|||
|
$request_data = [
|
|||
|
'out_trade_no' => $refund_data['out_trade_no'],//微信支付订单号
|
|||
|
'out_refund_no' => $refund_data['out_refund_no'],//商户退款单号
|
|||
|
'reason' => $refund_data['refund_desc'],//退款原因
|
|||
|
'notify_url' => $refund_data['notify_url'],//通知地址
|
|||
|
'amount' => [
|
|||
|
'refund' => round($refund_data['refund_amount'] * 100),//退款金额
|
|||
|
'total' => round($refund_data['total'] * 100),//原订单金额
|
|||
|
'currency' => 'CNY',//退款币种
|
|||
|
]
|
|||
|
];
|
|||
|
$resp = $this->instance
|
|||
|
->chain('/v3/refund/domestic/refunds')
|
|||
|
->post([
|
|||
|
'json' => $request_data,
|
|||
|
// 'debug' => true
|
|||
|
]);
|
|||
|
|
|||
|
$result = json_decode($resp->getBody(), true);
|
|||
|
raw_log('wechat_pay/refund', ['request_data' => $request_data, 'result' => $result]);
|
|||
|
|
|||
|
return [
|
|||
|
'transaction_id' => $result['transaction_id'],
|
|||
|
'out_transaction_id' => $result['refund_id'],
|
|||
|
'out_trade_no' => $result['out_refund_no'],
|
|||
|
];
|
|||
|
|
|||
|
} catch (RequestException $exception) {
|
|||
|
$response = $exception->getResponse();
|
|||
|
$result = json_decode($response->getBody()->getContents(), true);
|
|||
|
raw_log('wechat_pay/refund_failed', ['request_data' => $request_data, 'result' => $result]);
|
|||
|
|
|||
|
return [
|
|||
|
'code' => ResponseCode::FAIL,
|
|||
|
'msg' => $result['message']
|
|||
|
];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @desc 余额查询 微信没有余额查询接口,返回假数据
|
|||
|
* @param $seller_channel
|
|||
|
* @return array
|
|||
|
*/
|
|||
|
public function queryBalanceAmount($seller_channel)
|
|||
|
{
|
|||
|
return [
|
|||
|
'code'=>ResponseCode::SUCCESS,
|
|||
|
'data'=>[
|
|||
|
'total_amount'=>0.00,
|
|||
|
'settled_amount'=>0.00,
|
|||
|
'not_settled_amount'=>0.00
|
|||
|
]
|
|||
|
];
|
|||
|
}
|
|||
|
|
|||
|
}
|