From 0595f9bcb2ca082b0b3517315d94a3c5a66fff8e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=82=B9=E6=99=AF=E7=AB=8B?= <zoujingli@qq.com>
Date: Tue, 2 May 2023 22:54:26 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 AliPay/Transfer.php              | 21 +---------------
 WeChat/Contracts/BasicAliPay.php | 41 +++++++++++++++++++++++++-------
 _test/alipay-app.php             |  2 +-
 readme.md                        | 35 ++++++++++++++++-----------
 4 files changed, 55 insertions(+), 44 deletions(-)

diff --git a/AliPay/Transfer.php b/AliPay/Transfer.php
index 6b30c7d..e6d93b3 100644
--- a/AliPay/Transfer.php
+++ b/AliPay/Transfer.php
@@ -83,24 +83,5 @@ class Transfer extends BasicAliPay
         return $this->getResult($options);
     }
 
-    /**
-     * 新版 设置网关应用公钥证书SN、支付宝根证书SN
-     */
-    protected function setAppCertSnAndRootCertSn()
-    {
-        if (!$this->config->get('app_cert')) {
-            throw new InvalidArgumentException("Missing Config -- [app_cert]");
-        }
-        if (!$this->config->get('root_cert')) {
-            throw new InvalidArgumentException("Missing Config -- [root_cert]");
-        }
-        $this->options->set('app_cert_sn', $this->getCertSN($this->config->get('app_cert')));
-        $this->options->set('alipay_root_cert_sn', $this->getRootCertSN($this->config->get('root_cert')));
-        if (!$this->options->get('app_cert_sn')) {
-            throw new InvalidArgumentException("Missing options -- [app_cert_sn]");
-        }
-        if (!$this->options->get('alipay_root_cert_sn')) {
-            throw new InvalidArgumentException("Missing options -- [alipay_root_cert_sn]");
-        }
-    }
+
 }
\ No newline at end of file
diff --git a/WeChat/Contracts/BasicAliPay.php b/WeChat/Contracts/BasicAliPay.php
index 9613e1e..fc3da04 100644
--- a/WeChat/Contracts/BasicAliPay.php
+++ b/WeChat/Contracts/BasicAliPay.php
@@ -297,10 +297,10 @@ abstract class BasicAliPay
      * @param string $sign
      * @return string
      */
-    public function getCertSN($sign)
+    private function getCertSN($sign)
     {
         // if (file_exists($sign)) $sign = file_get_contents($sign);
-        $ssl = openssl_x509_parse($sign);
+        $ssl = openssl_x509_parse($sign, true);
         return md5($this->_arr2str(array_reverse($ssl['issuer'])) . $ssl['serialNumber']);
     }
 
@@ -309,27 +309,51 @@ abstract class BasicAliPay
      * @param string $sign
      * @return string|null
      */
-    public function getRootCertSN($sign)
+    private function getRootCertSN($sign)
     {
         $sn = null;
-        // if (file_exists($sign)) $sign = file_get_contents($sign);
-        $array = explode("-----END CERTIFICATE-----", $sign);
+        $array = explode('-----END CERTIFICATE-----', $sign);
         for ($i = 0; $i < count($array) - 1; $i++) {
-            $ssl[$i] = openssl_x509_parse($array[$i] . "-----END CERTIFICATE-----");
+            $ssl[$i] = openssl_x509_parse($array[$i] . '-----END CERTIFICATE-----', true);
             if (strpos($ssl[$i]['serialNumber'], '0x') === 0) {
-                $ssl[$i]['serialNumber'] = $this->_hex2dec($ssl[$i]['serialNumber']);
+                $ssl[$i]['serialNumber'] = $this->_hex2dec(isset($ssl[$i]['serialNumberHex']) ? $ssl[$i]['serialNumberHex'] : $ssl[$i]['serialNumber']);
             }
             if ($ssl[$i]['signatureTypeLN'] == "sha1WithRSAEncryption" || $ssl[$i]['signatureTypeLN'] == "sha256WithRSAEncryption") {
                 if ($sn == null) {
                     $sn = md5($this->_arr2str(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']);
                 } else {
-                    $sn = $sn . "_" . md5($this->_arr2str(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']);
+                    $sn = $sn . '_' . md5($this->_arr2str(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']);
                 }
             }
         }
         return $sn;
     }
 
+    /**
+     * 新版 设置网关应用公钥证书SN、支付宝根证书SN
+     */
+    protected function setAppCertSnAndRootCertSn()
+    {
+        if (!$this->config->get('app_cert') && !$this->config->get('app_cert_sn')) {
+            throw new InvalidArgumentException("Missing Config -- [app_cert|app_cert_sn]");
+        }
+        if (!$this->config->get('root_cert') && !$this->config->get('root_cert_sn')) {
+            throw new InvalidArgumentException("Missing Config -- [root_cert|root_cert_sn]");
+        }
+        $appCertSn = $this->config->get('app_cert_sn');
+        $rootCertSn = $this->config->get('root_cert_sn');
+        if (empty($appCertSn)) $appCertSn = $this->getCertSN($this->config->get('app_cert'));
+        if (empty($rootCertSn)) $rootCertSn = $this->getRootCertSN($this->config->get('root_cert'));
+        $this->options->set('app_cert_sn', $appCertSn);
+        $this->options->set('alipay_root_cert_sn', $rootCertSn);
+        if (empty($appCertSn)) {
+            throw new InvalidArgumentException("Missing options -- [app_cert_sn]");
+        }
+        if (empty($rootCertSn)) {
+            throw new InvalidArgumentException("Missing options -- [alipay_root_cert_sn]");
+        }
+    }
+
     /**
      * 新版 数组转字符串
      * @param array $array
@@ -367,5 +391,4 @@ abstract class BasicAliPay
      * @return mixed
      */
     abstract public function apply($options);
-
 }
\ No newline at end of file
diff --git a/_test/alipay-app.php b/_test/alipay-app.php
index 54bcfca..5ecbdd8 100644
--- a/_test/alipay-app.php
+++ b/_test/alipay-app.php
@@ -28,7 +28,7 @@ try {
 
     // 请参考(请求参数):https://docs.open.alipay.com/api_1/alipay.trade.app.pay
     $result = $pay->apply([
-        'out_trade_no' => time(), // 商户订单号
+        'out_trade_no' => strval(time()), // 商户订单号
         'total_amount' => '1', // 支付金额
         'subject'      => '支付宝订单标题', // 支付订单描述
     ]);
diff --git a/readme.md b/readme.md
index 48216ff..d6994cc 100644
--- a/readme.md
+++ b/readme.md
@@ -199,23 +199,30 @@ try {
 ```php
 $config = [
     // 沙箱模式
-    'debug'       => true,
+    'debug'        => true,
     // 签名类型(RSA|RSA2)
-    'sign_type'   => "RSA2",
-    // 应用ID
-    'appid'       => '2016090900468879',
-    // 支付宝公钥文字内容 (1行填写,特别注意:这里是支付宝公钥,不是应用公钥,最好从开发者中心的网页上去复制)
-    'public_key'  => 'MIIBIjANBgkqhkiG9...',
-    // 支付宝私钥文字内容 (1行填写)
-    'private_key' => 'MIIEvQIBADANBgkqh...',
-    // 应用公钥证书完整内容(新版资金类接口转 app_cert_sn)
-    'app_cert'    => '',
-    // 支付宝根证书完整内容(新版资金类接口转 alipay_root_cert_sn)
-    'root_cert'   => '',
+    'sign_type'    => "RSA2",
+    // 应用APPID
+    'appid'        => '2016090900468879',
+    // 支付宝公钥内容 (需1行填写,特别注意:这里不是应用公钥而是支付宝公钥,通常是上传应用公钥换取支付宝公钥,在网页可以复制)
+    'public_key'   => 'MIIBIjAN...',
+    // 应用私钥的内容 (需1行填写,特别注意:这里的应用私钥通常由支付宝密钥管理工具生成)
+    'private_key'  => 'MIIEpAIB...',
+
+    // 应用公钥的内容(新版资金类接口,生成 app_cert_sn)
+    'app_cert'     => '',
+    // 应用公钥序列号(可选,可从应用公钥的内容,优先取值)
+    'app_cert_sn'  => '',
+
+    // 支付宝根证书的内容(新版资金类接口,生成 alipay_root_cert_sn)
+    'root_cert'    => '',
+    // 支付宝根证书序列号(可选,可从应用公钥的内容,优先取值 )
+    'root_cert_sn' => '',
+
     // 支付成功通知地址
-    'notify_url'  => '',
+    'notify_url'   => '',
     // 网页支付回跳地址
-    'return_url'  => '',
+    'return_url'   => '',
 ];
 ```