init
- 框架初始化 - 安装插件 - 修复PHP8.4报错
This commit is contained in:
50
addons/shopro/library/pay/PayService.php
Normal file
50
addons/shopro/library/pay/PayService.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\library\pay;
|
||||
|
||||
use think\Log;
|
||||
use addons\shopro\library\pay\provider\Base;
|
||||
|
||||
class PayService
|
||||
{
|
||||
protected $payment;
|
||||
protected $platform;
|
||||
|
||||
public function __construct($payment, $platform = null)
|
||||
{
|
||||
$this->payment = $payment;
|
||||
|
||||
$this->platform = $platform ? : request()->header('platform', null);
|
||||
|
||||
if (!$this->platform) {
|
||||
error_stop('缺少用户端平台参数');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 支付提供器
|
||||
*
|
||||
* @param string $type
|
||||
* @return Base
|
||||
*/
|
||||
public function provider($payment = null)
|
||||
{
|
||||
$payment = $payment ?: $this->payment;
|
||||
$class = "\\addons\\shopro\\library\\pay\\provider\\" . \think\helper\Str::studly($payment);
|
||||
if (class_exists($class)) {
|
||||
return new $class($this, $this->platform);
|
||||
}
|
||||
|
||||
error_stop('支付类型不支持');
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function __call($funcname, $arguments)
|
||||
{
|
||||
return $this->provider()->{$funcname}(...$arguments);
|
||||
}
|
||||
|
||||
}
|
||||
234
addons/shopro/library/pay/provider/Alipay.php
Normal file
234
addons/shopro/library/pay/provider/Alipay.php
Normal file
@@ -0,0 +1,234 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\library\pay\provider;
|
||||
|
||||
use think\Log;
|
||||
use think\exception\HttpResponseException;
|
||||
use addons\shopro\service\pay\PayRefund;
|
||||
use Yansongda\Pay\Pay;
|
||||
|
||||
class Alipay extends Base
|
||||
{
|
||||
protected $payService = null;
|
||||
protected $pay = null;
|
||||
protected $platform = null;
|
||||
|
||||
public function __construct($payService, $platform = null)
|
||||
{
|
||||
$this->payService = $payService;
|
||||
|
||||
$this->platform = $platform;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function pay($order, $config = [], $from = null)
|
||||
{
|
||||
$this->init('alipay', $config);
|
||||
|
||||
if (in_array($this->platform, ['WechatOfficialAccount', 'WechatMiniProgram', 'H5'])) {
|
||||
// 返回支付宝支付链接
|
||||
if (!$from == 'url') {
|
||||
// return request()->domain() . '/shop/api/pay/alipay?pay_sn=' . $order['out_trade_no'] . '&platform=' . $this->platform . '&return_url=' . urlencode($this->config['alipay']['default']['return_url']);
|
||||
return request()->domain() . '/addons/shopro/pay/alipay?pay_sn=' . $order['out_trade_no'] . '&platform=' . $this->platform . '&return_url=' . urlencode($this->config['alipay']['default']['return_url']);
|
||||
}
|
||||
}
|
||||
|
||||
$method = $this->getMethod('alipay');
|
||||
$result = Pay::alipay()->$method($order);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function transfer($payload, $config = [])
|
||||
{
|
||||
$this->init('alipay', $config);
|
||||
|
||||
$code = 0;
|
||||
$response = Pay::alipay()->transfer($payload);
|
||||
if ($response['code'] === '10000' && $response['status'] === 'SUCCESS') {
|
||||
$code = 1;
|
||||
}
|
||||
|
||||
return [$code, $response];
|
||||
}
|
||||
|
||||
|
||||
public function notify($callback, $config = [])
|
||||
{
|
||||
$this->init('alipay', $config);
|
||||
try {
|
||||
$data = Pay::alipay()->callback(); // 是的,验签就这么简单!
|
||||
|
||||
// { // 支付宝支付成功回调参数
|
||||
// "gmt_create": "2022-06-21 14:54:39",
|
||||
// "charset": "utf-8",
|
||||
// "seller_email": "xptech@qq.com",
|
||||
// "subject": "\u5546\u57ce\u8ba2\u5355\u652f\u4ed8",
|
||||
// "buyer_id": "2088902485164146",
|
||||
// "invoice_amount": "0.01",
|
||||
// "notify_id": "2022062100222145440064141420932810",
|
||||
// "fund_bill_list": "[{\"amount\":\"0.01\",\"fundChannel\":\"ALIPAYACCOUNT\"}]",
|
||||
// "notify_type": "trade_status_sync",
|
||||
// "trade_status": "TRADE_SUCCESS",
|
||||
// "receipt_amount": "0.01",
|
||||
// "buyer_pay_amount": "0.01",
|
||||
// "app_id": "202*********742",
|
||||
// "seller_id": "2088721922277739",
|
||||
// "gmt_payment": "2022-06-21 14:54:40",
|
||||
// "notify_time": "2022-06-21 14:54:41",
|
||||
// "version": "1.0",
|
||||
// "out_trade_no": "P202202383569762189002100",
|
||||
// "total_amount": "0.01",
|
||||
// "trade_no": "2022062122001464141435375324",
|
||||
// "auth_app_id": "202*********742",
|
||||
// "buyer_logon_id": "157***@163.com",
|
||||
// "point_amount": "0.00"
|
||||
// }
|
||||
|
||||
// { // 支付宝退款成功(交易关闭)回调参数
|
||||
// "gmt_create": "2022-06-21 15:31:34",
|
||||
// "charset": "utf-8",
|
||||
// "seller_email": "xptech@qq.com",
|
||||
// "gmt_payment": "2022-06-21 15:31:34",
|
||||
// "notify_time": "2022-06-21 15:53:32",
|
||||
// "subject": "商城订单支付",
|
||||
// "gmt_refund": "2022-06-21 15:53:32.158",
|
||||
// "out_biz_no": "R202203533190902732002100",
|
||||
// "buyer_id": "2088902485164146",
|
||||
// "version": "1.0",
|
||||
// "notify_id": "2022062100222155332064141421692866",
|
||||
// "notify_type": "trade_status_sync",
|
||||
// "out_trade_no": "P202203305611515511002100",
|
||||
// "total_amount": "0.01",
|
||||
// "trade_status": "TRADE_CLOSED",
|
||||
// "refund_fee": "0.01",
|
||||
// "trade_no": "2022062122001464141435383344",
|
||||
// "auth_app_id": "202*********742",
|
||||
// "buyer_logon_id": "157***@163.com",
|
||||
// "gmt_close": "2022-06-21 15:53:32",
|
||||
// "app_id": "202*********742",
|
||||
// "seller_id": "2088721922277739"
|
||||
// }
|
||||
|
||||
Log::write('pay-notify-origin-data:' . json_encode($data));
|
||||
// 判断是否是支付宝退款(支付宝退款成功会通知该接口)
|
||||
|
||||
$out_trade_no = $data['out_trade_no']; // 商户单号
|
||||
$out_refund_no = $data['out_biz_no'] ?? ''; // 退款单号
|
||||
if (
|
||||
$data['notify_type'] == 'trade_status_sync' // 同步交易状态
|
||||
&& $data['trade_status'] == 'TRADE_CLOSED' // 交易关闭
|
||||
&& $out_refund_no // 退款单号
|
||||
) {
|
||||
// 交给退款实例处理
|
||||
$refund = new PayRefund();
|
||||
$refund->notify([
|
||||
'out_trade_no' => $out_trade_no,
|
||||
'out_refund_no' => $out_refund_no,
|
||||
'payment_json' => json_encode($data)
|
||||
]);
|
||||
|
||||
return Pay::alipay()->success();
|
||||
}
|
||||
|
||||
// 判断支付宝是否是支付成功状态,如果不是,直接返回响应
|
||||
if ($data['trade_status'] != 'TRADE_SUCCESS') {
|
||||
// 不是交易成功的通知,直接返回成功
|
||||
return Pay::alipay()->success();
|
||||
}
|
||||
|
||||
$data['pay_fee'] = $data['total_amount'];
|
||||
$data['transaction_id'] = $data['trade_no'];
|
||||
$data['buyer_info'] = $data['buyer_logon_id'];
|
||||
|
||||
return $callback($data);
|
||||
} catch (HttpResponseException $e) {
|
||||
$data = $e->getResponse()->getData();
|
||||
$message = $data ? ($data['msg'] ?? '') : $e->getMessage();
|
||||
format_log_error($e, 'alipayNotify.HttpResponseException', $message);
|
||||
return 'fail';
|
||||
} catch (\Exception $e) {
|
||||
format_log_error($e, 'alipayNotify');
|
||||
return 'fail';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 退款
|
||||
*
|
||||
* @param array $order_data
|
||||
* @param array $config
|
||||
* @return array
|
||||
*/
|
||||
public function refund($order_data, $config = [])
|
||||
{
|
||||
$this->init('alipay', $config);
|
||||
|
||||
$result = Pay::alipay()->refund($order_data);
|
||||
Log::write('pay-refund-origin-data:' . json_encode($result, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
// {
|
||||
// "code": "10000",
|
||||
// "msg": "Success",
|
||||
// "buyer_logon_id": "157***@163.com",
|
||||
// "buyer_user_id": "2088902485164146",
|
||||
// "fund_change": "Y",
|
||||
// "gmt_refund_pay": "2022-06-21 15:53:32",
|
||||
// "out_trade_no": "P202203305611515511002100",
|
||||
// "refund_fee": "0.01",
|
||||
// "send_back_fee": "0.00",
|
||||
// "trade_no": "2022062122001464141435383344"
|
||||
// }
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 格式化支付参数
|
||||
*
|
||||
* @param [type] $params
|
||||
* @return void
|
||||
*/
|
||||
protected function formatConfig($config, $data = [])
|
||||
{
|
||||
$config['notify_url'] = request()->domain() . '/addons/shopro/pay/notify/payment/alipay/platform/' . $this->platform;
|
||||
|
||||
if (in_array($this->platform, ['WechatOfficialAccount', 'WechatMiniProgram', 'H5'])) {
|
||||
// app 支付不能带着个参数
|
||||
$config['return_url'] = str_replace('&', '&', request()->param('return_url', ''));
|
||||
}
|
||||
|
||||
$config = $this->formatCert($config);
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 拼接支付证书绝对地址
|
||||
*
|
||||
* @param array $config
|
||||
* @return array
|
||||
*/
|
||||
protected function formatCert($config)
|
||||
{
|
||||
$end = substr($config['app_secret_cert'], -4);
|
||||
if ($end == '.crt') {
|
||||
$config['app_secret_cert'] = ROOT_PATH . 'public' . $config['app_secret_cert'];
|
||||
}
|
||||
$config['alipay_public_cert_path'] = ROOT_PATH . 'public' . $config['alipay_public_cert_path'];
|
||||
$config['app_public_cert_path'] = ROOT_PATH . 'public' . $config['app_public_cert_path'];
|
||||
$config['alipay_root_cert_path'] = ROOT_PATH . 'public' . $config['alipay_root_cert_path'];
|
||||
|
||||
// 兼容 epay
|
||||
$config['app_cert_public_key'] = $config['app_public_cert_path'];
|
||||
$config['alipay_root_cert'] = $config['alipay_root_cert_path'];
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
168
addons/shopro/library/pay/provider/Base.php
Normal file
168
addons/shopro/library/pay/provider/Base.php
Normal file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\library\pay\provider;
|
||||
|
||||
use think\Log;
|
||||
use Yansongda\Pay\Pay;
|
||||
use Yansongda\Pay\Contract\HttpClientInterface;
|
||||
use addons\shopro\facade\HttpClient;
|
||||
use app\admin\model\shopro\PayConfig;
|
||||
|
||||
class Base
|
||||
{
|
||||
|
||||
/**
|
||||
* yansongda 支付示例
|
||||
*
|
||||
* @var Yansongda\Pay\Pay
|
||||
*/
|
||||
protected $pay = null; // yansongda 支付实例
|
||||
|
||||
public $config = null; // 支付参数
|
||||
|
||||
|
||||
/**
|
||||
* yansongda 支付初始化
|
||||
*
|
||||
* @param string $payment
|
||||
* @param array $config
|
||||
* @return Yansongda\Pay\Pay
|
||||
*/
|
||||
public function init($payment, $config = [], $type = 'normal')
|
||||
{
|
||||
$this->config = $this->getConfig($payment, $config, $type);
|
||||
|
||||
$this->pay = Pay::config($this->config);
|
||||
Pay::set(HttpClientInterface::class, HttpClient::instance()); // 使用自定义 client (也继承至 GuzzleHttp\Client)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取支付的所有参数
|
||||
*
|
||||
* @param string $payment
|
||||
* @return array
|
||||
*/
|
||||
protected function getConfig($payment, $config = [], $type = 'normal')
|
||||
{
|
||||
// 获取平台配置
|
||||
$platformConfig = $this->getPlatformConfig();
|
||||
extract($platformConfig);
|
||||
|
||||
$params = $this->getPayConfig($payment, $paymentConfig);
|
||||
|
||||
// 格式化支付参数
|
||||
$params['mode'] = (int)($params['mode'] ?? 0);
|
||||
$params = $this->formatConfig($params, ['app_id' => $app_id], $type);
|
||||
|
||||
// 合并传入的参数
|
||||
$params = array_merge($params, $config);
|
||||
|
||||
// 合并参数
|
||||
$config = $this->baseConfig();
|
||||
$config = array_merge($config, [$payment => ['default' => $params]]);
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取平台配置参数
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getPlatformConfig()
|
||||
{
|
||||
$platformConfig = sheep_config('shop.platform.' . $this->platform);
|
||||
|
||||
$paymentConfig = $platformConfig['payment'] ?? [];
|
||||
$app_id = $platformConfig['app_id'] ?? '';
|
||||
|
||||
return compact('paymentConfig', 'app_id');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据平台以及支付方式 获取支付配置表的配置参数
|
||||
*
|
||||
* @param string $payment
|
||||
* @return array
|
||||
*/
|
||||
protected function getPayConfig($payment, $paymentConfig)
|
||||
{
|
||||
$methods = $paymentConfig['methods'];
|
||||
$payment_config = $paymentConfig[$payment] ?? 0;
|
||||
|
||||
if (!in_array($payment, $methods)) {
|
||||
error_stop('当前平台未开启该支付方式');
|
||||
}
|
||||
if ($payment_config) {
|
||||
$payConfig = PayConfig::normal()->where('type', $payment)->find($payment_config);
|
||||
}
|
||||
|
||||
if (!isset($payConfig) || !$payConfig) {
|
||||
error_stop('支付配置参数不存在');
|
||||
}
|
||||
|
||||
return $payConfig->params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取对应的支付方法名
|
||||
*
|
||||
* @param strign $payment
|
||||
* @return string
|
||||
*/
|
||||
protected function getMethod($payment)
|
||||
{
|
||||
$method = [
|
||||
'wechat' => [
|
||||
'WechatOfficialAccount' => 'mp', //公众号支付 Collection
|
||||
'WechatMiniProgram' => 'mini', //小程序支付 Collection
|
||||
'H5' => 'wap', //手机网站支付 Response
|
||||
'App' => 'app' // APP 支付 JsonResponse
|
||||
],
|
||||
'alipay' => [
|
||||
'WechatOfficialAccount' => 'wap', //手机网站支付 Response
|
||||
'WechatMiniProgram' => 'wap', //小程序支付
|
||||
'H5' => 'wap', //手机网站支付 Response
|
||||
'App' => 'app' //APP 支付 JsonResponse
|
||||
],
|
||||
];
|
||||
|
||||
return $method[$payment][$this->platform];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* yansongda 基础配置
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function baseConfig()
|
||||
{
|
||||
$log_path = RUNTIME_PATH . 'log/pay/';
|
||||
if (!is_dir($log_path)) {
|
||||
@mkdir($log_path, 0755, true);
|
||||
}
|
||||
|
||||
return [
|
||||
'logger' => [ // optional
|
||||
'enable' => true,
|
||||
'file' => $log_path . 'pay.log',
|
||||
'level' => config('app_debug') ? 'debug' : 'info', // 建议生产环境等级调整为 info,开发环境为 debug
|
||||
'type' => 'daily', // optional, 可选 daily.
|
||||
'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天
|
||||
],
|
||||
'http' => [ // optional
|
||||
'timeout' => 5.0,
|
||||
'connect_timeout' => 5.0,
|
||||
// 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html)
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
344
addons/shopro/library/pay/provider/Wechat.php
Normal file
344
addons/shopro/library/pay/provider/Wechat.php
Normal file
@@ -0,0 +1,344 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\library\pay\provider;
|
||||
|
||||
use think\Log;
|
||||
use think\exception\HttpResponseException;
|
||||
use Yansongda\Pay\Pay;
|
||||
|
||||
class Wechat extends Base
|
||||
{
|
||||
protected $payService = null;
|
||||
protected $platform = null;
|
||||
|
||||
public function __construct($payService, $platform = null)
|
||||
{
|
||||
$this->payService = $payService;
|
||||
|
||||
$this->platform = $platform;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function pay($order, $config = [])
|
||||
{
|
||||
$this->init('wechat', $config);
|
||||
|
||||
if (isset($this->config['wechat']['default']['mode']) && $this->config['wechat']['default']['mode'] === 2) {
|
||||
if (in_array($this->platform, ['WechatOfficialAccount', 'WechatMiniProgram'])) {
|
||||
$order['payer']['sub_openid'] = $order['payer']['openid'] ?? '';
|
||||
unset($order['payer']['openid']);
|
||||
}
|
||||
}
|
||||
|
||||
$order['amount']['total'] = intval(bcmul((string)$order['total_amount'], '100')); // 按分 为单位
|
||||
|
||||
if ($this->platform == 'H5') {
|
||||
$order['_type'] = 'app'; // 使用 配置中的 app_id 字段
|
||||
$order['scene_info'] = [
|
||||
'payer_client_ip' => request()->ip(),
|
||||
'h5_info' => [
|
||||
'type' => 'Wap',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
unset($order['order_id'], $order['total_amount']);
|
||||
$method = $this->getMethod('wechat');
|
||||
$result = Pay::wechat()->$method($order);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function transfer($payload, $config = [])
|
||||
{
|
||||
$this->init('wechat', $config, 'sub_mch');
|
||||
|
||||
$code = 0;
|
||||
$payload['total_amount'] = intval(bcmul((string)$payload['total_amount'], '100'));
|
||||
|
||||
foreach ($payload['transfer_detail_list'] as $key => &$detail) {
|
||||
$detail['transfer_amount'] = intval(bcmul((string)$detail['transfer_amount'], '100'));
|
||||
}
|
||||
if (isset($this->config['wechat']['default']['_type'])) {
|
||||
// 为了能正常获取 appid
|
||||
$payload['_type'] = $this->config['wechat']['default']['_type'];
|
||||
}
|
||||
|
||||
// $payload['authorization_type'] = 'INFORMATION_AUTHORIZATION_TYPE';
|
||||
$payload['authorization_type'] = 'FUND_AUTHORIZATION_TYPE';
|
||||
// $payload['authorization_type'] = 'INFORMATION_AND_FUND_AUTHORIZATION_TYPE';
|
||||
|
||||
$response = Pay::wechat()->transfer($payload);
|
||||
if (isset($response['batch_id']) && $response['batch_id']) {
|
||||
$code = 1;
|
||||
}
|
||||
|
||||
return [$code, $response];
|
||||
}
|
||||
|
||||
|
||||
public function notify($callback, $config = [])
|
||||
{
|
||||
$this->init('wechat', $config);
|
||||
try {
|
||||
$originData = Pay::wechat()->callback(); // 是的,验签就这么简单!
|
||||
// {
|
||||
// "id": "a5c68a7c-5474-5151-825d-88b4143f8642",
|
||||
// "create_time": "2022-06-20T16:16:12+08:00",
|
||||
// "resource_type": "encrypt-resource",
|
||||
// "event_type": "TRANSACTION.SUCCESS",
|
||||
// "summary": "支付成功",
|
||||
// "resource": {
|
||||
// "original_type": "transaction",
|
||||
// "algorithm": "AEAD_AES_256_GCM",
|
||||
// "ciphertext": {
|
||||
// "mchid": "1623831039",
|
||||
// "appid": "wx43********d3d0",
|
||||
// "out_trade_no": "P202204155176122100021000",
|
||||
// "transaction_id": "4200001433202206201698588194",
|
||||
// "trade_type": "JSAPI",
|
||||
// "trade_state": "SUCCESS",
|
||||
// "trade_state_desc": "支付成功",
|
||||
// "bank_type": "OTHERS",
|
||||
// "attach": "",
|
||||
// "success_time": "2022-06-20T16:16:12+08:00",
|
||||
// "payer": {
|
||||
// "openid": "oRj5A44G6lgCVENzVMxZtoMfNeww"
|
||||
// },
|
||||
// "amount": {
|
||||
// "total": 1,
|
||||
// "payer_total": 1,
|
||||
// "currency": "CNY",
|
||||
// "payer_currency": "CNY"
|
||||
// }
|
||||
// },
|
||||
// "associated_data": "transaction",
|
||||
// "nonce": "qoJzoS9MCNgu"
|
||||
// }
|
||||
// }
|
||||
Log::write('pay-notify-origin-data:' . json_encode($originData));
|
||||
if ($originData['event_type'] == 'TRANSACTION.SUCCESS') {
|
||||
// 支付成功回调
|
||||
$data = $originData['resource']['ciphertext'] ?? [];
|
||||
if (isset($data['trade_state']) && $data['trade_state'] == 'SUCCESS') {
|
||||
// 交易成功
|
||||
$data['pay_fee'] = ($data['amount']['total'] / 100);
|
||||
$data['notify_time'] = date('Y-m-d H:i:s', strtotime((string)$data['success_time']));
|
||||
$data['buyer_info'] = $data['payer']['openid'] ?? ($data['payer']['sub_openid'] ?? '');
|
||||
|
||||
$result = $callback($data, $originData);
|
||||
return $result;
|
||||
}
|
||||
|
||||
return 'fail';
|
||||
} else {
|
||||
// 微信交易未成功,返回 false,让微信再次通知
|
||||
Log::error('notify-error:交易未成功:' . $originData['event_type']);
|
||||
return 'fail';
|
||||
}
|
||||
} catch (HttpResponseException $e) {
|
||||
$data = $e->getResponse()->getData();
|
||||
$message = $data ? ($data['msg'] ?? '') : $e->getMessage();
|
||||
format_log_error($e, 'wechatNotify.HttpResponseException', $message);
|
||||
return 'fail';
|
||||
} catch (\Exception $e) {
|
||||
format_log_error($e, 'wechatNotify');
|
||||
return 'fail';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 退款
|
||||
*
|
||||
* @param array $order_data
|
||||
* @param array $config
|
||||
* @return array
|
||||
*/
|
||||
public function refund($order_data, $config = [])
|
||||
{
|
||||
$config['notify_url'] = $config['notify_url'] ?? request()->domain() . '/addons/shopro/pay/notifyRefund/payment/wechat/platform/' . $this->platform;
|
||||
$order_data['notify_url'] = $config['notify_url'];
|
||||
|
||||
$this->init('wechat', $config);
|
||||
|
||||
$order_data['amount']['total'] = intval(bcmul((string)$order_data['amount']['total'], '100'));
|
||||
$order_data['amount']['refund'] = intval(bcmul((string)$order_data['amount']['refund'], '100'));
|
||||
|
||||
$result = Pay::wechat()->refund($order_data);
|
||||
Log::write('pay-refund-origin-data:' . json_encode($result, JSON_UNESCAPED_UNICODE));
|
||||
// { 返回数据字段
|
||||
// "amount": {
|
||||
// "currency": "CNY",
|
||||
// "discount_refund": 0,
|
||||
// "from": [],
|
||||
// "payer_refund": 1,
|
||||
// "payer_total": 1,
|
||||
// "refund": 1,
|
||||
// "settlement_refund": 1,
|
||||
// "settlement_total": 1,
|
||||
// "total": 1
|
||||
// },
|
||||
// "channel": "ORIGINAL",
|
||||
// "create_time": "2022-06-20T19:06:36+08:00",
|
||||
// "funds_account": "AVAILABLE",
|
||||
// "out_refund_no": "R202207063668479227002100",
|
||||
// "out_trade_no": "P202205155977315528002100",
|
||||
// "promotion_detail": [],
|
||||
// "refund_id": "50301802252022062021833667769",
|
||||
// "status": "PROCESSING",
|
||||
// "transaction_id": "4200001521202206207964248014",
|
||||
// "user_received_account": "\u652f\u4ed8\u7528\u6237\u96f6\u94b1"
|
||||
// }
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 微信退款回调
|
||||
*
|
||||
* @param array $callback
|
||||
* @param array $config
|
||||
* @return array
|
||||
*/
|
||||
public function notifyRefund($callback, $config = [])
|
||||
{
|
||||
$this->init('wechat', $config);
|
||||
try {
|
||||
$originData = Pay::wechat()->callback(); // 是的,验签就这么简单!
|
||||
Log::write('pay-notifyrefund-callback-data:' . json_encode($originData));
|
||||
// {
|
||||
// "id": "4a553265-1f28-53a3-9395-8d902b902462",
|
||||
// "create_time": "2022-06-21T11:25:33+08:00",
|
||||
// "resource_type": "encrypt-resource",
|
||||
// "event_type": "REFUND.SUCCESS",
|
||||
// "summary": "\u9000\u6b3e\u6210\u529f",
|
||||
// "resource": {
|
||||
// "original_type": "refund",
|
||||
// "algorithm": "AEAD_AES_256_GCM",
|
||||
// "ciphertext": {
|
||||
// "mchid": "1623831039",
|
||||
// "out_trade_no": "P202211233042122753002100",
|
||||
// "transaction_id": "4200001417202206214219765470",
|
||||
// "out_refund_no": "R202211252676008994002100",
|
||||
// "refund_id": "50300002272022062121864292533",
|
||||
// "refund_status": "SUCCESS",
|
||||
// "success_time": "2022-06-21T11:25:33+08:00",
|
||||
// "amount": {
|
||||
// "total": 1,
|
||||
// "refund": 1,
|
||||
// "payer_total": 1,
|
||||
// "payer_refund": 1
|
||||
// },
|
||||
// "user_received_account": "\u652f\u4ed8\u7528\u6237\u96f6\u94b1"
|
||||
// },
|
||||
// "associated_data": "refund",
|
||||
// "nonce": "8xfQknYyLVop"
|
||||
// }
|
||||
// }
|
||||
|
||||
if ($originData['event_type'] == 'REFUND.SUCCESS') {
|
||||
// 支付成功回调
|
||||
$data = $originData['resource']['ciphertext'] ?? [];
|
||||
if (isset($data['refund_status']) && $data['refund_status'] == 'SUCCESS') {
|
||||
// 退款成功
|
||||
$result = $callback($data, $originData);
|
||||
return $result;
|
||||
}
|
||||
|
||||
return 'fail';
|
||||
} else {
|
||||
// 微信交易未成功,返回 false,让微信再次通知
|
||||
Log::error('notify-error:退款未成功:' . $originData['event_type']);
|
||||
return 'fail';
|
||||
}
|
||||
} catch (HttpResponseException $e) {
|
||||
$data = $e->getResponse()->getData();
|
||||
$message = $data ? ($data['msg'] ?? '') : $e->getMessage();
|
||||
format_log_error($e, 'wechatNotifyRefund.HttpResponseException', $message);
|
||||
return 'fail';
|
||||
} catch (\Exception $e) {
|
||||
format_log_error($e, 'wechatNotifyRefund');
|
||||
return 'fail';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 格式化支付参数
|
||||
*
|
||||
* @param [type] $params
|
||||
* @return void
|
||||
*/
|
||||
protected function formatConfig($config, $data = [], $type = 'normal')
|
||||
{
|
||||
if ($config['mode'] == 2 && $type == 'sub_mch') {
|
||||
// 服务商模式,但需要子商户直连 ,重新定义 config(商家转账到零钱)
|
||||
$config = [
|
||||
'mch_id' => $config['sub_mch_id'],
|
||||
'mch_secret_key' => $config['sub_mch_secret_key'],
|
||||
'mch_secret_cert' => $config['sub_mch_secret_cert'],
|
||||
'mch_public_cert_path' => $config['sub_mch_public_cert_path'],
|
||||
];
|
||||
$config['mode'] = 0; // 临时改为普通商户
|
||||
}
|
||||
|
||||
if ($config['mode'] === 2) {
|
||||
// 首先将平台配置的 app_id 初始化到配置中
|
||||
$config['mp_app_id'] = $config['app_id']; // 服务商关联的公众号的 appid
|
||||
$config['sub_app_id'] = $data['app_id']; // 服务商特约子商户
|
||||
} else {
|
||||
$config['app_id'] = $data['app_id'];
|
||||
}
|
||||
|
||||
switch ($this->platform) {
|
||||
case 'WechatMiniProgram':
|
||||
$config['_type'] = 'mini'; // 小程序提现,需要传 _type = mini 才能正确获取到 appid
|
||||
if ($config['mode'] === 2) {
|
||||
$config['sub_mini_app_id'] = $config['sub_app_id'];
|
||||
unset($config['sub_app_id']);
|
||||
} else {
|
||||
$config['mini_app_id'] = $config['app_id'];
|
||||
unset($config['app_id']);
|
||||
}
|
||||
break;
|
||||
case 'WechatOfficialAccount':
|
||||
$config['_type'] = 'mp'; // 小程序提现,需要传 _type = mp 才能正确获取到 appid
|
||||
if ($config['mode'] === 2) {
|
||||
$config['sub_mp_app_id'] = $config['sub_app_id'];
|
||||
unset($config['sub_app_id']);
|
||||
} else {
|
||||
$config['mp_app_id'] = $config['app_id'];
|
||||
unset($config['app_id']);
|
||||
}
|
||||
break;
|
||||
case 'App':
|
||||
case 'H5':
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$config['notify_url'] = request()->domain() . '/addons/shopro/pay/notify/payment/wechat/platform/' . $this->platform;
|
||||
$config['mch_secret_cert'] = ROOT_PATH . 'public' . $config['mch_secret_cert'];
|
||||
$config['mch_public_cert_path'] = ROOT_PATH . 'public' . $config['mch_public_cert_path'];
|
||||
|
||||
// 可手动配置微信支付公钥证书
|
||||
$config['wechat_public_cert_id'] = $config['wechat_public_cert_id'] ?? '';
|
||||
$config['wechat_public_cert'] = $config['wechat_public_cert'] ?? '';
|
||||
if ($config['wechat_public_cert_id'] && $config['wechat_public_cert']) {
|
||||
$config['wechat_public_cert_path'] = [
|
||||
$config['wechat_public_cert_id'] => ROOT_PATH . 'public' . $config['wechat_public_cert']
|
||||
];
|
||||
}
|
||||
unset($config['wechat_public_cert_id'], $config['wechat_public_cert']);
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user