#!/usr/bin/env php parseOpts(); if (!$opts) { $this->printHelp(); return; } if (isset($opts['help'])) { $this->printHelp(); return; } if (isset($opts['version'])) { echo ClientDecoratorInterface::VERSION, PHP_EOL; return; } $this->job($opts); } /** * @param array $opts * * @return void */ private function job(array $opts): void { static $certs = ['any' => null]; $outputDir = $opts['output'] ?? \sys_get_temp_dir(); $apiv3Key = (string) $opts['key']; $instance = Builder::factory([ 'mchid' => $opts['mchid'], 'serial' => $opts['serialno'], 'privateKey' => \file_get_contents((string)$opts['privatekey']), 'certs' => &$certs, ]); $handler = $instance->getDriver()->select(ClientDecoratorInterface::JSON_BASED)->getConfig('handler'); $handler->after('verifier', Middleware::mapResponse(static function($response) use ($apiv3Key, &$certs) { $body = $response->getBody()->getContents(); /** @var object{data:array} $json */ $json = Utils::jsonDecode($body); \array_map(static function($row) use ($apiv3Key, &$certs) { $cert = $row->encrypt_certificate; $certs[$row->serial_no] = AesGcm::decrypt($cert->ciphertext, $apiv3Key, $cert->nonce, $cert->associated_data); }, \is_object($json) && isset($json->data) && \is_array($json->data) ? $json->data : []); return $response; }), 'injector'); $instance->chain('v3/certificates')->getAsync(['debug' => true])->then(static function($response) use ($outputDir, &$certs) { $body = $response->getBody()->getContents(); $timeZone = new \DateTimeZone('Asia/Shanghai'); /** @var object{data:array} $json */ $json = Utils::jsonDecode($body); $data = \is_object($json) && isset($json->data) && \is_array($json->data) ? $json->data : []; \array_walk($data, static function($row, $index, $certs) use ($outputDir, $timeZone) { $serialNo = $row->serial_no; $outpath = $outputDir . DIRECTORY_SEPARATOR . 'wechatpay_' . $serialNo . '.pem'; echo 'Certificate #', $index, ' {', PHP_EOL; echo ' Serial Number: ', $serialNo, PHP_EOL; echo ' Not Before: ', (new \DateTime($row->effective_time, $timeZone))->format('Y-m-d H:i:s'), PHP_EOL; echo ' Not After: ', (new \DateTime($row->expire_time, $timeZone))->format('Y-m-d H:i:s'), PHP_EOL; echo ' Saved to: ', $outpath, PHP_EOL; echo ' Content: ', PHP_EOL, PHP_EOL, $certs[$serialNo], PHP_EOL, PHP_EOL; echo '}', PHP_EOL; \file_put_contents($outpath, $certs[$serialNo]); }, $certs); return $response; })->otherwise(static function($exception) { $body = $exception->getResponse()->getBody(); echo $body->getContents(), PHP_EOL, PHP_EOL, PHP_EOL; echo $exception->getTraceAsString(), PHP_EOL; })->wait(); } /** * @return ?array */ private function parseOpts(): ?array { $opts = [ [ 'key', 'k', true ], [ 'mchid', 'm', true ], [ 'privatekey', 'f', true ], [ 'serialno', 's', true ], [ 'output', 'o', false ], ]; $shortopts = 'hV'; $longopts = [ 'help', 'version' ]; foreach ($opts as $opt) { list($key, $alias) = $opt; $shortopts .= $alias . ':'; $longopts[] = $key . ':'; } $parsed = \getopt($shortopts, $longopts); if (!$parsed) { return null; } $args = []; foreach ($opts as $opt) { list($key, $alias, $mandatory) = $opt; if (isset($parsed[$key]) || isset($parsed[$alias])) { $possiable = $parsed[$key] ?? $parsed[$alias] ?? ''; $args[$key] = (string) (is_array($possiable) ? $possiable[0] : $possiable); } elseif ($mandatory) { return null; } } 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(): void { echo << -k= -m= -o=[outputFilePath] -s= -m, --mchid= 商户号 -s, --serialno= 商户证书的序列号 -f, --privatekey= 商户的私钥文件 -k, --key= API v3密钥 -o, --output=[outputFilePath] 下载成功后保存证书的路径,可选参数,默认为临时文件目录夹 -V, --version Print version information and exit. -h, --help Show this help message and exit. EOD; } } // main (new CertificateDownloader())->run();