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博客