diff --git a/composer.json b/composer.json index 8f85284..74d57bf 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,8 @@ "ext-openssl": "*", "ext-json": "*", "ext-simplexml": "*", + "ext-ctype": "*", + "ext-bcmath": "*", "guzzlehttp/guzzle": "^6.2", "pimple/pimple": "^3.0", "symfony/cache": "^3.3 || ^4.3", diff --git a/src/Kernel/AppClient.php b/src/Kernel/AppClient.php index 81661e0..8609b8d 100755 --- a/src/Kernel/AppClient.php +++ b/src/Kernel/AppClient.php @@ -136,14 +136,13 @@ class AppClient extends BaseClient /** * @param AppRequest $request - * @param null $authToken - * @param null $appInfoAuthtoken * @return Collection * @throws BadRequestException * @throws InvalidArgumentException * @throws InvalidConfigException * @throws InvalidSignException * @throws \GuzzleHttp\Exception\GuzzleException + * @throws \Exception */ public function execute(AppRequest $request) { @@ -164,6 +163,12 @@ class AppClient extends BaseClient $sysParams["sign"] = $this->generateSign(array_merge($apiParams, $sysParams), $this->signType); $requestUrl = $this->buildRequestUrl($sysParams); $result = $this->httpPost($requestUrl, $apiParams); + if(isset($result[$this->ERROR_RESPONSE])){ //返回错误 + throw new BadRequestException( + 'Get Alipay API Error:' . $result[$this->ERROR_RESPONSE]['msg'] . + (isset($result[$this->ERROR_RESPONSE]['sub_code']) ? (' - ' . $result[$this->ERROR_RESPONSE]['sub_code']) : '') + ); + } $apiName = $request->getApiMethodName(); $method = str_replace(".", "_", $apiName) . $this->RESPONSE_SUFFIX; $content = $result[$method]; @@ -186,6 +191,7 @@ class AppClient extends BaseClient /** * @param AppRequest $request * @return array + * @throws \Exception */ protected function buildParams(AppRequest $request): array { @@ -210,7 +216,10 @@ class AppClient extends BaseClient if (isset($this->config['auth_token'])) { $sysParams["auth_token"] = $this->config['auth_token']; } - + if ($this->app->config->get('app_cert_public_key') && $this->app->config->get('alipay_root_cert')) { + $sysParams['app_cert_sn'] = $this->getCertSN($this->app->config->get('app_cert_public_key')); + $sysParams['alipay_root_cert_sn'] = $this->getRootCertSN($this->app->config->get('alipay_root_cert')); + } if (isset($this->config['app_auth_token'])) { $sysParams["app_auth_token"] = $this->config['app_auth_token']; } diff --git a/src/Kernel/Traits/SingData.php b/src/Kernel/Traits/SingData.php index b023b18..e4d8355 100644 --- a/src/Kernel/Traits/SingData.php +++ b/src/Kernel/Traits/SingData.php @@ -29,13 +29,14 @@ trait SingData if (is_null($privateKey)) { throw new InvalidConfigException('Missing Alipay Config -- [private_key]'); } + if (Str::endsWith($privateKey, '.pem')) { $privateKey = openssl_pkey_get_private( - Str::startsWith($privateKey, 'file://') ? $privateKey : 'file://' . $privateKey + Str::startsWith($privateKey, 'file://') ? $privateKey : 'file://'.$privateKey ); } else { - $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" . - wordwrap($privateKey, 64, "\n", true) . + $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n". + wordwrap($privateKey, 64, "\n", true). "\n-----END RSA PRIVATE KEY-----"; } @@ -64,13 +65,15 @@ trait SingData if (is_null($publicKey)) { throw new InvalidConfigException('Missing Alipay Config -- [ali_public_key]'); } - if (Str::endsWith($publicKey, '.pem')) { + if (Str::endsWith($publicKey, '.crt')) { + $publicKey = file_get_contents($publicKey); + } elseif (Str::endsWith($publicKey, '.pem')) { $publicKey = openssl_pkey_get_public( - Str::startsWith($publicKey, 'file://') ? $publicKey : 'file://' . $publicKey + Str::startsWith($publicKey, 'file://') ? $publicKey : 'file://'.$publicKey ); } else { - $publicKey = "-----BEGIN PUBLIC KEY-----\n" . - wordwrap($publicKey, 64, "\n", true) . + $publicKey = "-----BEGIN PUBLIC KEY-----\n". + wordwrap($publicKey, 64, "\n", true). "\n-----END PUBLIC KEY-----"; } $data = json_encode($data, JSON_UNESCAPED_UNICODE); @@ -145,4 +148,101 @@ trait SingData return true; return false; } + + /** + * 生成应用证书SN. + * @param $certPath + * @return string + * @throws \Exception + */ + public function getCertSN($certPath): string + { + if (!is_file($certPath)) { + throw new \Exception('unknown certPath -- [getCertSN]'); + } + $x509data = file_get_contents($certPath); + if (false === $x509data) { + throw new \Exception('Alipay CertSN Error -- [getCertSN]'); + } + openssl_x509_read($x509data); + $certdata = openssl_x509_parse($x509data); + if (empty($certdata)) { + throw new \Exception('Alipay openssl_x509_parse Error -- [getCertSN]'); + } + $issuer_arr = []; + foreach ($certdata['issuer'] as $key => $val) { + $issuer_arr[] = $key.'='.$val; + } + $issuer = implode(',', array_reverse($issuer_arr)); + + return md5($issuer.$certdata['serialNumber']); + } + + /** + * 生成支付宝根证书SN. + * + * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html + * + * @param $certPath + * + * @return string + * + * @throws /Exception + */ + public function getRootCertSN($certPath) + { + if (!is_file($certPath)) { + throw new \Exception('unknown certPath -- [getRootCertSN]'); + } + $x509data = file_get_contents($certPath); + if (false === $x509data) { + throw new \Exception('Alipay CertSN Error -- [getRootCertSN]'); + } + $kCertificateEnd = '-----END CERTIFICATE-----'; + $certStrList = explode($kCertificateEnd, $x509data); + $md5_arr = []; + foreach ($certStrList as $one) { + if (!empty(trim($one))) { + $_x509data = $one.$kCertificateEnd; + openssl_x509_read($_x509data); + $_certdata = openssl_x509_parse($_x509data); + if (in_array($_certdata['signatureTypeSN'], ['RSA-SHA256', 'RSA-SHA1'])) { + $issuer_arr = []; + foreach ($_certdata['issuer'] as $key => $val) { + $issuer_arr[] = $key.'='.$val; + } + $_issuer = implode(',', array_reverse($issuer_arr)); + if (0 === strpos($_certdata['serialNumber'], '0x')) { + $serialNumber = self::bchexdec($_certdata['serialNumber']); + } else { + $serialNumber = $_certdata['serialNumber']; + } + $md5_arr[] = md5($_issuer.$serialNumber); + } + } + } + + return implode('_', $md5_arr); + } + /** + * 0x转高精度数字. + * + * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html + * + * @param $hex + * + * @return int|string + */ + private static function bchexdec($hex) + { + $dec = 0; + $len = strlen($hex); + for ($i = 1; $i <= $len; ++$i) { + if (ctype_xdigit($hex[$i - 1])) { + $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i)))); + } + } + + return $dec; + } } \ No newline at end of file