From dc15ae7732a0ddae40240c82e2aa565c0629e3d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E6=99=AF=E7=AB=8B?= Date: Sat, 6 May 2023 14:49:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=94=AF=E4=BB=98=E5=AE=9D?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AliPay/Transfer.php | 7 --- WeChat/Contracts/BasicAliPay.php | 79 +++++++++++++++++---------- _test/alipay-bill.php | 13 ++++- _test/alipay.php | 32 +++++------ _test/alipay/alipayPublicCert.crt | 38 +++++++++++++ _test/alipay/alipayRootCert.crt | 88 +++++++++++++++++++++++++++++++ _test/alipay/appPublicCert.crt | 19 +++++++ readme.md | 32 +++++------ 8 files changed, 241 insertions(+), 67 deletions(-) create mode 100644 _test/alipay/alipayPublicCert.crt create mode 100644 _test/alipay/alipayRootCert.crt create mode 100644 _test/alipay/appPublicCert.crt diff --git a/AliPay/Transfer.php b/AliPay/Transfer.php index e6d93b3..7d99212 100644 --- a/AliPay/Transfer.php +++ b/AliPay/Transfer.php @@ -17,7 +17,6 @@ namespace AliPay; use WeChat\Contracts\BasicAliPay; -use WeChat\Exceptions\InvalidArgumentException; /** * 支付宝转账到账户 @@ -49,7 +48,6 @@ class Transfer extends BasicAliPay */ public function create($options = []) { - $this->setAppCertSnAndRootCertSn(); $this->options->set('method', 'alipay.fund.trans.uni.transfer'); return $this->getResult($options); } @@ -63,10 +61,8 @@ class Transfer extends BasicAliPay */ public function queryResult($options = []) { - $this->setAppCertSnAndRootCertSn(); $this->options->set('method', 'alipay.fund.trans.common.query'); return $this->getResult($options); - } /** @@ -78,10 +74,7 @@ class Transfer extends BasicAliPay */ public function queryAccount($options = []) { - $this->setAppCertSnAndRootCertSn(); $this->options->set('method', 'alipay.fund.account.query'); return $this->getResult($options); } - - } \ No newline at end of file diff --git a/WeChat/Contracts/BasicAliPay.php b/WeChat/Contracts/BasicAliPay.php index b1e0880..24c0ae9 100644 --- a/WeChat/Contracts/BasicAliPay.php +++ b/WeChat/Contracts/BasicAliPay.php @@ -66,13 +66,19 @@ abstract class BasicAliPay $this->params = new DataArray([]); $this->config = new DataArray($options); if (empty($options['appid'])) { - throw new InvalidArgumentException("Missing Config -- [appid]"); + throw new InvalidArgumentException('Missing Config -- [appid]'); + } + if (empty($options['public_key']) && !empty($options['alipay_cert_path']) && is_file($options['alipay_cert_path'])) { + $options['public_key'] = file_get_contents($options['alipay_cert_path']); + } + if (empty($options['private_key']) && !empty($options['private_key_path']) && is_file($options['private_key_path'])) { + $options['private_key'] = file_get_contents($options['private_key_path']); } if (empty($options['public_key'])) { - throw new InvalidArgumentException("Missing Config -- [public_key]"); + throw new InvalidArgumentException('Missing Config -- [public_key]'); } if (empty($options['private_key'])) { - throw new InvalidArgumentException("Missing Config -- [private_key]"); + throw new InvalidArgumentException('Missing Config -- [private_key]'); } if (!empty($options['debug'])) { $this->gateway = 'https://openapi.alipaydev.com/gateway.do?charset=utf-8'; @@ -94,6 +100,16 @@ abstract class BasicAliPay if (isset($options['app_auth_token']) && $options['app_auth_token'] !== '') { $this->options->set('app_auth_token', $options['app_auth_token']); } + + // 证书模式读取证书 + $appCertPath = $this->config->get('app_cert_path'); + $aliRootPath = $this->config->get('alipay_root_path'); + if (!$this->config->get('app_cert') && !empty($appCertPath) && is_file($appCertPath)) { + $this->config->set('app_cert', file_get_contents($appCertPath)); + } + if (!$this->config->get('root_cert') && !empty($aliRootPath) && is_file($aliRootPath)) { + $this->config->set('root_cert', file_get_contents($appCertPath)); + } } /** @@ -166,9 +182,7 @@ abstract class BasicAliPay throw new InvalidResponseException('Illegal push request.', 0, $data); } $string = $this->getSignContent($data, $needSignType); - $content = wordwrap($this->config->get('public_key'), 64, "\n", true); - $res = "-----BEGIN PUBLIC KEY-----\n{$content}\n-----END PUBLIC KEY-----"; - if (openssl_verify($string, base64_decode($data['sign']), $res, OPENSSL_ALGO_SHA256) !== 1) { + if (openssl_verify($string, base64_decode($data['sign']), $this->getAliPublicKey(), OPENSSL_ALGO_SHA256) !== 1) { throw new InvalidResponseException('Data signature verification failed.', 0, $data); } return $data; @@ -183,14 +197,12 @@ abstract class BasicAliPay */ protected function verify($data, $sign) { - $content = wordwrap($this->config->get('public_key'), 64, "\n", true); - $res = "-----BEGIN PUBLIC KEY-----\n{$content}\n-----END PUBLIC KEY-----"; if ($this->options->get('sign_type') === 'RSA2') { - if (openssl_verify(json_encode($data, 256), base64_decode($sign), $res, 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.'); } } else { - if (openssl_verify(json_encode($data, 256), base64_decode($sign), $res, 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.'); } } @@ -203,12 +215,10 @@ abstract class BasicAliPay */ protected function getSign() { - $content = wordwrap($this->trimCert($this->config->get('private_key')), 64, "\n", true); - $string = "-----BEGIN RSA PRIVATE KEY-----\n{$content}\n-----END RSA PRIVATE KEY-----"; if ($this->options->get('sign_type') === 'RSA2') { - openssl_sign($this->getSignContent($this->options->get(), true), $sign, $string, OPENSSL_ALGO_SHA256); + openssl_sign($this->getSignContent($this->options->get(), true), $sign, $this->getAppPrivateKey(), OPENSSL_ALGO_SHA256); } else { - openssl_sign($this->getSignContent($this->options->get(), true), $sign, $string, OPENSSL_ALGO_SHA1); + openssl_sign($this->getSignContent($this->options->get(), true), $sign, $this->getAppPrivateKey(), OPENSSL_ALGO_SHA1); } return base64_encode($sign); } @@ -221,7 +231,7 @@ abstract class BasicAliPay protected function trimCert($sign) { // if (file_exists($sign)) $sign = file_get_contents($sign); - return preg_replace(['/\s+/', '/\-{5}.*?\-{5}/'], '', $sign); + return preg_replace(['/\s+/', '/-{5}.*?-{5}/'], '', $sign); } /** @@ -248,6 +258,9 @@ abstract class BasicAliPay */ protected function applyData($options) { + if ($this->config->get('app_cert') && $this->config->get('root_cert')) { + $this->setAppCertSnAndRootCertSn(); + } $this->options->set('biz_content', json_encode($this->params->merge($options), 256)); $this->options->set('sign', $this->getSign()); } @@ -292,6 +305,26 @@ abstract class BasicAliPay return "{$html}"; } + /** + * 获取应用私钥内容 + * @return string + */ + private function getAppPrivateKey() + { + $content = wordwrap($this->trimCert($this->config->get('private_key')), 64, "\n", true); + return "-----BEGIN RSA PRIVATE KEY-----\n{$content}\n-----END RSA PRIVATE KEY-----"; + } + + /** + * 获取支付公钥内容 + * @return string + */ + private function getAliPublicKey() + { + $content = wordwrap($this->config->get('public_key'), 64, "\n", true); + return "-----BEGIN PUBLIC KEY-----\n{$content}\n-----END PUBLIC KEY-----"; + } + /** * 新版 从证书中提取序列号 * @param string $sign @@ -333,21 +366,11 @@ abstract class BasicAliPay */ protected function setAppCertSnAndRootCertSn() { - $appCert = $this->config->get('app_cert'); - $rootCert = $this->config->get('root_cert'); - $appCertPath = $this->config->get('app_cert_path'); - $rootCertPath = $this->config->get('root_cert_path'); - if (empty($appCert) && !empty($appCertPath) && is_file($appCertPath)) { - $appCert = file_get_contents($appCertPath); - } - if (empty($rootCert) && !empty($rootCertPath) && is_file($rootCertPath)) { - $rootCert = file_get_contents($rootCertPath); - } - if (empty($appCert)) { + if (!($appCert = $this->config->get('app_cert'))) { throw new InvalidArgumentException('Missing Config -- [app_cert|app_cert_path]'); } - if (empty($rootCert)) { - throw new InvalidArgumentException('Missing Config -- [root_cert|root_cert_path]'); + if (!($rootCert = $this->config->get('root_cert'))) { + throw new InvalidArgumentException('Missing Config -- [root_cert|alipay_root_path]'); } $this->options->set('app_cert_sn', $this->getAppCertSN($appCert)); $this->options->set('alipay_root_cert_sn', $this->getRootCertSN($rootCert)); diff --git a/_test/alipay-bill.php b/_test/alipay-bill.php index 569577d..aaacf04 100644 --- a/_test/alipay-bill.php +++ b/_test/alipay-bill.php @@ -26,10 +26,19 @@ try { // $pay = \We::AliPayBill($config); $pay = \AliPay\Bill::instance($config); + /** + * 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型,支持: + * trade:商户基于支付宝交易收单的业务账单; + * signcustomer:基于商户支付宝余额收入及支出等资金变动的账务账单; + * merchant_act:营销活动账单,包含营销活动的发放,核销记录 + * trade_zft_merchant:直付通二级商户查询交易的业务账单; + * zft_acc:直付通平台商查询二级商户流水使用,返回所有二级商户流水。 + */ + // 请参考(请求参数):https://docs.open.alipay.com/api_15/alipay.data.dataservice.bill.downloadurl.query $result = $pay->apply([ - 'bill_date' => '2020-07-03', // 账单时间(日账单yyyy-MM-dd,月账单 yyyy-MM) - 'bill_type' => 'signcustomer', // 账单类型(trade指商户基于支付宝交易收单的业务账单,signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单) + 'bill_date' => date('Y-m-d', strtotime('-1 month')), // 账单时间(日账单yyyy-MM-dd,月账单 yyyy-MM) + 'bill_type' => 'trade', ]); echo '
';
     var_export($result);
diff --git a/_test/alipay.php b/_test/alipay.php
index 4c92920..9622ecb 100644
--- a/_test/alipay.php
+++ b/_test/alipay.php
@@ -35,22 +35,24 @@
  */
 
 return [
-    // 沙箱模式
-    'debug'       => true,
-    // 签名类型(RSA|RSA2)
-    'sign_type'   => 'RSA2',
+// 沙箱模式
+    'debug'            => true,
+    // 签名类型 ( RSA|RSA2 )
+    'sign_type'        => 'RSA2',
     // 应用ID
-    'appid'       => '2016090900468879',
-    // 支付宝公钥内容 (需1行填写,特别注意:这里不是应用公钥而是支付宝公钥,通常是上传应用公钥换取支付宝公钥,在网页可以复制)
-    'public_key'  => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtU71NY53UDGY7JNvLYAhsNa+taTF6KthIHJmGgdio9bkqeJGhHk6ttkTKkLqFgwIfgAkHpdKiOv1uZw6gVGZ7TCu5LfHTqKrCd6Uz+N7hxhY+4IwicLgprcV1flXQLmbkJYzFMZqkXGkSgOsR2yXh4LyQZczgk9N456uuzGtRy7MoB4zQy34PLUkkxR6W1B2ftNbLRGXv6tc7p/cmDcrY6K1bSxnGmfRxFSb8lRfhe0V0UM6pKq2SGGSeovrKHN0OLp+Nn5wcULVnFgATXGCENshRlp96piPEBFwneXs19n+sX1jx60FTR7/rME3sW3AHug0fhZ9mSqW4x401WjdnwIDAQAB',
-    // 应用私钥的内容 (需1行填写,特别注意:这里的应用私钥通常由支付宝密钥管理工具生成)
-    'private_key' => 'MIIEpAIBAAKCAQEArr8ymPBh/lQ46y76f3VZW4HGV8tClTHJhhlt7BPmn0F8fSc3alKFCiHXIT01ccy3xTPVgcYX26/vvQBt9cJZqxEWapj4hb8BH6lr5BH5MMQ88eyyPLlYicGzt5S4iXUjuImsUguaCO12SoFmM1eaMYKqrELkg3Apbc+16Ktq+puKAohZeozd80NnDc1ossrZtHU1DkUkEJrBeaC7/D+0H/HBHJzc60EMYpvSGgyKzP/ke6xF4ZGilFSKsdoKgS8u7paVMh2SlSO5AsMoELTTlsFS4eYOgD6ZThkBTZZIcyKHM1Wjq6qDfhU3oTkJDYqnLWdtzoqgnUPNQ8nYpYrdeQIDAQABAoIBAQCKWRmH+Bi9MJT3rfPo4VFjnzUW4PfQAuDX6F4coAzgXQpgU6IN7VMjGHOn/zvG4xtDZ6xL2DefWIVnj2V/QuWXCCpFLuLjkLslBA9FO+2b7GGL76eVZ/Bu8AqG95m6SiGDwovJUSIcm1Qh3Jy7XUnYlOjnBPbCERTbuaz9jmleCmOmh4RnpL0DmzvUffNmEuqJfgnF0h2h6CwYBUyGY8G3zpGAyqY+viVPjKA8catcQTqDRywe3ktXxIbi0JqTAYDpb81Ih4NQeHxGMUWHabwij9UTYdPYBgzwVxjcJF4O1cPGdnYeRXm8Neq6L2HbEPRqe/Jiw4i4AB4V3cVWExPhAoGBANWyjP43kguThkFsQZPfDP5xuXIQJ5W24ZcDIwcFYZ5glh+hddHuWsrCf68VHXsf2epeNdqz57xbdtg84FI8N0HmjTCtfuagxXUMPbX4NqqzPqOu2m0T5/b8HCEDV3zWqfMOt+kHR4NytFxLKLwR0a9kypsfZ2z/X4BYXBi6PiZFAoGBANFWw+AL9hJ2q51ePTcZrE788hPepcsdv3OfJjnspykKighTiFgT+m+Kr/AEa9IU/njz/+tcWJuAwOPKDFVd0zVRcxK2Y5WLfo8fYEt6yhq8oa5OKFwInXXVmCPMn0+qduNmZMTzSqL2kaN0U+AH5Te1vlv7I7yuwHDE9vwknBelAoGAbpPN0V3//G2B8yiJZnLszl0akKM7WIUhhnrhDSkDsmhYRlXOGas03+Z1G6vZbXS11kiZpWmiaB0MCii2CteN4FPki2O7XquigUasSBUAdKP7rcc0z2yVg4BBLfQEuVx65IKhN7vEjYg1O+zIT0kJL7EABfTiF8ytJkSSo1j7/+ECgYEA0NPuKHWmLvsE7cKR7IKG2nEIiHvGBl6RmyS7PHNwucdStUWnML4VSOof4p52dKcOx9gYh1Ci79U8FsB7Fzm2tWygD520r/zs7peNNx6xuIROAZTkPBM4CNFfqO66Sf2yBd0iTzqoTPMNi/JCra0So0WBNT7Ngq8NODG0dQmMUSUCgYANC6Y4JIq24nh27jZjTlsVA+jkv5tFK2aKnfRwOYcNhjVNaMyMz5BbvE07zlr/CgViauEMEJorTs+eNY1TEW7ZOOr2lFgbUnT+h/GLCvgwV00nYBEimfIJ96jfIotOLYB74oJJni9wCaJCuurLNfJ0E1XS/8d+1BkFQ+Q7VSDYpg==',
-    // 应用公钥的内容(新版资金类接口转 app_cert_sn,如文件 appCertPublicKey_2019051064521003.crt)
-    'app_cert'    => '', // 'app_cert_path' => '',
-    // 支付宝根证书的内容(新版资金类接口转 alipay_root_cert_sn,如文件 alipayRootCert.crt)
-    'root_cert'   => '', // 'root_cert_path' => ''
+    'appid'            => '2021000122667306',
+    // 应用私钥内容 ( 需1行填写,特别注意:这里的应用私钥通常由支付宝密钥管理工具生成 )
+    'private_key'      => 'MIIEowIBAAKCAQE...',
+    // 公钥模式,支付宝公钥内容 ( 需1行填写,特别注意:这里不是应用公钥而是支付宝公钥,通常是上传应用公钥换取支付宝公钥,在网页可以复制 )
+    'public_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',
+    // 证书模式,应用公钥证书 ( 新版资金类接口转 app_cert_sn,如文件 appCertPublicKey_2019051064521003.crt )
+    'app_cert_path'    => __DIR__ . '/alipay/appPublicCert.crt', // 'app_cert' => '证书内容',
+    // 证书模式,支付宝公钥证书 ( 未填写 public_key 时启用此参数,如文件 alipayPublicCert.crt )
+    'alipay_cert_path' => __DIR__ . '/alipay/alipayPublicCert.crt', // 'public_key' => '证书内容'
+    // 证书模式,支付宝根证书路径 ( 新版资金类接口转 alipay_root_cert_sn,如文件 alipayRootCert.crt )
+    'alipay_root_path' => __DIR__ . '/alipay/alipayRootCert.crt', // 'root_cert' => '证书内容',
     // 支付成功通知地址
-    'notify_url'  => '',
+    'notify_url'       => '',
     // 网页支付回跳地址
-    'return_url'  => '',
+    'return_url'       => '',
 ];
\ No newline at end of file
diff --git a/_test/alipay/alipayPublicCert.crt b/_test/alipay/alipayPublicCert.crt
new file mode 100644
index 0000000..0d7d612
--- /dev/null
+++ b/_test/alipay/alipayPublicCert.crt
@@ -0,0 +1,38 @@
+-----BEGIN CERTIFICATE-----
+MIIDszCCApugAwIBAgIQICMFBicVvB1uC0Lc3VFbITANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UE
+BhMCQ04xGzAZBgNVBAoMEkFudCBGaW5hbmNpYWwgdGVzdDElMCMGA1UECwwcQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkgdGVzdDE+MDwGA1UEAww1QW50IEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eSBDbGFzcyAyIFIxIHRlc3QwHhcNMjMwNTA2MDYwMTA3WhcNMjQwNTA1MDYwMTA3WjCB
+hDELMAkGA1UEBhMCQ04xHzAdBgNVBAoMFndtaWFrdTY3NDlAc2FuZGJveC5jb20xDzANBgNVBAsM
+BkFsaXBheTFDMEEGA1UEAww65pSv5LuY5a6dKOS4reWbvSnnvZHnu5zmioDmnK/mnInpmZDlhazl
+j7gtMjA4ODcyMTAwMjg0MzQxNDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALkO9F4+
+AB7CLI47hSthHsZFFQxnOiggc4R8pbeb24BkyRc6xJVekYtzuJ8cLSsy1spEX4zguTPc+b7eza5k
+nN1j2pdAfNkzbdEg1Tt4A0b5xbMAfaQtVhwU0aohhLF+i6TTgospMmBwJnN2++Eda6LccrTqS7ff
+x8I2bhkraLlEO4C6pxUcGCyorPLVvRWOTRC/RzxDURHEaGlBPMxxOpeIzYuYNg77OK+Sqp0zb5nk
+U3PO2cpSrOCT4UlrJDmqgSZgwHE0e+9MdBuveLSo1ubG5uGvz8Vjld3hBvVywOAnEuoMBxtSrEbv
+nkz7M0MJBDmQk/D8WSekm2lMGbFDuGUCAwEAAaMSMBAwDgYDVR0PAQH/BAQDAgTwMA0GCSqGSIb3
+DQEBCwUAA4IBAQC29uyA9W6uQivhMqc3YyGXPvi4LIOThU1ijeOSpovHiRUGVfaO/qIY4eAQ2ivF
+iKIrUEqcJnWdNN8LZwWdWd6UmInyngq2i+Pf4h3a2MLkV3ZufZNZkJP5GZJxbcjkKlnKuCTKFUd5
+wJ0zo369E+mPdNGlrPLvNXw4ziUpUb4KXmEn1yOVAkQMsBP43K6QB2QVIODrtp4O+rEs80KHgUQh
+cmla+PWGyX2nuwHURxUtEeIeJblra+ntyy+bTYfCVVG8jh4BN5bPExDprRa100aZGsqyExO1xtxk
+4pu0Jag5XRvGyZpmqH27SfQX2oKcsobuDyGCwff/yvYKOZk53JZD
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDszCCApugAwIBAgIQIBkIGbgVxq210KxLJ+YA/TANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UE
+BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxJTAjBgNVBAsMHENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IHRlc3QxNjA0BgNVBAMMLUFudCBGaW5hbmNpYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkgUjEgdGVzdDAeFw0xOTA4MTkxMTE2MDBaFw0yNDA4MDExMTE2MDBaMIGRMQswCQYDVQQGEwJD
+TjEbMBkGA1UECgwSQW50IEZpbmFuY2lhbCB0ZXN0MSUwIwYDVQQLDBxDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eSB0ZXN0MT4wPAYDVQQDDDVBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5IENsYXNzIDIgUjEgdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMh4FKYO
+ZyRQHD6eFbPKZeSAnrfjfU7xmS9Yoozuu+iuqZlb6Z0SPLUqqTZAFZejOcmr07ln/pwZxluqplxC
+5+B48End4nclDMlT5HPrDr3W0frs6Xsa2ZNcyil/iKNB5MbGll8LRAxntsKvZZj6vUTMb705gYgm
+VUMILwi/ZxKTQqBtkT/kQQ5y6nOZsj7XI5rYdz6qqOROrpvS/d7iypdHOMIM9Iz9DlL1mrCykbBi
+t25y+gTeXmuisHUwqaRpwtCGK4BayCqxRGbNipe6W73EK9lBrrzNtTr9NaysesT/v+l25JHCL9tG
+wpNr1oWFzk4IHVOg0ORiQ6SUgxZUTYcCAwEAAaMSMBAwDgYDVR0PAQH/BAQDAgTwMA0GCSqGSIb3
+DQEBCwUAA4IBAQBWThEoIaQoBX2YeRY/I8gu6TYnFXtyuCljANnXnM38ft+ikhE5mMNgKmJYLHvT
+yWWWgwHoSAWEuml7EGbE/2AK2h3k0MdfiWLzdmpPCRG/RJHk6UB1pMHPilI+c0MVu16OPpKbg5Vf
+LTv7dsAB40AzKsvyYw88/Ezi1osTXo6QQwda7uefvudirtb8FcQM9R66cJxl3kt1FXbpYwheIm/p
+j1mq64swCoIYu4NrsUYtn6CV542DTQMI5QdXkn+PzUUly8F6kDp+KpMNd0avfWNL5+O++z+F5Szy
+1CPta1D7EQ/eYmMP+mOQ35oifWIoFCpN6qQVBS/Hob1J/UUyg7BW
+-----END CERTIFICATE-----
diff --git a/_test/alipay/alipayRootCert.crt b/_test/alipay/alipayRootCert.crt
new file mode 100644
index 0000000..76417c5
--- /dev/null
+++ b/_test/alipay/alipayRootCert.crt
@@ -0,0 +1,88 @@
+-----BEGIN CERTIFICATE-----
+MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG
+EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw
+MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO
+UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE
+MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT
+V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti
+W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ
+MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b
+53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI
+pDoiVhsLwg==
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIF0zCCA7ugAwIBAgIIH8+hjWpIDREwDQYJKoZIhvcNAQELBQAwejELMAkGA1UE
+BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMyMTEzNDg0MFoXDTM4MDIyODEzNDg0
+MFowejELMAkGA1UEBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNV
+BAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5j
+aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEAtytTRcBNuur5h8xuxnlKJetT65cHGemGi8oD+beHFPTk
+rUTlFt9Xn7fAVGo6QSsPb9uGLpUFGEdGmbsQ2q9cV4P89qkH04VzIPwT7AywJdt2
+xAvMs+MgHFJzOYfL1QkdOOVO7NwKxH8IvlQgFabWomWk2Ei9WfUyxFjVO1LVh0Bp
+dRBeWLMkdudx0tl3+21t1apnReFNQ5nfX29xeSxIhesaMHDZFViO/DXDNW2BcTs6
+vSWKyJ4YIIIzStumD8K1xMsoaZBMDxg4itjWFaKRgNuPiIn4kjDY3kC66Sl/6yTl
+YUz8AybbEsICZzssdZh7jcNb1VRfk79lgAprm/Ktl+mgrU1gaMGP1OE25JCbqli1
+Pbw/BpPynyP9+XulE+2mxFwTYhKAwpDIDKuYsFUXuo8t261pCovI1CXFzAQM2w7H
+DtA2nOXSW6q0jGDJ5+WauH+K8ZSvA6x4sFo4u0KNCx0ROTBpLif6GTngqo3sj+98
+SZiMNLFMQoQkjkdN5Q5g9N6CFZPVZ6QpO0JcIc7S1le/g9z5iBKnifrKxy0TQjtG
+PsDwc8ubPnRm/F82RReCoyNyx63indpgFfhN7+KxUIQ9cOwwTvemmor0A+ZQamRe
+9LMuiEfEaWUDK+6O0Gl8lO571uI5onYdN1VIgOmwFbe+D8TcuzVjIZ/zvHrAGUcC
+AwEAAaNdMFswCwYDVR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFF90
+tATATwda6uWx2yKjh0GynOEBMB8GA1UdIwQYMBaAFF90tATATwda6uWx2yKjh0Gy
+nOEBMA0GCSqGSIb3DQEBCwUAA4ICAQCVYaOtqOLIpsrEikE5lb+UARNSFJg6tpkf
+tJ2U8QF/DejemEHx5IClQu6ajxjtu0Aie4/3UnIXop8nH/Q57l+Wyt9T7N2WPiNq
+JSlYKYbJpPF8LXbuKYG3BTFTdOVFIeRe2NUyYh/xs6bXGr4WKTXb3qBmzR02FSy3
+IODQw5Q6zpXj8prYqFHYsOvGCEc1CwJaSaYwRhTkFedJUxiyhyB5GQwoFfExCVHW
+05ZFCAVYFldCJvUzfzrWubN6wX0DD2dwultgmldOn/W/n8at52mpPNvIdbZb2F41
+T0YZeoWnCJrYXjq/32oc1cmifIHqySnyMnavi75DxPCdZsCOpSAT4j4lAQRGsfgI
+kkLPGQieMfNNkMCKh7qjwdXAVtdqhf0RVtFILH3OyEodlk1HYXqX5iE5wlaKzDop
+PKwf2Q3BErq1xChYGGVS+dEvyXc/2nIBlt7uLWKp4XFjqekKbaGaLJdjYP5b2s7N
+1dM0MXQ/f8XoXKBkJNzEiM3hfsU6DOREgMc1DIsFKxfuMwX3EkVQM1If8ghb6x5Y
+jXayv+NLbidOSzk4vl5QwngO/JYFMkoc6i9LNwEaEtR9PhnrdubxmrtM+RjfBm02
+77q3dSWFESFQ4QxYWew4pHE0DpWbWy/iMIKQ6UZ5RLvB8GEcgt8ON7BBJeMc+Dyi
+kT9qhqn+lw==
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIICiDCCAgygAwIBAgIIQX76UsB/30owDAYIKoZIzj0EAwMFADB6MQswCQYDVQQG
+EwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UECwwXQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNpYWwgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkgRTEwHhcNMTkwNDI4MTYyMDQ0WhcNNDkwNDIwMTYyMDQ0
+WjB6MQswCQYDVQQGEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UE
+CwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNp
+YWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRTEwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAASCCRa94QI0vR5Up9Yr9HEupz6hSoyjySYqo7v837KnmjveUIUNiuC9pWAU
+WP3jwLX3HkzeiNdeg22a0IZPoSUCpasufiLAnfXh6NInLiWBrjLJXDSGaY7vaokt
+rpZvAdmjXTBbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBRZ
+4ZTgDpksHL2qcpkFkxD2zVd16TAfBgNVHSMEGDAWgBRZ4ZTgDpksHL2qcpkFkxD2
+zVd16TAMBggqhkjOPQQDAwUAA2gAMGUCMQD4IoqT2hTUn0jt7oXLdMJ8q4vLp6sg
+wHfPiOr9gxreb+e6Oidwd2LDnC4OUqCWiF8CMAzwKs4SnDJYcMLf2vpkbuVE4dTH
+Rglz+HGcTLWsFs4KxLsq7MuU+vJTBUeDJeDjdA==
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIUEMdk6dVgOEIS2cCP0Q43P90Ps5YwDQYJKoZIhvcNAQEF
+BQAwajELMAkGA1UEBhMCQ04xEzARBgNVBAoMCmlUcnVzQ2hpbmExHDAaBgNVBAsM
+E0NoaW5hIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMMH2lUcnVzQ2hpbmEgQ2xhc3Mg
+MiBSb290IENBIC0gRzMwHhcNMTMwNDE4MDkzNjU2WhcNMzMwNDE4MDkzNjU2WjBq
+MQswCQYDVQQGEwJDTjETMBEGA1UECgwKaVRydXNDaGluYTEcMBoGA1UECwwTQ2hp
+bmEgVHJ1c3QgTmV0d29yazEoMCYGA1UEAwwfaVRydXNDaGluYSBDbGFzcyAyIFJv
+b3QgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOPPShpV
+nJbMqqCw6Bz1kehnoPst9pkr0V9idOwU2oyS47/HjJXk9Rd5a9xfwkPO88trUpz5
+4GmmwspDXjVFu9L0eFaRuH3KMha1Ak01citbF7cQLJlS7XI+tpkTGHEY5pt3EsQg
+wykfZl/A1jrnSkspMS997r2Gim54cwz+mTMgDRhZsKK/lbOeBPpWtcFizjXYCqhw
+WktvQfZBYi6o4sHCshnOswi4yV1p+LuFcQ2ciYdWvULh1eZhLxHbGXyznYHi0dGN
+z+I9H8aXxqAQfHVhbdHNzi77hCxFjOy+hHrGsyzjrd2swVQ2iUWP8BfEQqGLqM1g
+KgWKYfcTGdbPB1MCAwEAAaNjMGEwHQYDVR0OBBYEFG/oAMxTVe7y0+408CTAK8hA
+uTyRMB8GA1UdIwQYMBaAFG/oAMxTVe7y0+408CTAK8hAuTyRMA8GA1UdEwEB/wQF
+MAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBLnUTfW7hp
+emMbuUGCk7RBswzOT83bDM6824EkUnf+X0iKS95SUNGeeSWK2o/3ALJo5hi7GZr3
+U8eLaWAcYizfO99UXMRBPw5PRR+gXGEronGUugLpxsjuynoLQu8GQAeysSXKbN1I
+UugDo9u8igJORYA+5ms0s5sCUySqbQ2R5z/GoceyI9LdxIVa1RjVX8pYOj8JFwtn
+DJN3ftSFvNMYwRuILKuqUYSHc2GPYiHVflDh5nDymCMOQFcFG3WsEuB+EYQPFgIU
+1DHmdZcz7Llx8UOZXX2JupWCYzK1XhJb+r4hK5ncf/w8qGtYlmyJpxk3hr1TfUJX
+Yf4Zr0fJsGuv
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/_test/alipay/appPublicCert.crt b/_test/alipay/appPublicCert.crt
new file mode 100644
index 0000000..586f341
--- /dev/null
+++ b/_test/alipay/appPublicCert.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDmTCCAoGgAwIBAgIQICMFBmpzKUKQGs/81YT4UDANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UE
+BhMCQ04xGzAZBgNVBAoMEkFudCBGaW5hbmNpYWwgdGVzdDElMCMGA1UECwwcQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkgdGVzdDE+MDwGA1UEAww1QW50IEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eSBDbGFzcyAyIFIxIHRlc3QwHhcNMjMwNTA2MDYwMTA3WhcNMjQwNTEwMDYwMTA3WjBr
+MQswCQYDVQQGEwJDTjEfMB0GA1UECgwWd21pYWt1Njc0OUBzYW5kYm94LmNvbTEPMA0GA1UECwwG
+QWxpcGF5MSowKAYDVQQDDCEyMDg4NzIxMDAyODQzNDE0LTIwMjEwMDAxMjI2NjczMDYwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCd0fbopV7cjL7wbFXEO4b1miViuoY2bwml2L0atB0z
+zVp3ik/K6XY1U770LYqvprqMrpDDWDLNzBjG2RQLvGgtlRI+mZIfuwfewPqvYHsx/kWytVJrb2Vy
+bX7dSTIWBmNhOTHjmV9suYAUc7M6XRZ8aQxbRKrc3/k4yvTvgTgSdi0At0TeNVAIN/xvOo3eP8rB
+7t/XQCCW/GKWLYGsUznriyKWpxuInxTXMOZN1m+YSQ/uJr9hWSa3nDeuHtkGhq5u1wz+tUZhcvaF
+A/Cv+mq40JO7sSHVFxZ+lydu7D6A6do3JeY0WZmR42V6BciJ9y6e3UYrprWlfT3RHx4U3cuNAgMB
+AAGjEjAQMA4GA1UdDwEB/wQEAwIE8DANBgkqhkiG9w0BAQsFAAOCAQEAiLA1wk7or0uqxPSD9z31
+BLJRnamf51Uz2YOSDPjUivu9VJrkjf1PsCleK9RSgOcXcpy9QWZSFIIyakFqCCWylgBdjmhSvzAv
+po86ycJXEBrd7klOM/6VFh68BqpmK1CJl0g29JOJ1fNvIKYJ/WS4ue988NSGrpsVhXbNGALhgiBL
+Gqs7KkZQSsIAySx2HNCSknFM5G5xG6IR6wUJ619khba6rMaKOJ3D7mxuv0Vjyu7DrFBsPLOSdjP1
+Jzi5WmutetJGVrbOKayVjL4xDcU7lgOWTrhTs7S54Z6GnxqRLwy8gD7RmDbXtnVyGP/mUcAgnFtE
+BPFZBe7qjqsCCkf+Zw==
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/readme.md b/readme.md
index f4bc72d..697baf7 100644
--- a/readme.md
+++ b/readme.md
@@ -199,23 +199,25 @@ try {
 ```php
 $config = [
     // 沙箱模式
-    'debug'        => true,
-    // 签名类型(RSA|RSA2)
-    'sign_type'    => "RSA2",
-    // 应用APPID
-    'appid'        => '2016090900468879',
-    // 支付宝公钥内容 (需1行填写,特别注意:这里不是应用公钥而是支付宝公钥,通常是上传应用公钥换取支付宝公钥,在网页可以复制)
-    'public_key'   => 'MIIBIjAN...',
-    // 应用私钥的内容 (需1行填写,特别注意:这里的应用私钥通常由支付宝密钥管理工具生成)
-    'private_key'  => 'MIIEpAIB...',
-    // 应用公钥的内容(新版资金类接口转 app_cert_sn,如文件 appCertPublicKey_2019051064521003.crt)
-    'app_cert'    => '', // 'app_cert_path' => '',
-    // 支付宝根证书的内容(新版资金类接口转 alipay_root_cert_sn,如文件 alipayRootCert.crt)
-    'root_cert'   => '', // 'root_cert_path' => ''
+    'debug'            => true,
+    // 签名类型 ( RSA|RSA2 )
+    'sign_type'        => 'RSA2',
+    // 应用ID
+    'appid'            => '2021000122667306',
+    // 应用私钥内容 ( 需1行填写,特别注意:这里的应用私钥通常由支付宝密钥管理工具生成 )
+    'private_key'      => 'MIIEowIBAAKCAQE...',
+    // 公钥模式,支付宝公钥内容 ( 需1行填写,特别注意:这里不是应用公钥而是支付宝公钥,通常是上传应用公钥换取支付宝公钥,在网页可以复制 )
+    'public_key'       => 'MIIBIjANBgkqhki...',
+    // 证书模式,应用公钥证书 ( 新版资金类接口转 app_cert_sn,如文件 appCertPublicKey_2019051064521003.crt )
+    'app_cert_path'    => __DIR__ . '/alipay/appPublicCert.crt', // 'app_cert' => '证书内容',
+    // 证书模式,支付宝公钥证书 ( 未填写 public_key 时启用此参数,如文件 alipayPublicCert.crt )
+    'alipay_cert_path' => __DIR__ . '/alipay/alipayPublicCert.crt', // 'public_key' => '证书内容'
+    // 证书模式,支付宝根证书路径 ( 新版资金类接口转 alipay_root_cert_sn,如文件 alipayRootCert.crt )
+    'alipay_root_path' => __DIR__ . '/alipay/alipayRootCert.crt', // 'root_cert' => '证书内容',
     // 支付成功通知地址
-    'notify_url'  => '',
+    'notify_url'       => '',
     // 网页支付回跳地址
-    'return_url'  => '',
+    'return_url'       => '',
 ];
 ```