canyin-project/ybcy/vendor/wechatpay/wechatpay-guzzle-middleware/tool/CertificateDownloader.php
2024-11-01 16:07:54 +08:00

219 lines
7.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env php
<?php
// load autoload.php
$possibleFiles = [__DIR__.'/../vendor/autoload.php', __DIR__.'/../../../autoload.php', __DIR__.'/../../autoload.php'];
$file = null;
foreach ($possibleFiles as $possibleFile) {
if (file_exists($possibleFile)) {
$file = $possibleFile;
break;
}
}
if (null === $file) {
throw new RuntimeException('Unable to locate autoload.php file.');
}
require_once $file;
unset($possibleFiles, $possibleFile, $file);
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
use WechatPay\GuzzleMiddleware\Validator;
use WechatPay\GuzzleMiddleware\Util\PemUtil;
use WechatPay\GuzzleMiddleware\Util\AesUtil;
use WechatPay\GuzzleMiddleware\Auth\CertificateVerifier;
use WechatPay\GuzzleMiddleware\Auth\WechatPay2Validator;
class CertificateDownloader
{
const VERSION = '0.1.0';
public function run()
{
$opts = $this->parseOpts();
if (!$opts) {
$this->printHelp();
exit(1);
}
if (isset($opts['help'])) {
$this->printHelp();
exit(0);
}
if (isset($opts['version'])) {
echo self::VERSION . "\n";
exit(0);
}
$this->downloadCert($opts);
}
private function downloadCert($opts)
{
try {
// 构造一个WechatPayMiddleware
$builder = WechatPayMiddleware::builder()
->withMerchant($opts['mchid'], $opts['serialno'], PemUtil::loadPrivateKey($opts['privatekey'])); // 传入商户相关配置
if (isset($opts['wechatpay-cert'])) {
$builder->withWechatPay([ PemUtil::loadCertificate($opts['wechatpay-cert']) ]); // 使用平台证书验证
}
else {
$builder->withValidator(new NoopValidator); // 临时"跳过”应答签名的验证
}
$wechatpayMiddleware = $builder->build();
// 将WechatPayMiddleware添加到Guzzle的HandlerStack中
$stack = HandlerStack::create();
$stack->push($wechatpayMiddleware, 'wechatpay');
// 创建Guzzle HTTP Client时将HandlerStack传入
$client = new GuzzleHttp\Client(['handler' => $stack]);
// 接下来正常使用Guzzle发起API请求WechatPayMiddleware会自动地处理签名和验签
$resp = $client->request('GET', 'https://api.mch.weixin.qq.com/v3/certificates', [
'headers' => [ 'Accept' => 'application/json' ]
]);
if ($resp->getStatusCode() < 200 || $resp->getStatusCode() > 299) {
echo "download failed, code={$resp->getStatusCode()}, body=[{$resp->getBody()}]\n";
return;
}
$list = json_decode($resp->getBody(), true);
$plainCerts = [];
$x509Certs = [];
$decrypter = new AesUtil($opts['key']);
foreach ($list['data'] as $item) {
$encCert = $item['encrypt_certificate'];
$plain = $decrypter->decryptToString($encCert['associated_data'],
$encCert['nonce'], $encCert['ciphertext']);
if (!$plain) {
echo "encrypted certificate decrypt fail!\n";
exit(1);
}
// 通过加载对证书进行简单合法性检验
$cert = \openssl_x509_read($plain); // 从字符串中加载证书
if (!$cert) {
echo "downloaded certificate check fail!\n";
exit(1);
}
$plainCerts[] = $plain;
$x509Certs[] = $cert;
}
// 使用下载的证书再来验证一次应答的签名
$validator = new WechatPay2Validator(new CertificateVerifier($x509Certs));
if (!$validator->validate($resp)) {
echo "validate response fail using downloaded certificates!";
exit(1);
}
// 输出证书信息,并保存到文件
foreach ($list['data'] as $index => $item) {
echo "Certificate {\n";
echo " Serial Number: ".$item['serial_no']."\n";
echo " Not Before: ".(new DateTime($item['effective_time']))->format('Y-m-d H:i:s')."\n";
echo " Not After: ".(new DateTime($item['expire_time']))->format('Y-m-d H:i:s')."\n";
echo " Text: \n ".str_replace("\n", "\n ", $plainCerts[$index])."\n";
echo "}\n";
$outpath = $opts['output'].DIRECTORY_SEPARATOR.'wechatpay_'.$item['serial_no'].'.pem';
file_put_contents($outpath, $plainCerts[$index]);
}
}
catch (RequestException $e) {
echo "download failed, message=[{$e->getMessage()}] ";
if ($e->hasResponse()) {
echo "code={$e->getResponse()->getStatusCode()}, body=[{$e->getResponse()->getBody()}]\n";
}
exit(1);
}
catch (Exception $e) {
echo "download failed, message=[{$e->getMessage()}]\n";
echo $e;
exit(1);
}
}
private function parseOpts()
{
$opts = [
[ 'key', 'k', true ],
[ 'mchid', 'm', true ],
[ 'privatekey', 'f', true ],
[ 'serialno', 's', true ],
[ 'output', 'o', true ],
[ 'wechatpay-cert', 'c', false ],
];
$shortopts = 'hV';
$longopts = [ 'help', 'version' ];
foreach ($opts as $opt) {
$shortopts .= $opt[1].':';
$longopts[] = $opt[0].':';
}
$parsed = getopt($shortopts, $longopts);
if (!$parsed) {
return false;
}
$args = [];
foreach ($opts as $opt) {
if (isset($parsed[$opt[0]])) {
$args[$opt[0]] = $parsed[$opt[0]];
}
else if (isset($parsed[$opt[1]])) {
$args[$opt[0]] = $parsed[$opt[1]];
}
else if ($opt[2]) {
return false;
}
}
if (isset($parsed['h']) || isset($parsed['help'])) {
$args['help'] = true;
}
if (isset($parsed['V']) || isset($parsed['version'])) {
$args['version'] = true;
}
return $args;
}
private function printHelp()
{
echo <<<EOD
Usage: 微信支付平台证书下载工具 [-hV] [-c=<wechatpayCertificatePath>]
-f=<privateKeyFilePath> -k=<apiV3key> -m=<merchantId>
-o=<outputFilePath> -s=<serialNo>
-m, --mchid=<merchantId> 商户号
-s, --serialno=<serialNo> 商户证书的序列号
-f, --privatekey=<privateKeyFilePath>
商户的私钥文件
-k, --key=<apiV3key> ApiV3Key
-c, --wechatpay-cert=<wechatpayCertificatePath>
微信支付平台证书,验证签名
-o, --output=<outputFilePath>
下载成功后保存证书的路径
-V, --version Print version information and exit.
-h, --help Show this help message and exit.
EOD;
}
}
class NoopValidator implements Validator
{
public function validate(\Psr\Http\Message\ResponseInterface $response)
{
return true;
}
}
// main
(new CertificateDownloader())->run();