w7corp/easywechat · error · InvalidConfigException
Read the $certificate failed, please check it whether or nor
Error message
Read the $certificate failed, please check it whether or nor correct
What it means
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.
Source
Thrown at src/Kernel/Support/PublicKey.php:30
class PublicKey
{
public function __construct(public string $certificate)
{
if (file_exists($certificate)) {
$this->certificate = "file://{$certificate}";
}
}
/**
* @throws InvalidConfigException
*/
public function getSerialNo(): string
{
$info = openssl_x509_parse($this->certificate);
if ($info === false) {
throw new InvalidConfigException('Read the $certificate failed, please check it whether or nor correct');
}
return strtoupper($info['serialNumberHex']);
}
public function __toString(): string
{
if (str_starts_with($this->certificate, 'file://')) {
return file_get_contents($this->certificate) ?: '';
}
return $this->certificate;
}
}
View on GitHub (pinned to f0cf0a8b83)
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.
Example fix
// before: private key PEM in the certificate slot
$merchant = new Merchant(mchId: $mchId, certificate: new PublicKey($privateKeyPem));
// after: verified X.509 certificate
$cert = trim((string) file_get_contents('/etc/wechat/apiclient_cert.pem'));
if (!str_contains($cert, 'BEGIN CERTIFICATE')) { throw new InvalidArgumentException('not an X.509 certificate'); }
$merchant = new Merchant(mchId: $mchId, certificate: new PublicKey($cert)); Defensive patterns
Strategy: validation
Validate before calling
$cert = trim((string) $config['certificate']);
if (!str_contains($cert, 'BEGIN CERTIFICATE')) { throw new InvalidArgumentException('certificate config must be an X.509 PEM'); }
if (!str_starts_with($cert, '/') || is_file($cert)) { /* ok: existing path or inline PEM */ } Type guard
function isValidX509Pem(string $cert): bool { return str_contains($cert, '-----BEGIN CERTIFICATE-----') && @openssl_x509_parse($cert) !== false; } Try / catch
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); } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21).
Data as JSON: /api/errors/f27a01531ddcb82b.
Report an issue: GitHub.