修改支付宝支付签名问题

This commit is contained in:
邹景立 2023-05-07 14:08:00 +08:00
parent 5db97a1d90
commit 940cdf8abf
4 changed files with 91 additions and 19 deletions

View File

@ -63,8 +63,6 @@ abstract class BasicAliPay
*/ */
public function __construct($options) public function __construct($options)
{ {
$this->params = new DataArray([]);
$this->config = new DataArray($options);
if (empty($options['appid'])) { if (empty($options['appid'])) {
throw new InvalidArgumentException('Missing Config -- [appid]'); throw new InvalidArgumentException('Missing Config -- [appid]');
} }
@ -81,8 +79,10 @@ abstract class BasicAliPay
throw new InvalidArgumentException('Missing Config -- [private_key]'); throw new InvalidArgumentException('Missing Config -- [private_key]');
} }
if (!empty($options['debug'])) { if (!empty($options['debug'])) {
$this->gateway = 'https://openapi.alipaydev.com/gateway.do?charset=utf-8'; $this->gateway = 'https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8';
} }
$this->params = new DataArray([]);
$this->config = new DataArray($options);
$this->options = new DataArray([ $this->options = new DataArray([
'app_id' => $this->config->get('appid'), 'app_id' => $this->config->get('appid'),
'charset' => empty($options['charset']) ? 'utf-8' : $options['charset'], 'charset' => empty($options['charset']) ? 'utf-8' : $options['charset'],
@ -108,7 +108,7 @@ abstract class BasicAliPay
$this->config->set('app_cert', file_get_contents($appCertPath)); $this->config->set('app_cert', file_get_contents($appCertPath));
} }
if (!$this->config->get('root_cert') && !empty($aliRootPath) && is_file($aliRootPath)) { if (!$this->config->get('root_cert') && !empty($aliRootPath) && is_file($aliRootPath)) {
$this->config->set('root_cert', file_get_contents($appCertPath)); $this->config->set('root_cert', file_get_contents($aliRootPath));
} }
} }
@ -197,13 +197,14 @@ abstract class BasicAliPay
*/ */
protected function verify($data, $sign) protected function verify($data, $sign)
{ {
unset($data['sign']);
if ($this->options->get('sign_type') === 'RSA2') { if ($this->options->get('sign_type') === 'RSA2') {
if (openssl_verify(json_encode($data, 256), base64_decode($sign), $this->getAliPublicKey(), OPENSSL_ALGO_SHA256) !== 1) { if (openssl_verify(json_encode($data, 256), base64_decode($sign), $this->getAliPublicKey(), OPENSSL_ALGO_SHA256) !== 1) {
throw new InvalidResponseException('Data signature verification failed.'); throw new InvalidResponseException('Data signature verification failed by RSA2.');
} }
} else { } else {
if (openssl_verify(json_encode($data, 256), base64_decode($sign), $this->getAliPublicKey(), OPENSSL_ALGO_SHA1) !== 1) { if (openssl_verify(json_encode($data, 256), base64_decode($sign), $this->getAliPublicKey(), OPENSSL_ALGO_SHA1) !== 1) {
throw new InvalidResponseException('Data signature verification failed.'); throw new InvalidResponseException('Data signature verification failed by RSA.');
} }
} }
return $data; return $data;
@ -284,9 +285,9 @@ abstract class BasicAliPay
$data[$method]['code'], $data $data[$method]['code'], $data
); );
} }
return $data[$method]; // return $data[$method];
// 去除返回结果签名检查 // 返回结果签名检查
// return $this->verify($data[$method], $data['sign']); return $this->verify($data[$method], $data['sign']);
} }
/** /**
@ -318,10 +319,17 @@ abstract class BasicAliPay
* 获取支付公钥内容 * 获取支付公钥内容
* @return string * @return string
*/ */
private function getAliPublicKey() public function getAliPublicKey()
{ {
$content = wordwrap($this->trimCert($this->config->get('public_key')), 64, "\n", true); $cert = $this->config->get('public_key');
return "-----BEGIN PUBLIC KEY-----\n{$content}\n-----END PUBLIC KEY-----"; if (strpos(trim($cert), '-----BEGIN CERTIFICATE-----') !== false) {
$pkey = openssl_pkey_get_public($cert);
$keyData = openssl_pkey_get_details($pkey);
return trim($keyData['key']);
} else {
$content = wordwrap($this->trimCert($cert), 64, "\n", true);
return "-----BEGIN PUBLIC KEY-----\n{$content}\n-----END PUBLIC KEY-----";
}
} }
/** /**
@ -347,9 +355,9 @@ abstract class BasicAliPay
for ($i = 0; $i < count($array) - 1; $i++) { for ($i = 0; $i < count($array) - 1; $i++) {
$ssl[$i] = openssl_x509_parse($array[$i] . '-----END CERTIFICATE-----', true); $ssl[$i] = openssl_x509_parse($array[$i] . '-----END CERTIFICATE-----', true);
if (strpos($ssl[$i]['serialNumber'], '0x') === 0) { if (strpos($ssl[$i]['serialNumber'], '0x') === 0) {
$ssl[$i]['serialNumber'] = $this->_hex2dec(isset($ssl[$i]['serialNumberHex']) ? $ssl[$i]['serialNumberHex'] : $ssl[$i]['serialNumber']); $ssl[$i]['serialNumber'] = $this->_hex2dec($ssl[$i]['serialNumberHex']);
} }
if ($ssl[$i]['signatureTypeLN'] == "sha1WithRSAEncryption" || $ssl[$i]['signatureTypeLN'] == "sha256WithRSAEncryption") { if ($ssl[$i]['signatureTypeLN'] == 'sha1WithRSAEncryption' || $ssl[$i]['signatureTypeLN'] == 'sha256WithRSAEncryption') {
if ($sn == null) { if ($sn == null) {
$sn = md5($this->_arr2str(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']); $sn = md5($this->_arr2str(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']);
} else { } else {
@ -394,7 +402,7 @@ abstract class BasicAliPay
$string[] = $key . '=' . $value; $string[] = $key . '=' . $value;
} }
} }
return implode(',', $string); return join(',', $string);
} }
/** /**

View File

@ -32,7 +32,13 @@ try {
'total_amount' => '1', // 支付金额 'total_amount' => '1', // 支付金额
'subject' => '支付宝订单标题', // 支付订单描述 'subject' => '支付宝订单标题', // 支付订单描述
]); ]);
echo $result; echo $result . PHP_EOL .'<br></br>'. PHP_EOL;
// 请求关闭订单
$result = $pay->close([
'out_trade_no' => strval(time())
]);
echo PHP_EOL . PHP_EOL . $result;
} catch (\Exception $e) { } catch (\Exception $e) {
echo $e->getMessage(); echo $e->getMessage();
} }

View File

@ -18,7 +18,7 @@
include "../include.php"; include "../include.php";
// 2. 准备公众号配置参数 // 2. 准备公众号配置参数
$config = include "./alipay.php"; $config = include "./alipay2.php";
try { try {
// 实例支付对象 // 实例支付对象
@ -30,10 +30,10 @@ try {
$result = $pay->apply([ $result = $pay->apply([
'out_biz_no' => time(), // 订单号 'out_biz_no' => time(), // 订单号
'payee_type' => 'ALIPAY_LOGONID', // 收款方账户类型(ALIPAY_LOGONID | ALIPAY_USERID) 'payee_type' => 'ALIPAY_LOGONID', // 收款方账户类型(ALIPAY_LOGONID | ALIPAY_USERID)
'payee_account' => 'demo@sandbox.com', // 收款方账户 'payee_account' => 'yvvfcr3065@sandbox.com', // 收款方账户
'amount' => '10', // 转账金额 'amount' => '10', // 转账金额
'payer_show_name' => '未寒', // 付款方姓名 'payer_show_name' => '未寒', // 付款方姓名
'payee_real_name' => '张三', // 收款方真实姓名 'payee_real_name' => 'yvvfcr3065', // 收款方真实姓名
'remark' => '张三', // 转账备注 'remark' => '张三', // 转账备注
]); ]);

58
_test/alipay2.php Normal file
View File

@ -0,0 +1,58 @@
<?php
// +----------------------------------------------------------------------
// | WeChatDeveloper
// +----------------------------------------------------------------------
// | 版权所有 2014~2023 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/WeChatDeveloper
// | github 代码仓库https://github.com/zoujingli/WeChatDeveloper
// +----------------------------------------------------------------------
/**
* 名词解释
* 应用私钥:用来给应用消息进行签名,请务必要妥善保管,避免遗失或泄露。
* 应用公钥:需要提供给支付宝开放平台,平台会对应用发送的消息进行签名验证。
* 支付宝公钥:应用收到支付宝发送的同步、异步消息时,使用支付宝公钥验证签名信息。
* CSR 文件CSR 即证书签名请求Certificate Signing RequestCSR 文件(.csr是申请证书时所需要的一个数据文件。
* 应用公钥证书:在开放平台上传 CSR 文件后可以获取 CA 机构颁发的应用证书文件(.crt其中包含了组织/公司名称、应用公钥、证书有效期等内容,一般有效期为 5 年。
* 支付宝公钥证书:用来验证支付宝消息,包含了支付宝公钥、支付宝公司名称、证书有效期等内容,一般有效期为 5 年。
* 支付宝根证书:用来验证支付宝消息,包含了根 CA 名称、根 CA 的公钥、证书有效期等内容。
*/
/**
* 应用公钥证书SNapp_cert_sn和支付宝根证书SNalipay_root_cert_sn sn 是指什么?
* 使用公钥证书签名方式下, 请求参数中需要携带应用公钥证书SNapp_cert_sn、支付宝根证书SNalipay_root_cert_sn这里的SN是指基于开放平台提供的计算规则动态计算出来的公钥证书序列号与X.509证书中内置的序列号serialNumber不同。
* 具体的计算规则如下解析X.509证书文件获取证书签发机构名称name以及证书内置序列号serialNumber 将name与serialNumber拼接成字符串再对该字符串做MD5计算。
* 可以参考开放平台SDK源码中的 AlipaySignature.getCertSN 方法实现
*
* 不直接使用证书文件中内置的序列号原因开放平台支持开发者上传自己找第三方权威CA签发的证书而证书文件中内置序列号只能保证同一家签发机构签发的证书不重复
*/
return [
// 沙箱模式
'debug' => true,
// 签名类型 ( RSA|RSA2 )
'sign_type' => 'RSA2',
// 应用ID
'appid' => '2021000122667306',
// 应用私钥内容 ( 需1行填写特别注意这里的应用私钥通常由支付宝密钥管理工具生成 )
'private_key' => 'MIIEowIBAAKCAQEAndH26KVe3Iy+8GxVxDuG9ZolYrqGNm8Jpdi9GrQdM81ad4pPyul2NVO+9C2Kr6a6jK6Qw1gyzcwYxtkUC7xoLZUSPpmSH7sH3sD6r2B7Mf5FsrVSa29lcm1+3UkyFgZjYTkx45lfbLmAFHOzOl0WfGkMW0Sq3N/5OMr074E4EnYtALdE3jVQCDf8bzqN3j/Kwe7f10Aglvxili2BrFM564silqcbiJ8U1zDmTdZvmEkP7ia/YVkmt5w3rh7ZBoaubtcM/rVGYXL2hQPwr/pquNCTu7Eh1RcWfpcnbuw+gOnaNyXmNFmZkeNlegXIifcunt1GK6a1pX090R8eFN3LjQIDAQABAoIBACrLY4OETCvL8n6pMbyLU7ZHfTm/UGN0So5xLh4OlxiT56MgmzBvjAE72zzFGKU2tcEuGM0Pnn8Vh+ZruLbR+QHbOV5GMExwX9r0Q0XJCL7uryGdb2L4iu6zaEJC9dTpGIulgbSwwyJtTqC9Gu2Jjm5f4dzhyt8n0KGozzAevwCqI9RaJSD96gGWLbMlHCyWKGy1OdBP4V/+agPyHAGZ9gqpfKY7y4L0My8gUxhWzQWOwihtFACjV66ULhutUYT2bro3j1k9UekKlX7IiWrssPmmmw2vfUbrKiNugF6zkfyStPt7jGJ0CdzAHWe3pyF72TyO5NU2NGcX8eKgYlY2crUCgYEAzOcg5Zot9X+Ao+fYH/Oq/3eGZd6krzByiXfcjuRco7mGODwmUnzt3PpPT1fPry9TxarTajt+A9LuxqWawfQ9eWAfrTGAbtDJB0LYo6CynDqUoRqBukROuNaLQiUqEreOQqt08o6VVblgVLv8475ij8s4z/6C2NSSjgUJHf0PL38CgYEAxS0bcXGI+WtempZ4Q3QMTUmp/+B3zuw9JzSV1gvbVi7MleI9V62V2IXHPSXL5mRhYOuQWR0MnVOhbo69fkEA8HpdSd1q2JjaeS+OiZ0ditcJISQJbqWtvmF2+XtQcbVwfID69GWGxyBEHHTW8AtAzIPc6T7x2izyzBw0lXDHSvMCgYEAi+Y61ckhG/9EC6TeMWKjG+21u5P6CQshCK7nzkAo6DhhZb/bwnI9zaSxxdCEom3D2rA5zMx1y5KXKNYlBcwGtPpmZk/oCsFOoECJvZ6YlIaCuERq0oyU2yrQxgat5T2iSe7a2El1uKPrG6+GiNCSZu8wCQMSv4zTy1ew0+LWHW0CgYAvX7ESRpcEZjmqprBqdH1oLGS9566hdr0SqF2/ucWPJVteP6dBY6F3Dl1aYbRlvIRxBuf9oS8gtbE5oO4CYZfaL2wujRZYyBDlwPlcMvWgIB4/aish/IiMD1rIgkpHp7JJF6w0ABiryyLSO3hQ4ENHX/85wzfUlawYQkaYCSq45QKBgCdqrv58KD8tDYn4JnaHJNE+5TgKK5cNhYLZLAsYz7x1KfPdkiC7y/hnenn3TWkm4xw8Tw1rJ1ZIJ24iZgTCTO7EEsB7jZegvg4z/4zVbSK2Y4VI1lJ7jlyqmwg0ArimXTNZFoy66h9c9t2smG40YEZCmmmTLEqVlWgyR1MU5iM5',
// 公钥模式,支付宝公钥内容 ( 需1行填写特别注意这里不是应用公钥而是支付宝公钥通常是上传应用公钥换取支付宝公钥在网页可以复制 )
'public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuQ70Xj4AHsIsjjuFK2EexkUVDGc6KCBzhHylt5vbgGTJFzrElV6Ri3O4nxwtKzLWykRfjOC5M9z5vt7NrmSc3WPal0B82TNt0SDVO3gDRvnFswB9pC1WHBTRqiGEsX6LpNOCiykyYHAmc3b74R1rotxytOpLt9/HwjZuGStouUQ7gLqnFRwYLKis8tW9FY5NEL9HPENREcRoaUE8zHE6l4jNi5g2Dvs4r5KqnTNvmeRTc87ZylKs4JPhSWskOaqBJmDAcTR770x0G694tKjW5sbm4a/PxWOV3eEG9XLA4CcS6gwHG1KsRu+eTPszQwkEOZCT8PxZJ6SbaUwZsUO4ZQIDAQAB',
// 证书模式,应用公钥证书路径 ( 新版资金类接口转 app_cert_sn如文件 appCertPublicKey.crt )
'app_cert_path' => __DIR__ . '/alipay/appPublicCert.crt', // 'app_cert' => '证书内容',
// 证书模式,支付宝根证书路径 ( 新版资金类接口转 alipay_root_cert_sn如文件 alipayRootCert.crt )
'alipay_root_path' => __DIR__ . '/alipay/alipayRootCert.crt', // 'root_cert' => '证书内容',
// 证书模式,支付宝公钥证书路径 ( 未填写 public_key 时启用此参数,如文件 alipayPublicCert.crt )
// 'alipay_cert_path' => __DIR__ . '/alipay/alipayPublicCert.crt', // 'public_key' => '证书内容'
// 支付成功通知地址
'notify_url' => '',
// 网页支付回跳地址
'return_url' => '',
];