From 9dbabfe89e3e967579d65db66c5a8af072884348 Mon Sep 17 00:00:00 2001
From: Anyon <zoujingli@qq.com>
Date: Wed, 11 Mar 2020 09:47:17 +0800
Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=94=AF=E4=BB=98=E8=B5=84?=
 =?UTF-8?q?=E9=87=91=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 AliPay/Transfer.php              | 25 ++++++++++++
 We.php                           |  2 +-
 WeChat/Contracts/BasicAliPay.php | 69 ++++++++++++++++++++++++++++++++
 _test/alipay.php                 |  4 ++
 composer.json                    |  1 +
 5 files changed, 100 insertions(+), 1 deletion(-)

diff --git a/AliPay/Transfer.php b/AliPay/Transfer.php
index 46e9f21..3ba69f1 100644
--- a/AliPay/Transfer.php
+++ b/AliPay/Transfer.php
@@ -15,6 +15,7 @@
 namespace AliPay;
 
 use WeChat\Contracts\BasicAliPay;
+use WeChat\Exceptions\InvalidArgumentException;
 
 /**
  * 支付宝转账到账户
@@ -46,6 +47,7 @@ class Transfer extends BasicAliPay
      */
     public function create($options = [])
     {
+        $this->setAppCertSnAndRootCertSn();
         $this->options->set('method', 'alipay.fund.trans.uni.transfer');
         return $this->getResult($options);
     }
@@ -59,6 +61,7 @@ class Transfer extends BasicAliPay
      */
     public function queryResult($options = [])
     {
+        $this->setAppCertSnAndRootCertSn();
         $this->options->set('method', 'alipay.fund.trans.common.query');
         return $this->getResult($options);
 
@@ -73,7 +76,29 @@ class Transfer extends BasicAliPay
      */
     public function queryAccount($options = [])
     {
+        $this->setAppCertSnAndRootCertSn();
         $this->options->set('method', 'alipay.fund.account.query');
         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/We.php b/We.php
index 4afcb2d..83da261 100644
--- a/We.php
+++ b/We.php
@@ -85,7 +85,7 @@ class We
      * 定义当前版本
      * @var string
      */
-    const VERSION = '1.2.18';
+    const VERSION = '1.2.19';
 
     /**
      * 静态配置
diff --git a/WeChat/Contracts/BasicAliPay.php b/WeChat/Contracts/BasicAliPay.php
index b2d320a..468b03a 100644
--- a/WeChat/Contracts/BasicAliPay.php
+++ b/WeChat/Contracts/BasicAliPay.php
@@ -276,6 +276,75 @@ abstract class BasicAliPay
         return "{$html}<script>document.forms['alipaysubmit'].submit();</script>";
     }
 
+    /**
+     * 新版 从证书中提取序列号
+     * @param string $sign
+     * @return string
+     */
+    public function getCertSN($sign)
+    {
+        if (file_exists($sign)) $sign = file_get_contents($sign);
+        $ssl = openssl_x509_parse($sign);
+        return md5($this->_arr2str(array_reverse($ssl['issuer'])) . $ssl['serialNumber']);
+    }
+
+    /**
+     * 新版 提取根证书序列号
+     * @param string $sign
+     * @return string|null
+     */
+    public function getRootCertSN($sign)
+    {
+        $sn = null;
+        if (file_exists($sign)) $sign = file_get_contents($sign);
+        $array = explode("-----END CERTIFICATE-----", $sign);
+        for ($i = 0; $i < count($array) - 1; $i++) {
+            $ssl[$i] = openssl_x509_parse($array[$i] . "-----END CERTIFICATE-----");
+            if (strpos($ssl[$i]['serialNumber'], '0x') === 0) {
+                $ssl[$i]['serialNumber'] = $this->_hex2dec($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']);
+                }
+            }
+        }
+        return $sn;
+    }
+
+    /**
+     * 新版 数组转字符串
+     * @param array $array
+     * @return string
+     */
+    private function _arr2str($array)
+    {
+        $string = [];
+        if ($array && is_array($array)) {
+            foreach ($array as $key => $value) {
+                $string[] = $key . '=' . $value;
+            }
+        }
+        return implode(',', $string);
+    }
+
+
+    /**
+     * 新版 0x转高精度数字
+     * @param string $hex
+     * @return int|string
+     */
+    private function _hex2dec($hex)
+    {
+        list($dec, $len) = [0, strlen($hex)];
+        for ($i = 1; $i <= $len; $i++) {
+            $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
+        }
+        return $dec;
+    }
+
     /**
      * 应用数据操作
      * @param array $options
diff --git a/_test/alipay.php b/_test/alipay.php
index 25a7379..16bfe40 100644
--- a/_test/alipay.php
+++ b/_test/alipay.php
@@ -23,6 +23,10 @@ return [
     'public_key'  => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtU71NY53UDGY7JNvLYAhsNa+taTF6KthIHJmGgdio9bkqeJGhHk6ttkTKkLqFgwIfgAkHpdKiOv1uZw6gVGZ7TCu5LfHTqKrCd6Uz+N7hxhY+4IwicLgprcV1flXQLmbkJYzFMZqkXGkSgOsR2yXh4LyQZczgk9N456uuzGtRy7MoB4zQy34PLUkkxR6W1B2ftNbLRGXv6tc7p/cmDcrY6K1bSxnGmfRxFSb8lRfhe0V0UM6pKq2SGGSeovrKHN0OLp+Nn5wcULVnFgATXGCENshRlp96piPEBFwneXs19n+sX1jx60FTR7/rME3sW3AHug0fhZ9mSqW4x401WjdnwIDAQAB',
     // 支付宝私钥(1行填写)
     'private_key' => 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3pbN7esinxgjE8uxXAsccgGNKIq+PR1LteNTFOy0fsete43ObQCrzd9DO0zaUeBUzpIOnxrKxez7QoZROZMYrinttFZ/V5rbObEM9E5AR5Tv/Fr4IBywoS8ZtN16Xb+fZmibfU91yq9O2RYSvscncU2qEYmmaTenM0QlUO80ZKqPsM5JkgCNdcYZTUeHclWeyER3dSImNtlSKiSBSSTHthb11fkudjzdiUXua0NKVWyYuAOoDMcpXbD6NJmYqEA/iZ/AxtQt08pv0Mow581GPB0Uop5+qA2hCV85DpagE94a067sKcRui0rtkJzHem9k7xVL+2RoFm1fv3RnUkMwhAgMBAAECggEAAetkddzxrfc+7jgPylUIGb8pyoOUTC4Vqs/BgZI9xYAJksNT2QKRsFvHPfItNt4Ocqy8h4tnIL3GCU43C564B4p6AcjhE85GiN/O0BudPOKlfuQQ9mqExqMMHuYeQfz0cmzPDTSGMwWiv9v4KBH2pyvkCCAzNF6uG+rvawb4/NNVuiI7C8Ku/wYsamtbgjMZVOFFdScYgIw1BgA99RUU/fWBLMnTQkoyowSRb9eSmEUHjt/WQt+/QgKAT2WmuX4RhaGy0qcQLbNaJNKXdJ+PVhQrSiasINNtqYMa8GsQuuKsk3X8TCg9K6/lowivt5ruhyWcP2sx93zY/LGzIHgHcQKBgQDoZlcs9RWxTdGDdtH8kk0J/r+QtMijNzWI0a+t+ZsWOyd3rw+uM/8O4JTNP4Y98TvvxhJXewITbfiuOIbW1mxh8bnO/fcz7+RXZKgPDeoTeNo717tZFZGBEyUdH9M9Inqvht7+hjVDIMCYBDomYebdk3Xqo4mDBjLRdVNGrhGmVQKBgQDKS/MgTMK8Ktfnu1KzwCbn/FfHTOrp1a1t1wWPv9AW0rJPYeaP6lOkgIoO/1odG9qDDhdB6njqM+mKY5Yr3N94PHamHbwJUCmbkqEunCWpGzgcQZ1Q254xk9D7UKq/XUqW2WDqDq80GQeNial+fBc46yelQzokwdA+JdIFKoyinQKBgQCBems9V/rTAtkk1nFdt6EGXZEbLS3PiXXhGXo4gqV+OEzf6H/i/YMwJb2hsK+5GQrcps0XQihA7PctEb9GOMa/tu5fva0ZmaDtc94SLR1p5d4okyQFGPgtIp594HpPSEN0Qb9BrUJFeRz0VP6U3dzDPGHo7V4yyqRLgIN6EIcy1QKBgAqdh6mHPaTAHspDMyjJiYEc5cJIj/8rPkmIQft0FkhMUB0IRyAALNlyAUyeK61hW8sKvz+vPR8VEEk5xpSQp41YpuU6pDZc5YILZLfca8F+8yfQbZ/jll6Foi694efezl4yE/rUQG9cbOAJfEJt4o4TEOaEK5XoMbRBKc8pl22lAoGARTq0qOr9SStihRAy9a+8wi2WEwL4QHcmOjH7iAuJxy5b5TRDSjlk6h+0dnTItiFlTXdfpO8KhWA8EoSJVBZ1kcACQDFgMIA+VM+yXydtzMotOn21W4stfZ4I6dHFiujMsnKpNYVpQh3oCrJf4SeXiQDdiSCodqb1HlKkEc6naHQ=',
+    // 应用公钥证书(新版资金类接口转app_cert_sn)
+    'app_cert'    => '',
+    // 支付宝根证书(新版资金类接口转alipay_root_cert_sn)
+    'root_cert'   => '',
     // 支付成功通知地址
     'notify_url'  => '',
     // 网页支付回跳地址
diff --git a/composer.json b/composer.json
index 931573b..d21c7f0 100644
--- a/composer.json
+++ b/composer.json
@@ -23,6 +23,7 @@
     "php": ">=5.4",
     "ext-json": "*",
     "ext-curl": "*",
+    "ext-bcmath": "*",
     "ext-libxml": "*",
     "ext-openssl": "*",
     "ext-mbstring": "*",