From fc62209e6444efd82b4ca6af12b6122de5c66284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E6=99=AF=E7=AB=8B?= Date: Tue, 4 Mar 2025 09:16:32 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=A2=9E=E5=8A=A0=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E6=89=B9=E9=87=8F=E8=AF=81=E4=B9=A6=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=EF=BC=8C=E6=94=AF=E6=8C=81=E5=B9=B3=E5=8F=B0=E8=AF=81=E4=B9=A6?= =?UTF-8?q?=E5=8F=8A=E6=94=AF=E4=BB=98=E8=AF=81=E4=B9=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WePayV3/Contracts/BasicWePay.php | 103 +++++++++++++++---- _test/pay-v3-config.php | 166 ++++++++++++++++--------------- 2 files changed, 171 insertions(+), 98 deletions(-) diff --git a/WePayV3/Contracts/BasicWePay.php b/WePayV3/Contracts/BasicWePay.php index a31c820..bf3de97 100644 --- a/WePayV3/Contracts/BasicWePay.php +++ b/WePayV3/Contracts/BasicWePay.php @@ -39,7 +39,7 @@ abstract class BasicWePay * 实例对象静态缓存 * @var array */ - static $cache = []; + protected static $cache = []; /** * 自动配置平台证书 @@ -58,8 +58,9 @@ abstract class BasicWePay 'cert_serial' => '', // 商户证书序号,无需配置 'cert_public' => '', // 商户公钥内容,需要配置 'cert_private' => '', // 商户密钥内容,需要配置 - 'mp_cert_serial' => '', // 平台证书序号,无需配置 ( 新平台,当做微信支付证书用 ) - 'mp_cert_content' => '', // 平台证书内容,无需配置 ( 新平台,当做微信支付证书用 ) + 'cert_package' => [], // 平台证书或支付证书配置 + 'mp_cert_serial' => '', // 平台证书序号,无需配置 ( 指定平台证书或支付公钥 ) + 'mp_cert_content' => '', // 平台证书内容,无需配置 ( 指定平台证书或支付公钥 ) ]; /** @@ -117,10 +118,19 @@ abstract class BasicWePay Tools::$cache_path = $options['cache_path']; } - // 自动配置平台证书 + // 批量设置自定义证书 + if (isset($options['cert_package']) && is_array($options['cert_package'])) { + foreach ($options['cert_package'] as $key => $cert) { + $this->withCertContent($key, $cert); + } + } + + // 自动配置平台证书或支付公钥 if (empty($options['mp_cert_serial']) || empty($options['mp_cert_content'])) { - if ($this->autoCert) $this->_autoCert(); - } else { + if ($this->autoCert && !$this->withCertPayment()) { + $this->_autoCert(); + } + } elseif ($this->withCertContent($options['mp_cert_serial'], $options['mp_cert_content'])) { $this->config['mp_cert_serial'] = $options['mp_cert_serial']; $this->config['mp_cert_content'] = $options['mp_cert_content']; } @@ -340,10 +350,10 @@ abstract class BasicWePay */ protected function signVerify($data, $sign, $serial) { - if (stripos($this->config['mp_cert_serial'], 'PUB_KEY_ID_') !== false) { - return @openssl_verify($data, base64_decode($sign), $this->config['mp_cert_content'], OPENSSL_ALGO_SHA256); + if (stripos($serial, 'PUB_KEY_ID_') !== false && !empty($this->config['cert_package'][$serial])) { + return openssl_verify($data, base64_decode($sign), $this->config['cert_package'][$serial], OPENSSL_ALGO_SHA256); } else { - return @openssl_verify($data, base64_decode($sign), openssl_x509_read($this->_getCert($serial)), 'sha256WithRSAEncryption'); + return openssl_verify($data, base64_decode($sign), openssl_x509_read($this->_getCert($serial)), 'sha256WithRSAEncryption'); } } @@ -361,11 +371,31 @@ abstract class BasicWePay Cert::instance($this->config)->download(); $certs = $this->tmpFile("{$this->config['mch_id']}_certs"); } - if (empty($certs[$serial]['content']) || $certs[$serial]['expire'] < time()) { - throw new InvalidResponseException("读取平台证书失败!"); - } else { - return $certs[$serial]['content']; + foreach ($certs as $cert) { + if ($certs[$serial]['expire'] > time()) { + $this->config['cert_package'][$cert['serial']] = $cert['content']; + if (empty($this->config['mp_cert_serial'])) { + $this->config['mp_cert_serial'] = $cert['serial']; + $this->config['mp_cert_content'] = $cert['content']; + } + } } + // 未设置序号时,直接返回默认证书内容 + if (empty($serial) && !empty($this->config['mp_cert_content'])) { + return $this->config['mp_cert_content']; + } + + // 遍历证书数组,找到匹配的证书 + if ($cert = $this->withCertPayment()) { + return $cert; + } + + // 检查指定序号的证书是否存在 + if (!isset($this->config['cert_package'][$serial])) { + throw new InvalidResponseException("读取平台证书失败!"); + } + + return $this->config['cert_package'][$serial]; } /** @@ -388,11 +418,13 @@ abstract class BasicWePay throw new InvalidResponseException("读取平台证书失败!"); } foreach ($certs as $k => $v) if ($v['expire'] > time() + 10) { - $this->config['mp_cert_serial'] = $k; - $this->config['mp_cert_content'] = $v['content']; - break; + $this->config['cert_package'][$k] = $v['content']; + if (empty($this->config['mp_cert_serial'])) { + $this->config['mp_cert_serial'] = $k; + $this->config['mp_cert_content'] = $v['content']; + } } - if (empty($this->config['mp_cert_serial']) || empty($this->config['mp_cert_content'])) { + if (empty($this->config['cert_package'])) { throw new InvalidResponseException("自动配置平台证书失败!"); } } @@ -432,4 +464,41 @@ abstract class BasicWePay throw new InvalidDecryptException('Rsa Encrypt Error.'); } } + + /** + * 设置证书内容 + * @param string $key 证书ID或序号 + * @param string $cert 证书文本内容 + * @return string + * @throws \WeChat\Exceptions\InvalidResponseException + */ + private function withCertContent($key, $cert) + { + if (substr(trim($cert), 0, 5) == '-----') { + $this->config['cert_package'][$key] = $cert; + } elseif (file_exists($cert)) { + $this->config['cert_package'][$key] = file_get_contents($cert); + } else { + throw new InvalidResponseException("证书设置失败!"); + } + return $cert; + } + + /** + * 获取支付证书 + * @return mixed|string + */ + private function withCertPayment() + { + foreach ($this->config['cert_package'] as $key => $cert) { + if (strpos($key, 'PUB_KEY_ID_') === 0) { + if (empty($this->config['mp_cert_serial']) || empty($this->config['mp_cert_content'])) { + $this->config['mp_cert_serial'] = $key; + $this->config['mp_cert_content'] = $cert; + } + return $cert; + } + } + return ''; + } } \ No newline at end of file diff --git a/_test/pay-v3-config.php b/_test/pay-v3-config.php index 61af7fd..66f48c9 100644 --- a/_test/pay-v3-config.php +++ b/_test/pay-v3-config.php @@ -1,82 +1,86 @@ - function ($name, $value, $expired = 360) { -// var_dump(func_get_args()); -// return $value; -// }, -// 'get' => function ($name) { -// var_dump(func_get_args()); -// return $value; -// }, -// 'del' => function ($name) { -// var_dump(func_get_args()); -// return true; -// }, -// 'put' => function ($name) { -// var_dump(func_get_args()); -// return $filePath; -// }, -// ]; - -return [ - // 可选,公众号APPID - 'appid' => 'wx3760axxxxxxxxxxx', - // 必填,微信商户编号ID - 'mch_id' => '152xxxxxxxx', - // 必填,微信商户V3接口密钥,不影响发起支付但无法验证支付通知 - 'mch_v3_key' => '98b7f45xxxxxxxxxxxxxxxxxxxxxxxxxx', - // 可选,微信商户证书序列号,可从公钥中提取,请求签名使用 - 'cert_serial' => '49055xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', - // 必填,微信商户证书公钥,支持证书内容或文件路径,仅用于提取序号 - 'cert_public' => $certPublic, - // 必填,微信商户证书私钥,支持证书内容或文件路径,用于请求数据签名 - 'cert_private' => $certPrivate, - // 可选,微信平台证书序号或支付证书序号,用于接口请求序号 - 'mp_cert_serial' => 'PUB_KEY_ID_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', - // 可选,微信平台证书内容或支付证书内容 - 'mp_cert_content' => $certPayment, - // 可选,运行时的文件缓存路径 - 'cache_path' => '' + function ($name, $value, $expired = 360) { +// var_dump(func_get_args()); +// return $value; +// }, +// 'get' => function ($name) { +// var_dump(func_get_args()); +// return $value; +// }, +// 'del' => function ($name) { +// var_dump(func_get_args()); +// return true; +// }, +// 'put' => function ($name) { +// var_dump(func_get_args()); +// return $filePath; +// }, +// ]; + +return [ + // 可选,公众号APPID + 'appid' => 'wx3760xxxxxxxxxxxx', + // 必填,微信商户编号ID + 'mch_id' => '15293xxxxxx', + // 必填,微信商户V3接口密钥 + 'mch_v3_key' => '98b7fxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + // 可选,微信商户证书序列号,可从公钥中提取,请求签名使用 + 'cert_serial' => '49055D67B2XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + // 必填,微信商户证书公钥,支持证书内容或文件路径,仅用于提取序号 + 'cert_public' => $certPublic, + // 必填,微信商户证书私钥,支持证书内容或文件路径,用于请求数据签名 + 'cert_private' => $certPrivate, + // 批量设置自定义证书内容,支持平台证书或支付公钥,可填写文件路径及内容 + 'cert_package' => [ + 'PUB_KEY_ID_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' => $certPayment + ], + // 可选,微信平台证书序号或支付证书序号,直接支持平台证书或支付公钥 + // 'mp_cert_serial' => 'PUB_KEY_ID_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + // 可选,微信平台证书内容或支付证书内容,直接支持平台证书或支付公钥 + // 'mp_cert_content' => $certPayment, + // 可选,运行时的文件缓存路径 + 'cache_path' => '' ]; \ No newline at end of file