当前位置: 首页 > news >正文

TP8 PHP 支付宝-通用版-V3 SDK 接口加签方式为证书方式

TP8 已安装支付宝-通用版-V3 SDK

接口加签方式之前使用密钥方式,现在要使用证书
官方文档小程序文档 - 支付宝文档中心
SDK源码仓库https://github.com/alipay/alipay-sdk-php-all/tree/master/v3

第一步:生成证书

需要先下载支付宝官方工具:密钥生成工具。

通过这个工具,我们可以获取到 CSR 文件 和 应用私钥

注意,其中:

  • CSR文件 是要拿到平台跟支付宝换 应用公钥证书、支付宝公钥证书、支付宝根证书 的。
  • 应用私钥 后续要写到代码里面的,需要妥善保管,如果泄露了需要及时更换整个密钥。

第二步:配置证书

配置密钥的过程就是讲 CSR 文件 要怎么去跟支付宝换 应用公钥证书、支付宝公钥证书、支付宝根证书。

其实官方的指引流程比较详细了,只要找到配置的位置,接下来的步骤就比较顺利。

位置在支付宝开放平台对应的应用界面下:

上面能拿到需要传入到代码中的 应用公钥证书、支付宝公钥证书、支付宝根证书、应用私钥在之前就拿到:

在系统中使用这些文件后续格式是:

应用公钥证书.crt  支付宝公钥证书.crt  支付宝根证书.crt  应用私钥.pem


应用私钥.pem 内容格式:

四 代码使用

4.1 支付配置文件config\pay.php
可以切换加密方式

<?php

return [
    // ========================
    // 🟢 微信支付配置-转到wechat.php文件中
    // ========================
    // 'wechat' => [
    //     // 基础参数
    //     'app_id' => env('WECHAT.WECHAT_APP_ID'),// 公众号APPID
    //     'mch_id' => env('WECHAT.WECHAT_MCH_ID'),// 商户号
    //     'v3_secret' => env('WECHAT.WECHAT_V3_SECRET'),// V3密钥
    //     'key' => env('WECHAT.WECHAT_API_KEY_V2'),// API密钥
        
    //     // 支付证书
    //     'certificate' => root_path(env('WECHAT.WITHDRAW_CERT_PATH')),// 证书路径
    //     'private_key' => root_path(env('WECHAT.WITHDRAW_KEY_PATH')),// 私钥路径
        
    //     // 提现配置
    //     'withdraw' => [
    //         'certificate' => root_path(env('WECHAT.WECHAT_WITHDRAW_CERT_PATH')),// 证书路径
    //         'private_key' => root_path(env('WECHAT.WECHAT_WITHDRAW_KEY_PATH')),// 私钥路径
    //         'min_amount' => 1.00,    // 最低提现金额(元)
    //         'fee' => 0.00,           // 单笔手续费(微信企业付款无手续费)
    //         'daily_limit' => 50000.00 // 单日提现限额
    //     ],
        
    //     // 统一回调地址
    //     'notify_url' => env('WECHAT.WECHAT_NOTIFY_URL'),

    //     // 第三方登录配置
    //     'social_login' => [
    //         'client_id' => env('WECHAT.SOCIAL_CLIENT_ID', ''),     // 微信开放平台 AppID
    //         'client_secret' => env('WECHAT.SOCIAL_CLIENT_SECRET', ''), // 微信开放平台 AppSecret
    //         'redirect_uri' => env('WECHAT.SOCIAL_REDIRECT_URI', ''),   // 授权回调地址
    //         'scopes' => ['snsapi_login', 'snsapi_userinfo'],           // 授权作用域
    //     ],
    // ],

    // ========================
    // 🔵 支付宝配置
    // ========================
    'alipay' => [
        'mode' => 'cert', // 接口加签方式 切换模式: cert(证书)模式,其它是密钥模式
        'app_id' => env('ALIPAY.ALIPAY_APP_ID',''),// 应用ID
        'sandbox' => env('ALIPAY.ALIPAY_SANDBOX', false),// 沙箱模式 false=沙箱
        'gateway' => env('ALIPAY.ALIPAY_SANDBOX', false) ? 'https://openapi-sandbox.dl.alipaydev.com/gateway.do' : 'https://openapi.alipay.com/gateway.do',支付网关配置,根据沙箱模式选择
        'websocket' => env('ALIPAY.ALIPAY_SANDBOX', false) ? 'openchannel-sandbox.dl.alipaydev.com' : 'wss://openapi.alipay.com/gateway.do',websocket服务地址, 根据沙箱模式选择
        
        // 密钥配置(公钥模式)
        'merchant_private_key' => env('ALIPAY.ALIPAY_PRIVATE_KEY_PATH'),// 应用私钥 - 无论密钥模式还是证书模式都需要
        'alipay_public_key' => env('ALIPAY.ALIPAY_PUBLIC_KEY_PATH'),// 支付宝公钥 - 密钥模式使用
        
        // 证书配置(证书模式)
        'app_cert_path' => env('ALIPAY.ALIPAY_APP_CERT_PATH', ''),// 应用公钥证书路径
        'alipay_cert_path' => env('ALIPAY.ALIPAY_PUBLIC_CERT_PATH', ''),// 支付宝公钥证书路径
        'root_cert_path' => env('ALIPAY.ALIPAY_ROOT_CERT_PATH', ''),// 支付宝根证书路径
        'cert_app_private_key_path' => env('ALIPAY.ALIPAY_APP_PRIVATE_KEY_PATH', ''),// 应用私钥路径
        
        // 提现配置
        'withdraw' => [
            'account' => env('ALIPAY.ALIPAY_WITHDRAW_ACCOUNT'),// 提现账号
            'account_name' => env('ALIPAY.ALIPAY_WITHDRAW_ACCOUNT_NAME'),// 提现账号名称
            'fee_type' => 'ratio',    // 手续费类型:ratio-比例 fixed-固定
            'fee_value' => 0.001,     // 0.1%手续费
            'min_amount' => 1.00,     // 最低提现金额
            'daily_limit' => 100000.00
        ],
        
        // 回调配置
        'notify_url' => env('ALIPAY.ALIPAY_NOTIFY_URL'),// 支付宝网关地址
        'return_url' => env('ALIPAY.ALIPAY_RETURN_URL'),// 同步回调地址/授权回调地址
    ],

    // ========================
    // 💼 提现通用配置
    // ========================
    'withdraw_common' => [
        'audit' => true,       // 是否开启人工审核
        'working_hours' => [   // 提现处理时间段
            'start' => '09:00',
            'end' => '18:00'
        ],
        'blacklist' => [       // 禁止提现的IP段
            '192.168.0.0/16',
            '10.0.0.0/8'
        ]
    ]
];

4.2 .env

# ========================
#支付宝参数
# ========================
[ALIPAY]
ALIPAY_APP_ID = xxxxxx
ALIPAY_SANDBOX=false
ALIPAY_NOTIFY_URL=https://www.xxx.com/pay/index/notify_url
ALIPAY_RETURN_URL=https://www.xxx.com/pay/index/return_url
# 支付宝提现账户
ALIPAY_WITHDRAW_ACCOUNT=0179@sandbox.com
ALIPAY_WITHDRAW_ACCOUNT_NAME="xxxxx"
# 1加签方式:密钥 支付宝密钥文件路径(公钥模式)
#1.1应用私钥证书路径
ALIPAY_PRIVATE_KEY_PATH=app_private_key.pem
#1.2支付宝公钥
ALIPAY_PUBLIC_KEY_PATH=alipay_public_key.pem
# 2加签方式: 证书配置(证书模式)若使用“现金红包”、“单笔转账到支付宝“产品必选
#2.1应用公钥证书路径
ALIPAY_APP_CERT_PATH = cert_app_public.crt
#2.2支付宝公钥证书路径
ALIPAY_PUBLIC_CERT_PATH = cert_alipay_public.crt
#2.3支付宝根证书路径
ALIPAY_ROOT_CERT_PATH = cert_alipay_root.crt
#2.3 应用私钥证书路径
ALIPAY_APP_PRIVATE_KEY_PATH = cert_app_private_key.pem

4.3 支付宝服务文件

重要方法protected function getAlipayConfig()

<?php
namespace app\services\payment\model;

use \Alipay\OpenAPISDK\ApiException;
use Alipay\OpenAPISDK\Util\AlipayConfigUtil;
use Alipay\OpenAPISDK\Util\GenericExecuteApi;
use Alipay\OpenAPISDK\Util\Model\AlipayConfig;
use Alipay\OpenAPISDK\Util\Model\CustomizedParams;
use Alipay\OpenAPISDK\Util\Model\OpenApiGenericRequest;
use Alipay\OpenAPISDK\Api\AlipayTradeApi;
use Alipay\OpenAPISDK\Model\AlipayTradeFastpayRefundQueryModel;
use Alipay\OpenAPISDK\Api\AlipayTradeFastpayRefundApi;
use Alipay\OpenAPISDK\Util\AlipayLogger;
use GuzzleHttp\Client;
use Exception;

/**
 * 支付宝支付服务模型
 * 功能包含:
 * - 多环境配置支持(平台/商户)
 * - 支付创建(APP/WAP/PC)
 * - 订单查询
 * - 退款处理
 * - 关闭订单
 * - 退款查询
 * - 异步通知验证
 * - 动态配置切换
 */
class AlipayServicesModel
{
    /**
     * 支付宝配置
     * @var array
     */
    protected $config = [];
    
    /**
     * 错误信息
     * @var string
     */
    protected $error = '';
    
    /**
     * 构造函数
     * @param array $config 自定义配置(可选)
     */
    public function __construct($config = [])
    {
        $baseConfig = config('pay.alipay');
        if (empty($baseConfig)) {
            throw new \Exception("支付宝基础配置缺失,请检查config/pay.php");
        }
        $this->config = $config ? array_merge($baseConfig, $config) : $baseConfig;
        
        AlipayLogger::setNeedEnableLogger(false);
    }
    
    /**
     * 获取错误信息
     * @return string
     */
    public function getError()
    {
        return $this->error;
    }
    
    /**
     * 设置错误信息
     * @param string $error 错误信息
     * @return $this
     */
    protected function setError($error)
    {
        $this->error = $error;
        return $this;
    }
    
    protected $merchantConfig = null;
    
    /**
     * 设置商户支付参数
     * @param array $merchantConfig 商户支付参数
     * @return $this
     */
    public function setMerchantConfig($merchantConfig)
    {
        $this->merchantConfig = $merchantConfig;
        return $this;
    }
        /**
     * 验证证书格式是否正确
     * @param string $certContent 证书内容
     * @return bool 是否为有效的PEM格式证书
     */
    protected function validateCertificateFormat($certContent)
    {
        // 检查是否包含证书头尾标记
        if (strpos($certContent, '-----BEGIN CERTIFICATE-----') === false || 
            strpos($certContent, '-----END CERTIFICATE-----') === false) {
            return false;
        }
        
        // 使用正则表达式验证证书格式
        $pattern = '/^-----BEGIN CERTIFICATE-----\s*([A-Za-z0-9+\/=\s]+)\s*-----END CERTIFICATE-----$/s';
        if (!preg_match($pattern, $certContent)) {
            return false;
        }
        
        return true;
    }

    /**
     * 验证根证书格式是否正确
     * @param string $rootCertContent 根证书内容
     * @return bool 是否为有效的PEM格式根证书
     */
    protected function validateRootCertificateFormat($rootCertContent)
    {
        // 根证书可能包含多个证书,至少要有一个有效证书
        $pattern = '/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/';
        preg_match_all($pattern, $rootCertContent, $matches);
        
        if (empty($matches[0])) {
            return false;
        }
        
        // 验证每个证书
        foreach ($matches[0] as $cert) {
            // 使用简单的格式验证
            if (strpos($cert, '-----BEGIN CERTIFICATE-----') !== false && 
                strpos($cert, '-----END CERTIFICATE-----') !== false) {
                return true; // 只要有一个证书格式正确即可
            }
        }
        
        return false;
    }

    /**
     * 获取支付宝配置
     * @return AlipayConfig
     */
    protected function getAlipayConfig()
    {
        try {
            $alipayConfig = new AlipayConfig();
            $alipayPayMode = isset($this->config['mode']) ? $this->config['mode'] : 'key';//接口加签方式 切换模式:key(密钥) 或 cert(证书)
            
            // 基础配置验证
            if (empty($this->config['app_id'])) {
                throw new \Exception('配置中缺少app_id');
            }
            if (empty($this->config['merchant_private_key'])) {
                throw new \Exception('配置中缺少应用私钥(merchant_private_key)');
            }
            
            // 设置网关地址
            $serverUrl = isset($this->config['sandbox']) && $this->config['sandbox'] ? 'https://openapi-sandbox.alipay.com' : 'https://openapi.alipay.com';
            $alipayConfig->setServerUrl($serverUrl);
            
            // 如果存在商户配置,使用商户配置
            if ($this->merchantConfig) {
                if (empty($this->merchantConfig['app_id'])) {
                    throw new \Exception('商户配置中缺少app_id');
                }
                $alipayConfig->setAppId($this->merchantConfig['app_id']);
                $privateKeyPath = $this->merchantConfig['merchant_private_key'];
                $publicKeyPath = $this->merchantConfig['alipay_public_key'];
                $certPath = 'certs/alipay/商户/';
                
                // 证书模式下的证书路径
                $appCertPath = isset($this->merchantConfig['app_cert_path']) ? $this->merchantConfig['app_cert_path'] : '';
                $alipayPublicCertPath = isset($this->merchantConfig['alipay_cert_path']) ? $this->merchantConfig['alipay_cert_path'] : '';
                $rootCertPath = isset($this->merchantConfig['root_cert_path']) ? $this->merchantConfig['root_cert_path'] : '';
            } else {
                // 使用平台配置
                $alipayConfig->setAppId($this->config['app_id']);
                $privateKeyPath = $this->config['merchant_private_key'];
                $publicKeyPath = $this->config['alipay_public_key'];
                $certPath = 'certs/alipay/';
                
                // 证书模式下的证书路径
                $appCertPath = isset($this->config['app_cert_path']) ? $this->config['app_cert_path'] : '';
                $alipayPublicCertPath = isset($this->config['alipay_cert_path']) ? $this->config['alipay_cert_path'] : '';
                $rootCertPath = isset($this->config['root_cert_path']) ? $this->config['root_cert_path'] : '';
                $certAppPprivatKeyPath = isset($this->config['cert_app_private_key_path']) ? $this->config['cert_app_private_key_path'] : '';
            }
            
            // 判断是否使用证书模式
            if ($alipayPayMode === 'cert') {
                // 证书模式
                if (empty($appCertPath) || empty($alipayPublicCertPath) || empty($rootCertPath) || empty($certAppPprivatKeyPath)) {
                    throw new \Exception('证书模式下必须设置完整的证书路径');
                }
                
                // 设置应用私钥 - 修改私钥格式判断和处理
                if (file_exists(root_path() . $certPath . $certAppPprivatKeyPath)) {
                    $privateKey = file_get_contents(root_path() . $certPath . $certAppPprivatKeyPath);
                } else {
                    $privateKey = $certAppPprivatKeyPath;
                }
                
                if (empty($privateKey)) {
                    throw new \Exception('商户私钥不能为空');
                }
                
                // // 如果私钥不是PEM格式,添加正确的RSA PRIVATE KEY头尾
                // if (strpos($privateKey, '-----BEGIN RSA PRIVATE KEY-----') === false) {
                //     $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" .
                //         wordwrap($privateKey, 64, "\n", true) .
                //         "\n-----END RSA PRIVATE KEY-----";
                // }
                $alipayConfig->setPrivateKey($privateKey);


                // 处理应用公钥证书
                if (!file_exists(root_path() . $certPath . $appCertPath)) {
                    throw new \Exception('应用公钥证书文件不存在');
                }
                $appCertContent = file_get_contents(root_path() . $certPath . $appCertPath);
                if (empty($appCertContent)) {
                    throw new \Exception('应用公钥证书内容为空');
                }
                
                // // 移除可能存在的BOM头和空白字符
                // $appCertContent = trim($appCertContent);
                // if (substr($appCertContent, 0, 3) === "\xEF\xBB\xBF") {
                //     $appCertContent = substr($appCertContent, 3);
                // }
                
                // 确保证书内容完整性
                if (strpos($appCertContent, '-----BEGIN CERTIFICATE-----') === false || 
                    strpos($appCertContent, '-----END CERTIFICATE-----') === false) {
                    throw new \Exception('证书格式不完整,请检查证书内容');
                }
                
                // 设置证书内容
                $alipayConfig->setAppCertContent($appCertContent);

                // 设置支付宝公钥证书
                if (!file_exists(root_path() . $certPath . $alipayPublicCertPath)) {
                    throw new \Exception('支付宝公钥证书文件不存在');
                }
                $alipayPublicCertContent = file_get_contents(root_path() . $certPath . $alipayPublicCertPath);
                if (empty($alipayPublicCertContent)) {
                    throw new \Exception('支付宝公钥证书内容为空');
                }

                // 直接设置证书内容,让SDK处理格式化
                $alipayConfig->setAlipayPublicCertContent($alipayPublicCertContent);

                // 设置支付宝根证书
                if (!file_exists(root_path() . $certPath . $rootCertPath)) {
                    throw new \Exception('支付宝根证书文件不存在');
                }
                $rootCertContent = file_get_contents(root_path() . $certPath . $rootCertPath);
                if (empty($rootCertContent)) {
                    throw new \Exception('支付宝根证书内容为空');
                }

                // 直接设置根证书内容,让SDK处理格式化
                $alipayConfig->setRootCertContent($rootCertContent);
            } else {

                if (file_exists(root_path() . $certPath . $privateKeyPath)) {
                    $privateKey = file_get_contents(root_path() . $certPath . $privateKeyPath);
                } else {
                    $privateKey = $privateKeyPath;
                }
                
                if (empty($privateKey)) {
                    throw new \Exception('商户私钥不能为空');
                }
                
                // // 如果私钥不是PEM格式,添加正确的RSA PRIVATE KEY头尾
                // if (strpos($privateKey, '-----BEGIN RSA PRIVATE KEY-----') === false) {
                //     $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" .
                //         wordwrap($privateKey, 64, "\n", true) .
                //         "\n-----END RSA PRIVATE KEY-----";
                // }
                $alipayConfig->setPrivateKey($privateKey);

                // 公钥模式
                if (file_exists(root_path() . $certPath . $publicKeyPath)) {
                    $publicKey = file_get_contents(root_path() . $certPath . $publicKeyPath);
                } else {
                    $publicKey = $publicKeyPath;
                }
                if (empty($publicKey)) {
                    throw new \Exception('支付宝公钥不能为空');
                }
                // 如果公钥不是PEM格式,添加PEM头尾
                if (strpos($publicKey, '-----BEGIN PUBLIC KEY-----') === false) {
                    $publicKey = "-----BEGIN PUBLIC KEY-----\n" .
                        wordwrap($publicKey, 64, "\n", true) .
                        "\n-----END PUBLIC KEY-----";
                }
                $alipayConfig->setAlipayPublicKey($publicKey);
            }
            
            return $alipayConfig;
        } catch (Exception $e) {
            $this->setError('支付宝配置初始化失败:' . $e->getMessage());
            return null;
        }
    }
    
    /**
     * 格式化证书内容
     * @param string $certContent 原始证书内容
     * @return string 格式化后的证书内容
     * @throws \Exception
     */
    protected function formatCertificate($certContent) 
    {
        if (empty($certContent)) {
            throw new \Exception('证书内容为空');
        }

        // 如果已经是PEM格式,直接返回
        if (strpos($certContent, '-----BEGIN CERTIFICATE-----') !== false && 
            strpos($certContent, '-----END CERTIFICATE-----') !== false) {
            return str_replace(["\r\n", "\r"], "\n", $certContent);
        }

        // 尝试转换为PEM格式
        $pattern = '/^[A-Za-z0-9+\/=\s]+$/';
        if (preg_match($pattern, trim($certContent))) {
            // 看起来是base64编码的内容
            $certContent = trim($certContent);
        } else {
            // 可能是二进制格式,进行base64编码
            $certContent = base64_encode($certContent);
        }

        // 添加证书头尾标记并格式化
        $formattedCert = "-----BEGIN CERTIFICATE-----\n" .
            trim(chunk_split($certContent, 64, "\n")) .
            "\n-----END CERTIFICATE-----";

        // 验证格式化后的证书
        if (!openssl_x509_read($formattedCert)) {
            throw new \Exception('无法生成有效的证书格式');
        }

        return $formattedCert;
    }
    
    /**
     * 创建电脑网站支付
     * @param array $payData 支付数据
     * @param string $payMode 支付模式:redirect(跳转), qrcode(二维码) form(返回表单式html代码)
     * @param string $payMerchantType 支付类型:默认=平台支付, merchant(商户支付)
     * @return array|string 成功返回支付表单或二维码链接,失败返回错误信息
     */
    public function createPayment($payData, $payMode = 'redirect', $payMerchantType = '')
    {
        try {
            // 根据payMerchantType设置是否使用商户配置
            if ($payMerchantType === 'merchant' && $this->merchantConfig) {
                $this->setMerchantConfig($this->merchantConfig);
            } else {
                $this->merchantConfig = null;
            }
            
            // 参数验证
            if (empty($payData['out_trade_no'])) {
                throw new \Exception("商户订单号不能为空");
            }
            if (empty($payData['total_amount']) || !is_numeric($payData['total_amount'])) {
                throw new \Exception("订单金额必须为数字");
            }
            if (empty($payData['subject'])) {
                throw new \Exception("订单标题不能为空");
            }
            
            // 初始化SDK
            $alipayConfig = $this->getAlipayConfig();
            if (!$alipayConfig) {
                throw new \Exception($this->getError() ?: '支付宝配置初始化失败');
            }
    
            // 构造业务参数
            $bizContent = [
                'out_trade_no' => $payData['out_trade_no'],
                'total_amount' => $payData['total_amount'],
                'subject' => $payData['subject'],
                'product_code' => 'FAST_INSTANT_TRADE_PAY',
                'timeout_express' => !empty($payData['timeout_express']) ? $payData['timeout_express'] : '30m'
            ];
    
            // 可选参数
            if (!empty($payData['body'])) {
                $bizContent['body'] = $payData['body'];
            }
            if (!empty($payData['time_expire'])) {
                $bizContent['time_expire'] = $payData['time_expire'];
            }
    
            // 构造请求参数
            $bizParams = [
                'biz_content' => $bizContent
            ];
    
            // 设置回调地址
            if (!empty($payData['notify_url'])) {
                $bizParams['notify_url'] = $payData['notify_url'];
            } elseif (!empty($this->config['notify_url'])) {
                $bizParams['notify_url'] = $this->config['notify_url'];
            }
    
            // 设置同步返回地址
            if (!empty($payData['return_url'])) {
                $bizParams['return_url'] = $payData['return_url'];
            } elseif (!empty($this->config['return_url'])) {
                $bizParams['return_url'] = $this->config['return_url'];
            }
    
            // var_dump($this->config); // 检查配置是否正确加载
            // var_dump($alipayConfig); // 检查支付宝配置对象
            // var_dump($bizParams); die;// 检查请求参数

            $alipayConfigUtil = new AlipayConfigUtil($alipayConfig);
            $apiInstance = new GenericExecuteApi(
                $alipayConfigUtil,
                new Client()
            );
    
            // 根据支付模式设置请求方法
            $method = $payMode == 'form' ? 'POST' : 'GET';
            
            // 执行请求
            $pageRedirectionData = $apiInstance->pageExecute('alipay.trade.page.pay', $method, $bizParams);
            
            if (!$pageRedirectionData) {
                throw new \Exception('支付创建失败');
            }
    
            return [
                'code' => 200,
                'msg' => 'ok',
                'data' => [
                    'order_id' => $payData['out_trade_no'],
                    $payMode == 'qrcode' ? 'qr_code' : ($payMode == 'form' ? 'form_html' : 'pay_url') => $pageRedirectionData
                ],
                'mode' => $payMode
            ];
    
        } catch (ApiException $e) {
            $responseBody = json_decode($e->getResponseBody(), true);
            $errorMsg = !empty($responseBody['code']) && !empty($responseBody['message']) 
                ? $responseBody['code'] . ':' . $responseBody['message']
                : '支付创建失败: ' . $e->getMessage();
                
            return [
                'code' => -200,
                'msg' => $errorMsg
            ];
        } catch (Exception $e) {
            return [
                'code' => -200,
                'msg' => '支付创建异常: ' . $e->getMessage()
            ];
        }
    }
    
    /**
     * 创建APP支付
     * @param array $payData 支付数据
     * @param string $payType 支付类型:platform(平台支付), merchant(商户支付)
     * @return array 支付结果
     */
    public function createAppPayment($payData, $payType = 'platform')
    {
        try {
            // 根据payType设置是否使用商户配置
            if ($payType === 'merchant' && $this->merchantConfig) {
                $this->setMerchantConfig($this->merchantConfig);
            } else {
                $this->merchantConfig = null;
            }
            // 参数验证
            if (empty($payData['out_trade_no'])) {
                //  $this->setError('商户订单号不能为空')->getError();
                 throw new \Exception("商户订单号不能为空");
            }
            if (empty($payData['total_amount']) || !is_numeric($payData['total_amount'])) {
                //  $this->setError('订单金额必须为数字')->getError();
                 throw new \Exception("订单金额必须为数字");
            }
            if (empty($payData['subject'])) {
                //  $this->setError('订单标题不能为空')->getError();
                 throw new \Exception("订单标题不能为空");
            }
            
            // 初始化SDK
            $alipayConfig = $this->getAlipayConfig();
            if (!$alipayConfig) {
                 $this->getError();
            }
            
            $alipayConfigUtil = new AlipayConfigUtil($alipayConfig);
            $apiInstance = new GenericExecuteApi(
                $alipayConfigUtil,
                new Client()
            );
            
            // 构造请求参数
            $bizParams = [];
            $bizContent = [];
            
            // 设置商户订单号
            $bizContent['out_trade_no'] = $payData['out_trade_no'];
            
            // 设置订单总金额
            $bizContent['total_amount'] = $payData['total_amount'];
            
            // 设置订单标题
            $bizContent['subject'] = $payData['subject'];
            
            // 设置产品码 - APP支付
            $bizContent['product_code'] = 'QUICK_MSECURITY_PAY';
            
            // 设置订单描述(可选)
            if (!empty($payData['body'])) {
                $bizContent['body'] = $payData['body'];
            }
            
            // 设置超时时间(可选)
            if (!empty($payData['timeout_express'])) {
                $bizContent['timeout_express'] = $payData['timeout_express'];
            } else {
                // 默认30分钟
                $bizContent['timeout_express'] = '30m';
            }
            
            // 绝对超时时间(可选)
            if (!empty($payData['time_expire'])) {
                $bizContent['time_expire'] = $payData['time_expire'];
            }
            
            // 设置回调地址
            if (!empty($payData['notify_url'])) {
                $bizParams['notify_url'] = $payData['notify_url'];
            } else if (!empty($this->config['notify_url'])) {
                $bizParams['notify_url'] = $this->config['notify_url'];
            }
            
            // 设置同步返回地址
            if (!empty($payData['return_url'])) {
                // 如果传入了前一页面URL,将其作为参数添加到return_url
                if (!empty($payData['prev_url'])) {
                    $return_url = $payData['return_url'];
                    if (strpos($return_url, '?') !== false) {
                        $return_url .= '&prev_url=' . urlencode($payData['prev_url']);
                    } else {
                        $return_url .= '?prev_url=' . urlencode($payData['prev_url']);
                    }
                    $bizParams['return_url'] = $return_url;
                } else {
                    $bizParams['return_url'] = $payData['return_url'];
                }
            } else if (!empty($this->config['return_url'])) {
                $bizParams['return_url'] = $this->config['return_url'];
            }
            
            // 将业务参数放入请求参数中
            $bizParams['biz_content'] = $bizContent;
            
            // 执行请求 - 获取APP支付所需的参数字符串
            $result = $apiInstance->sdkExecute('alipay.trade.app.pay', $bizParams);
            
            return [
                'success' => true,
                'data' => $result,
                'mode' => 'app'
            ];
            
        } catch (ApiException $e) {
            // 捕获支付宝API异常
            $responseBody = json_decode($e->getResponseBody(), true);
            $errorMsg = '';
            if (!empty($responseBody['code']) && !empty($responseBody['message'])) {
                $errorMsg = $responseBody['code'] . ':' . $responseBody['message'];
            } else {
                $errorMsg = '关闭订单失败: ' . $e->getMessage();
            }
            return [
                'code' => -200,
                'msg' => $errorMsg
            ];
            // $errorMsg = 'APP支付请求失败: ' . $e->getMessage();
            // $errorDetails = [
            //     'message' => $e->getMessage(),
            //     'body' => $e->getResponseBody(),
            //     'headers' => $e->getResponseHeaders()
            // ];
            
            // $this->setError($errorMsg);
            // return [
            //     'code' => -200,
            //     'msg' => $errorMsg,
            //     'error_details' => $errorDetails
            // ];
        } catch (Exception $e) {
            // 捕获其他异常
            $errorMsg = 'APP支付创建异常: ' . $e->getMessage();
            $this->setError($errorMsg);
            return [
                'code' => -200,
                'msg' => $errorMsg
            ];
        }
    }
    
    /**
     * 查询订单
     * @param array $postData 查询参数
     * @param string $postData['out_trade_no'] 商户订单号
     * @param string $postData['trade_no'] 支付宝交易号(与商户订单号二选一,如果同时传入优先取商户订单号)
     * @return array 订单信息
     */
    public function queryOrder(array $postData)
    {
        try {
            if (empty($postData)) {
                //  $this->setError('查询参数为空')->getError();
                 throw new \Exception("查询参数为空");
            }
            $outTradeNo = !empty($postData['out_trade_no']) ? $postData['out_trade_no'] : '';
            $tradeNo = !empty($postData['trade_no']) ? $postData['trade_no'] : '';
            // 参数验证
            if (empty($outTradeNo) && empty($tradeNo)) {
                //  $this->setError('商户订单号和支付宝交易号不能同时为空')->getError();
                 throw new \Exception("商户订单号和支付宝交易号不能同时为空");
            }
            
            // 初始化SDK
            $alipayConfig = $this->getAlipayConfig();
            if (!$alipayConfig) {
                 $this->getError();
            }
            
            $alipayConfigUtil = new AlipayConfigUtil($alipayConfig);
            $apiInstance = new AlipayTradeApi(
                new Client(),
                null,
                null,
                0,
                $alipayConfigUtil
            );
            
            // 构造请求参数
            $model = new \Alipay\OpenAPISDK\Model\AlipayTradeQueryModel();
            
            // 设置查询参数
            if (!empty($outTradeNo)) {
                $model->setOutTradeNo($outTradeNo);
            }
            if (!empty($tradeNo)) {
                $model->setTradeNo($tradeNo);
            }
            
            // 执行请求
            $result = $apiInstance->query($model);
            $Status = $result->getTradeStatus();//交易状态:WAIT_BUYER_PAY(交易创建,等待买家付款)、TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)、TRADE_SUCCESS(交易支付成功)、TRADE_FINISHED(交易结束,不可退款)
            if ($Status == 'TRADE_SUCCESS') {//TRADE_SUCCESS 和 TRADE_FINISHED 都表示交易成功,区别只在于 TRADE_SUCCESS 状态可进行退款。
                //支付成功-可进行退款
                $code = 200;$msg = '支付成功';
            } else if($Status == 'TRADE_FINISHED') {
                //同样表示交易成功(交易结束,不可退款)
                $code = 300;$msg = '支付成功';
            } else if($Status == 'WAIT_BUYER_PAY') {
                //等待买家付款
                return ['code' => 210,'msg' => '等待买家付款','data' => []];
            } else if($Status == 'TRADE_CLOSED') {
                //交易关闭
                return ['code' => 220,'msg' => '交易关闭','data' => []];
            } else {
                //支付失败
                $code = -200;$msg = '未知状态';
                return ['code' => -200,'msg' => '未知状态','data' => []];
            }

            $total_amount = $result->getTotalAmount();//订单金额
            $TradeNo = $result->getTradeNo();//支付宝交易号:
            $OutTradeNo = $result->getOutTradeNo();//商户订单号:
            $BuyerLogonId = $result->getBuyerLogonId();//买家支付宝账号:
            $BuyerUserId = $result->getBuyerUserId();//买家用户ID:
            // $BuyerPayAmount = $result->getBuyerPayAmount();//实付金额:
            $SendPayDate = $result->getSendPayDate();//交易创建时间:

            $data = [
                'trade_status' => $result->getTradeStatus(),
                'trade_no' => $result->getTradeNo(),
                'out_trade_no' => $result->getOutTradeNo(),
                'buyer_logon_id' => $result->getBuyerLogonId(),
                'total_amount' => $result->getTotalAmount(),
                'buyer_pay_amount' => $result->getBuyerPayAmount(),
                'point_amount' => $result->getPointAmount(),
                'invoice_amount' => $result->getInvoiceAmount(),
                'send_pay_date' => $result->getSendPayDate(),
                'receipt_amount' => $result->getReceiptAmount(),
                'store_id' => $result->getStoreId(),
                'terminal_id' => $result->getTerminalId(),
                'fund_bill_list' => $result->getFundBillList(),
                'store_name' => $result->getStoreName(),
                'buyer_user_id' => $result->getBuyerUserId(),
                'charge_amount' => $result->getChargeAmount(),
                'charge_flags' => $result->getChargeFlags(),
                'settlement_id' => $result->getSettlementId(),
                'trade_settle_info' => $result->getTradeSettleInfo(),
                'auth_trade_pay_mode' => $result->getAuthTradePayMode(),
                'buyer_user_type' => $result->getBuyerUserType(),
                'mdiscount_amount' => $result->getMdiscountAmount(),
                'discount_amount' => $result->getDiscountAmount(),
                'buyer_user_name' => $result->getBuyerUserName()
            ];
            
            return ['code' => $code,'msg' => 'success','data' => $data];
            
        } catch (ApiException $e) {
            // 捕获支付宝API异常
            $responseBody = json_decode($e->getResponseBody(), true);
            
            // 特殊处理code=400的情况(通常是签名验证失败)
            if ($e->getCode() == 400 && !empty($responseBody) && strpos($e->getMessage(), 'sign check fail') !== false) {
                //问题出在支付宝查询订单接口的签名验证失败。错误信息显示:"sign check fail: check Sign and Data Fail!",但同时响应体中包含了正确的订单信息
                return ['code' => -200, 'msg' => '证书错误,请检查证书内容与路径!', 'data' => $responseBody];
            }
            // 捕获支付宝API异常
            $responseBody = json_decode($e->getResponseBody(), true);
            $errorMsg = '';
            if (!empty($responseBody['code']) && !empty($responseBody['message'])) {
                $errorMsg = $responseBody['code'] . ':' . $responseBody['message'];
            } else {
                $errorMsg = $e->getMessage();
            }
            
            return [
                'code' => -200,
                'msg' => $errorMsg
            ];
        } catch (Exception $e) {
            // 捕获其他异常
            $errorMsg = '订单查询异常: ' . $e->getMessage();
            $this->setError($errorMsg);
            return [
                'code' => -200,
                'msg' => $errorMsg
            ];
        }
    }
    
    /**
     * 关闭订单
     * @param array $postData 查询参数
     * @param string $postData['out_trade_no'] 商户订单号
     * @param string $postData['trade_no'] 支付宝交易号(与商户订单号二选一,如果同时传入优先取商户订单号)
     * @return array 关闭结果
     */
    public function closeOrder(array $postData)
    {
        try {
            if (empty($postData)) {
                 throw new \Exception("查询参数为空");
            }
            $outTradeNo = !empty($postData['out_trade_no']) ? $postData['out_trade_no'] : '';
            $tradeNo = !empty($postData['trade_no']) ? $postData['trade_no'] : '';
            // 参数验证
            if (empty($outTradeNo) && empty($tradeNo)) {
                //  $this->setError('商户订单号和支付宝交易号不能同时为空')->getError();
                 throw new \Exception("商户订单号和支付宝交易号不能同时为空");
            }
            
            // 初始化SDK
            $alipayConfig = $this->getAlipayConfig();
            if (!$alipayConfig) {
                 $this->getError();
            }
            
            $alipayConfigUtil = new AlipayConfigUtil($alipayConfig);
            $apiInstance = new AlipayTradeApi(
                new Client(),
                null,
                null,
                0,
                $alipayConfigUtil
            );
            
            // 构造请求参数
            $model = new \Alipay\OpenAPISDK\Model\AlipayTradeCloseModel();
            
            // 设置查询参数
            if (!empty($outTradeNo)) {
                $model->setOutTradeNo($outTradeNo);
            }
            if (!empty($tradeNo)) {
                $model->setTradeNo($tradeNo);
            }
            
            // 执行请求
            $result = $apiInstance->close($model);
            
            // 处理关单结果
            if ($result instanceof \Alipay\OpenAPISDK\Model\AlipayTradeCloseResponseModel) {
                // 关单成功
                return ['code' => 200, 'msg' => '订单关闭成功', 'data' => [
                    'out_trade_no' => $result->getOutTradeNo(),
                    'trade_no' => $result->getTradeNo()
                ]];
            } else {
                // 关单失败
                //  $this->setError('订单关闭失败')->getError();
                throw new \Exception("订单关闭失败");
            }
            
        } catch (ApiException $e) {
            // 捕获支付宝API异常
            $responseBody = json_decode($e->getResponseBody(), true);
            $errorMsg = '';$code = -200;
            if (!empty($responseBody['code']) && !empty($responseBody['message'])) {
                $errorMsg = $responseBody['message'];
                if ($responseBody['code'] == 'ACQ.TRADE_STATUS_ERROR') {//等待买家付款
                    $message = '订单买家未支付';
                } else if($responseBody['code'] == "ACQ.TRADE_NOT_EXIST") {//交易不存在
                    $message = '订单未生成,请使用支付软件来支付';
                } else if($responseBody['code'] == "ACQ.TRADE_STATUS_ERROR") {//检查当前交易的状态是不是等待买家付款,只有等待买家付款状态下才能发起交易关闭。
                    $message = '等待买家付款';
                }
                $code = -301;
                // $errorMsg = '关闭订单失败: ' .$responseBody['code'] . ':' . $responseBody['message'];
            } else {
                $errorMsg = '关闭订单失败: ' . $e->getMessage();
            }
            return ['code' => $code,'errCode' => $responseBody['code'],'msg' => $errorMsg];
        } catch (Exception $e) {
            // 捕获其他异常
            $errorMsg = '关闭订单异常: ' . $e->getMessage();
            $this->setError($errorMsg);
            return [
                'code' => -200,
                'msg' => $errorMsg
            ];
        }
    }
    
    /**
     * 申请退款
     * @param array $postData 查询参数
     * @param string $postData['out_trade_no'] 商户订单号
     * @param string $postData['trade_no'] 支付宝交易号(与商户订单号二选一,如果同时传入优先取商户订单号)
     * @param string $postData['out_request_no'] 退款请求号,标识一次退款请求,需要保证在交易号下唯一
     * @param float $postData['refund_amount'] 退款金额,不能大于订单总金额
     * @param string $postData['refund_reason'] 退款原因(可选)
     * @return array 退款结果
     */
    public function refund(array $postData)
    {
        try {
            if (empty($postData)) {
                // $this->setError('查询参数为空')->getError();
                throw new \Exception("查询参数为空");
            }
            $outTradeNo =!empty($postData['out_trade_no'])? $postData['out_trade_no'] : '';
            $tradeNo =!empty($postData['trade_no'])? $postData['trade_no'] : '';
            $outRequestNo =!empty($postData['out_request_no'])? $postData['out_request_no'] : '';
            $refundAmount =!empty($postData['refund_amount'])? $postData['refund_amount'] : '';
            $refundReason =!empty($postData['refund_reason'])? $postData['refund_reason'] : '';

            // 参数验证
            if (empty($outTradeNo) && empty($tradeNo)) {
                // $this->setError('商户订单号和支付宝交易号不能同时为空')->getError();
                throw new \Exception("商户订单号和支付宝交易号不能同时为空");
            }
            
            if (empty($outRequestNo)) {
                // $this->setError('退款请求号不能为空')->getError();
                throw new \Exception("退款请求号不能为空");
            }
            
            if (empty($refundAmount) || !is_numeric($refundAmount) || $refundAmount <= 0) {
                // $this->setError('退款金额必须为大于0的数字')->getError();
                throw new \Exception("退款金额必须为大于0的数字");
            }
            
            // 添加退款请求间隔限制(至少3秒)
            $cacheKey = 'refund_request_' . ($outTradeNo ?: $tradeNo);
            $lastRefundTime = cache($cacheKey);
            if ($lastRefundTime && time() - $lastRefundTime < 3) {
                // $this->setError('退款请求过于频繁,请稍后再试')->getError();
                throw new \Exception("退款请求过于频繁,请稍后再试");
            }
            cache($cacheKey, time(), 10); // 缓存10秒
            
            // 初始化SDK
            $alipayConfig = $this->getAlipayConfig();
            if (!$alipayConfig) {
                 $this->getError();
            }
            
            $alipayConfigUtil = new AlipayConfigUtil($alipayConfig);
            $apiInstance = new AlipayTradeApi(
                new Client(),
                null,
                null,
                0,
                $alipayConfigUtil
            );
            
            // 构造请求参数
            $model = new \Alipay\OpenAPISDK\Model\AlipayTradeRefundModel();
            
            // 设置退款参数
            if (!empty($outTradeNo)) {
                $model->setOutTradeNo($outTradeNo);
            }
            if (!empty($tradeNo)) {
                $model->setTradeNo($tradeNo);
            }
            
            // 设置退款金额
            $model->setRefundAmount($refundAmount);
            
            // 设置退款请求号
            $model->setOutRequestNo($outRequestNo);
            
            // 设置退款原因(可选)
            if (!empty($refundReason)) {
                $model->setRefundReason($refundReason);
            }
            
            // 执行请求
            $result = $apiInstance->refund($model);

            // 处理退款结果
            if ($result && $result->getFundChange() === 'Y') {//Y 表示退款成功,N也不是表示退款失败需要通过查询退款查询接口来确定
                // 退款成功
                return [
                    'code' => 200,
                    'msg' => '退款成功',
                    'data' => [
                        'refund_status' => 'REFUND_SUCCESS',
                        'trade_no' => $result->getTradeNo(),
                        'out_trade_no' => $result->getOutTradeNo(),
                        'buyer_logon_id' => $result->getBuyerLogonId(),
                        'fund_change' => $result->getFundChange(),
                        'refund_fee' => $result->getRefundFee(),
                        'refund_currency' => $result->getRefundCurrency(),
                        'gmt_refund_pay' => $result->getGmtRefundPay(),
                        'send_back_fee' => $result->getSendBackFee(),
                        'refund_detail_item_list' => $result->getRefundDetailItemList()
                    ]
                ];
            } else if ($result) {
                //$result->getFundChange() === 'N',不是表示退款失败,多次请求这个接口申请退款接口就会返回N
                // 需要通过退款查询接口确认状态
                $queryRefundData  = $this->queryRefund(['trade_no' => $result->getTradeNo(), 'out_request_no' => $result->getOutTradeNo()]);
                if ($queryRefundData['code'] == 200) {
                    return [
                        'code' => 200,
                        'msg' => '退款成功',
                        'data' => [
                            'trade_no' => !empty($queryRefundData['data']['trade_no'])? $queryRefundData['data']['trade_no'] : '',
                            'out_trade_no' => !empty($queryRefundData['data']['out_trade_no'])? $queryRefundData['data']['out_trade_no'] : '',
                            'out_request_no' => !empty($queryRefundData['data']['out_request_no'])? $queryRefundData['data']['out_request_no'] : '',
                            'refund_amount' => !empty($queryRefundData['data']['refund_amount'])? $queryRefundData['data']['refund_amount'] : '',
                            'total_amount' => !empty($queryRefundData['data']['total_amount'])? $queryRefundData['data']['total_amount'] : '',
                            'refund_status' => !empty($queryRefundData['data']['refund_status'])? $queryRefundData['data']['refund_status'] : '',
                            'gmt_refund_pay' => !empty($queryRefundData['data']['gmt_refund_pay'])? $queryRefundData['data']['gmt_refund_pay'] : '',
                        ]
                    ];
                }else{
                    $this->setError($queryRefundData['msg'])->getError();
                }
            } else {
                // 退款失败
                return [
                    'code' => -200,
                    'msg' => $result ? $result->getMessage() : '退款申请失败',
                    'sub_code' => $result ? $result->getCode() : '',
                ];
            }
            
        } catch (ApiException $e) {
            // 捕获支付宝API异常
            $responseBody = json_decode($e->getResponseBody(), true);
            $errorMsg = '';
            if (!empty($responseBody['code']) && !empty($responseBody['message'])) {
                $errorMsg = $responseBody['code'] . ':' . $responseBody['message'];
            } else {
                $errorMsg = $e->getMessage();
            }
            return [
                'code' => -200,
                'msg' => $errorMsg
            ];
            //比较详细的错误信息
            // $errorMsg = '申请退款失败: ' . $e->getMessage();
            // $errorDetails = [
            //     'message' => $e->getMessage(),
            //     'body' => $e->getResponseBody(),
            //     'headers' => $e->getResponseHeaders()
            // ];
            
            // $this->setError($errorMsg);
            // return [
            //     'code' => -200,
            //     'msg' => $errorMsg,
            //     'error_details' => $errorDetails
            // ];
        } catch (Exception $e) {
            // 捕获其他异常
            $errorMsg = '申请退款异常: ' . $e->getMessage();
            $this->setError($errorMsg);
            return [
                'code' => -200,
                'msg' => $errorMsg
            ];
        }
    }
    
    /**
     * 查询退款
     * @param array $postData 查询参数
     * @param string $postData['out_trade_no'] 商户订单号
     * @param string $postData['trade_no'] 支付宝交易号(与商户订单号二选一,如果同时传入优先取商户订单号)
     * @param string $postData['out_request_no'] 退款请求号
     * @return array 退款查询结果
     */
    public function queryRefund(array $postData)
    {
        try {
            if (empty($postData)) {
                //  $this->setError('查询参数为空')->getError();
                 throw new \Exception("查询参数为空");
            }
            $outRequestNo =!empty($postData['out_request_no'])? $postData['out_request_no'] : '';
            $outTradeNo =!empty($postData['out_trade_no'])? $postData['out_trade_no'] : '';
            $tradeNo =!empty($postData['trade_no'])? $postData['trade_no'] : '';
            // 参数验证
            if (empty($outTradeNo) && empty($tradeNo)) {
                //  $this->setError('商户订单号和支付宝交易号不能同时为空')->getError();
                 throw new \Exception("商户订单号和支付宝交易号不能同时为空");
            }
            
            if (empty($outRequestNo)) {
                //  $this->setError('退款请求号不能为空')->getError();
                 throw new \Exception("退款请求号不能为空");
            }
            
            // 初始化SDK
            $alipayConfig = $this->getAlipayConfig();
            if (!$alipayConfig) {
                 $this->getError();
            }
            
            $alipayConfigUtil = new AlipayConfigUtil($alipayConfig);
            $apiInstance = new AlipayTradeFastpayRefundApi(
                new Client(),
                null,
                null,
                0,
                $alipayConfigUtil
            );

            // 设置查询参数
            $model = new AlipayTradeFastpayRefundQueryModel();
            if (!empty($outTradeNo)) {
                $model->setOutTradeNo($outTradeNo);
            }
            if (!empty($tradeNo)) {
                $model->setTradeNo($tradeNo);
            }
            $model->setOutRequestNo($outRequestNo);

            // 发起查询请求
            $result = $apiInstance->query($model);
            
            // 处理退款查询结果
            $refundStatus = $result->getRefundStatus();
            if ($result && $refundStatus === 'REFUND_SUCCESS') {
                    $data = [
                        'trade_no' => $result->getTradeNo(),
                        'out_trade_no' => $result->getOutTradeNo(), 
                        'out_request_no' => $result->getOutRequestNo(),
                        'refund_amount' => $result->getRefundAmount(),
                        'total_amount' => $result->getTotalAmount(),
                        'refund_status' => $refundStatus,
                        'gmt_refund_pay' => $result->getGmtRefundPay()
                    ];
                    // 退款成功
                    return [
                        'code' => 200,
                        'msg' => '退款成功',
                        'data' => $data
                    ];
            } else {
                throw new \Exception("此订单没有提出退款申请");
                // 查询失败
            }
        } catch (ApiException $e) {
            // 捕获支付宝API异常
            $responseBody = json_decode($e->getResponseBody(), true);
            $errorMsg = '';
            if (!empty($responseBody['code']) && !empty($responseBody['message'])) {
                $errorMsg = $responseBody['code'] . ':' . $responseBody['message'];
            } else {
                $errorMsg = $e->getMessage();
            }
            return [
                'code' => -200,
                'msg' => $errorMsg
            ];
             //比较详细的错误信息
            // $errorMsg = '申请退款失败: ' . $e->getMessage();
            // $errorDetails = [
            //     'message' => $e->getMessage(),
            //     'body' => $e->getResponseBody(),
            //     'headers' => $e->getResponseHeaders()
            // ];
            
            // $this->setError($errorMsg);
            // return [
            //     'code' => -200,
            //     'msg' => $errorMsg,
            //     'error_details' => $errorDetails
            // ];
        } catch (\Exception $e) {
            // return $this->setError($e->getMessage())->getError();
            $errorMsg = $e->getMessage();
            $this->setError($errorMsg);
            return [
                'code' => -200,
                'msg' => $errorMsg
            ];

        }
    }
    
    /**
     * 验证异步通知签名
     * @param array $params 通知参数
     * @return bool 验证结果
     */
    public function verifyNotify($params)
    {
        try {
            // 获取支付宝配置
            $alipayConfig = $this->getAlipayConfig();
            if (!$alipayConfig) {
                return false;
            }
            
            $alipayConfigUtil = new AlipayConfigUtil($alipayConfig);
            
            // 移除sign_type参数
            if (isset($params['sign_type'])) {
                unset($params['sign_type']);
            }
            
            // 获取签名
            $sign = '';
            if (isset($params['sign'])) {
                $sign = $params['sign'];
                unset($params['sign']);
            } else {
                return false;
            }
            
            // 按照支付宝要求排序参数
            ksort($params);
            
            // 构建待签名字符串
            $stringToSign = '';
            foreach ($params as $k => $v) {
                if (!empty($v) && $v !== '' && !is_array($v)) {
                    $stringToSign .= "$k=$v&";
                }
            }
            $stringToSign = rtrim($stringToSign, '&');
            
            // 验证签名
            // 使用SDK V3版本的验签方法
            return $alipayConfigUtil->verify($stringToSign, $sign, 'RSA2', false, 'UTF-8');
            
        } catch (Exception $e) {
            $this->setError('验证异步通知签名异常: ' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * 处理异步通知
     * @param array $params 通知参数
     * @return array 处理结果
     */
    public function handleNotify($params)
    {
        // 验证签名
        if (!$this->verifyNotify($params)) {
            return [
                'success' => false,
                'error' => '异步通知验签失败'
            ];
        }
        
        // 验证通知类型
        if (!isset($params['notify_type']) || $params['notify_type'] != 'trade_status_sync') {
            return [
                'success' => false,
                'error' => '非交易状态通知'
            ];
        }
        
        // 验证app_id是否为当前应用的app_id
        if (!isset($params['app_id']) || $params['app_id'] != $this->config['app_id']) {
            return [
                'success' => false,
                'error' => 'app_id不匹配'
            ];
        }
        
        // 验证交易状态
        if (!isset($params['trade_status'])) {
            return [
                'success' => false,
                'error' => '通知中不包含交易状态'
            ];
        }
        
        // 返回处理结果
        return [
            'success' => true,
            'data' => [
                'out_trade_no' => $params['out_trade_no'] ?? '',
                'trade_no' => $params['trade_no'] ?? '',
                'trade_status' => $params['trade_status'],
                'total_amount' => $params['total_amount'] ?? 0,
                'gmt_payment' => $params['gmt_payment'] ?? '',
                'buyer_id' => $params['buyer_id'] ?? '',
                'buyer_logon_id' => $params['buyer_logon_id'] ?? '',
                'seller_id' => $params['seller_id'] ?? '',
                'notify_time' => $params['notify_time'] ?? '',
                'notify_id' => $params['notify_id'] ?? '',
                'notify_type' => $params['notify_type'] ?? '',
                'raw_data' => $params
            ]
        ];
    }
    
    /**
     * 创建手机网站支付
     * @param array $payData 支付数据
     * @param string $payMode 支付模式:redirect(跳转), qrcode(二维码) form(返回表单式html代码)
     * @return array 支付结果
     */
    public function createWapPayment(array $payData,string $payMode):array
    {
        try {
            // 参数验证
            if (empty($payData['out_trade_no'])) {
                //  $this->setError('商户订单号不能为空')->getError();
                 throw new \Exception("商户订单号不能为空");
            }
            if (empty($payData['total_amount']) || !is_numeric($payData['total_amount'])) {
                //  $this->setError('订单金额必须为数字')->getError();
                 throw new \Exception("订单金额必须为数字");
            }
            if (empty($payData['subject'])) {
                //  $this->setError('订单标题不能为空')->getError();
                 throw new \Exception("订单标题不能为空");
            }
            
            // 初始化SDK
            $alipayConfig = $this->getAlipayConfig();
            if (!$alipayConfig) {
                 $this->getError();
            }
            
            $alipayConfigUtil = new AlipayConfigUtil($alipayConfig);
            $apiInstance = new GenericExecuteApi(
                $alipayConfigUtil,
                new Client()
            );
            
            // 构造请求参数
            $bizParams = [];
            $bizContent = [];
            
            // 设置商户订单号
            $bizContent['out_trade_no'] = $payData['out_trade_no'];
            
            // 设置订单总金额
            $bizContent['total_amount'] = $payData['total_amount'];
            
            // 设置订单标题
            $bizContent['subject'] = $payData['subject'];
            
            // 设置产品码 - 手机网站支付
            $bizContent['product_code'] = 'QUICK_WAP_WAY';
            
            // 设置订单描述(可选)
            if (!empty($payData['body'])) {
                $bizContent['body'] = $payData['body'];
            }
            
            // 设置超时时间(可选)
            if (!empty($payData['timeout_express'])) {
                $bizContent['timeout_express'] = $payData['timeout_express'];
            } else {
                // 默认30分钟
                $bizContent['timeout_express'] = '30m';
            }
            
            // 绝对超时时间(可选)
            if (!empty($payData['time_expire'])) {
                $bizContent['time_expire'] = $payData['time_expire'];
            }
            
            // 设置回调地址
            if (!empty($payData['notify_url'])) {
                $bizParams['notify_url'] = $payData['notify_url'];
            } else if (!empty($this->config['notify_url'])) {
                $bizParams['notify_url'] = $this->config['notify_url'];
            }
            
            // 设置同步返回地址
            if (!empty($payData['return_url'])) {
                // 如果传入了前一页面URL,将其作为参数添加到return_url
                if (!empty($payData['prev_url'])) {
                    $return_url = $payData['return_url'];
                    if (strpos($return_url, '?') !== false) {
                        $return_url .= '&prev_url=' . urlencode($payData['prev_url']);
                    } else {
                        $return_url .= '?prev_url=' . urlencode($payData['prev_url']);
                    }
                    $bizParams['return_url'] = $return_url;
                } else {
                    $bizParams['return_url'] = $payData['return_url'];
                }
            } else if (!empty($this->config['return_url'])) {
                $bizParams['return_url'] = $this->config['return_url'];
            }
            
            // 将业务参数放入请求参数中
            $bizParams['biz_content'] = $bizContent;

            if($payMode=='redirect'){
                $Mode = 'GET';//转跳网址
            }else{
                $Mode = 'POST';//表单元素
            }
            
            // 执行请求
            $pageRedirectionData = $apiInstance->pageExecute('alipay.trade.wap.pay', $Mode, $bizParams);
            $data['code'] = 200;
            $data['msg'] = 'ok';
            $data['data']['order_id'] = $payData['out_trade_no'];
            $data['data']['pay_url'] = $pageRedirectionData;
            $data['mode'] = $payMode;
            return $data;
        } catch (ApiException $e) {
            // 捕获支付宝API异常
            $errorMsg = '手机网站支付请求失败: ' . $e->getMessage();
            $errorDetails = [
                'message' => $e->getMessage(),
                'body' => $e->getResponseBody(),
                'headers' => $e->getResponseHeaders()
            ];
            
            $this->setError($errorMsg);
            return [
                'code' => -200,
                'msg' => $errorMsg,
                'error_details' => $errorDetails
            ];
        } catch (Exception $e) {
            // 捕获其他异常
            $errorMsg = '手机网站支付创建异常: ' . $e->getMessage();
            $this->setError($errorMsg);
            return [
                'code' => -200,
                'msg' => $errorMsg
            ];
        }
    }
    
    /**
     * 创建APP支付
     * @param array $payData 支付数据
     * @return array 支付结果
     */
    // 已在前面定义过createAppPayment方法,此处删除重复定义
}

五 设置储存证书文件夹与证书文件的权限

文件夹应设置为 700(所有者可读写执行)

.pem(私钥)400或600仅所有者可读写,防止未授权修改或泄露(参考知识库[2])。
.crt(证书)400仅所有者可读,确保敏感证书不被其他用户访问(如支付宝公钥或根证书)。

宝塔面板中配置Nginx禁止访问证书目录
 server{...} 块内添加以下规则(建议放在 location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ 等静态资源规则之后):

location ^~ /储存证书文件夹/ {
    deny all;
    return 403;
}

并「重载Nginx服务」生效

参考:如何配置支付宝密钥之如何配置证书|保姆级教学(二)_alipaycertpath-CSDN博客

相关文章:

  • 手撕TCP内网穿透及配置树莓派
  • 【DDR 内存学习专栏 1.2 -- DDR Channel 介绍】
  • 【webSocket协议】进阶实战案例(Spring 原生低层 API)
  • Python基础语法1
  • C# 混淆代码工具--ConfuserEx功能与使用指南
  • 边缘计算:从概念到落地的技术解读
  • SQL语言基础(二)--以postersql为例
  • MySQL 的lock_wait_timeout 参数
  • 【C++初学】课后作业汇总复习(六) 函数模板
  • HarmonyOS: ArkUI V2装饰器-@Event:规范组件输出
  • AF3 ProteinDataset类的_patch方法解读
  • 如何在 Windows 安卓子系统 (WSA) 上安装小红书应用
  • Linux学习笔记_002:用户的基本操作
  • Node.js中URL模块详解
  • 【docker】--部署--安装docker教程
  • Linux内存管理架构(2)
  • WheatA小麦芽:农业气象大数据下载器
  • Python依赖注入完全指南:高效解耦、技术深析与实践落地
  • Midjourney 图生图:实现人物一致性的多元场景选择
  • 使用Java截取MP4文件图片的技术指南
  • 西湖大学本科招生新增三省两市,首次面向上海招生
  • 君亭酒店:2024年营业收入约6.76亿元, “酒店行业传统增长模式面临巨大挑战”
  • 玉渊谭天丨中方减少美国农产品进口后,舟山港陆续出现巴西大豆船
  • 传智教育连续3个交易日跌停:去年净利润由盈转亏
  • 中央纪委办公厅公开通报3起整治形式主义为基层减负典型问题
  • 王庆成:儒家、墨家和洪秀全的“上帝”