{"record":{"id":"f27a01531ddcb82b","repo":"w7corp/easywechat","slug":"read-the-certificate-failed-please-check-it-whet","errorCode":null,"errorMessage":"Read the $certificate failed, please check it whether or nor correct","messagePattern":"Read the \\$certificate failed, please check it whether or nor correct","errorType":"exception","errorClass":"InvalidConfigException","httpStatus":null,"severity":"error","filePath":"src/Kernel/Support/PublicKey.php","lineNumber":30,"sourceCode":"\nclass PublicKey\n{\n    public function __construct(public string $certificate)\n    {\n        if (file_exists($certificate)) {\n            $this->certificate = \"file://{$certificate}\";\n        }\n    }\n\n    /**\n     * @throws InvalidConfigException\n     */\n    public function getSerialNo(): string\n    {\n        $info = openssl_x509_parse($this->certificate);\n\n        if ($info === false) {\n            throw new InvalidConfigException('Read the $certificate failed, please check it whether or nor correct');\n        }\n\n        return strtoupper($info['serialNumberHex']);\n    }\n\n    public function __toString(): string\n    {\n        if (str_starts_with($this->certificate, 'file://')) {\n            return file_get_contents($this->certificate) ?: '';\n        }\n\n        return $this->certificate;\n    }\n}\n","sourceCodeStart":12,"sourceCodeEnd":45,"githubUrl":"https://github.com/w7corp/easywechat/blob/f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8/src/Kernel/Support/PublicKey.php#L12-L45","documentation":"PublicKey::getSerialNo() runs openssl_x509_parse() on the stored certificate and throws this InvalidConfigException when parsing fails, i.e. the string is not an X.509 certificate. Pay\\Application uses it for the merchant certificate (src/Pay/Application.php:44) and Pay\\Merchant::getPlatformCerts for platform certs; the serial feeds request signing (src/Pay/Signature.php:64). The constructor only maps an existing file path to file:// — any other string is treated as PEM content, so a bare non-existent path is parsed as PEM and fails.","triggerScenarios":"Config certificate contains a PRIVATE KEY or PUBLIC KEY PEM instead of CERTIFICATE; passing a relative file path that does not exist in the current runtime cwd (file_exists fails, path string is parsed as PEM); a platform cert string truncated, with BOM, or in DER binary form.","commonSituations":"Swapping the certificate and private_key config entries (apiclient_cert.pem vs apiclient_key.pem); relative paths that differ between CLI and web workers; copying the cert with mangled line endings; platform certs downloaded via API stored incompletely.","solutions":["Ensure the value is a PEM starting with -----BEGIN CERTIFICATE----- (or an existing file path containing one).","Check you did not swap 'certificate' and 'private_key' (mch_key) config entries.","Pre-parse to see the real error: openssl_x509_parse($pem) or openssl x509 -in cert.pem -noout -text.","Use absolute paths or pass file_get_contents() output; strip BOM/whitespace."],"exampleFix":"// before: private key PEM in the certificate slot\n$merchant = new Merchant(mchId: $mchId, certificate: new PublicKey($privateKeyPem));\n// after: verified X.509 certificate\n$cert = trim((string) file_get_contents('/etc/wechat/apiclient_cert.pem'));\nif (!str_contains($cert, 'BEGIN CERTIFICATE')) { throw new InvalidArgumentException('not an X.509 certificate'); }\n$merchant = new Merchant(mchId: $mchId, certificate: new PublicKey($cert));","handlingStrategy":"validation","validationCode":"$cert = trim((string) $config['certificate']);\nif (!str_contains($cert, 'BEGIN CERTIFICATE')) { throw new InvalidArgumentException('certificate config must be an X.509 PEM'); }\nif (!str_starts_with($cert, '/') || is_file($cert)) { /* ok: existing path or inline PEM */ }","typeGuard":"function isValidX509Pem(string $cert): bool { return str_contains($cert, '-----BEGIN CERTIFICATE-----') && @openssl_x509_parse($cert) !== false; }","tryCatchPattern":"try { $serial = $publicKey->getSerialNo(); } catch (\\EasyWeChat\\Kernel\\Exceptions\\InvalidConfigException $e) { throw new RuntimeException('merchant/platform certificate is not a parseable X.509 PEM', 0, $e); }","preventionTips":["Name config keys after the files they hold (apiclient_cert.pem vs apiclient_key.pem)","Add a boot-time openssl_x509_parse smoke test for every configured cert","Prefer absolute paths or file contents over relative paths"],"tags":["php","openssl","x509-certificate","wechat-pay","config"],"backgroundTag":"invalid-certificate","analyzedSha":"f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8","analyzedAt":"2026-08-21T05:29:19.565Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}