{"record":{"id":"7bd005cf63f67024","repo":"w7corp/easywechat","slug":"missing-platform-certificate-7bd005","errorCode":null,"errorMessage":"Missing platform certificate.","messagePattern":"Missing platform certificate\\.","errorType":"exception","errorClass":"InvalidConfigException","httpStatus":null,"severity":"error","filePath":"src/Pay/Utils.php","lineNumber":164,"sourceCode":"     * @link https://pay.weixin.qq.com/doc/v3/merchant/4013053257\n     * @link https://pay.weixin.qq.com/doc/v3/partner/4013059044\n     *\n     * @param  string  $plaintext  The text to be encrypted.\n     * @param  string|null  $serial  The serial number of the platform certificate to use for encryption. If null, the first available certificate will be used.\n     * @return string The base64-encoded encrypted text.\n     *\n     * @throws InvalidConfigException If no platform certificate is found.\n     * @throws EncryptionFailureException If the encryption process fails.\n     */\n    public function encryptWithRsaPublicKey(string $plaintext, ?string $serial = null): string\n    {\n        $platformCerts = $this->merchant->getPlatformCerts();\n        /** @var string $identifier - One of the serial number of the platform certificates OR the weixin pay's public key identifier. */\n        $identifier = $serial ?? array_key_first($platformCerts);\n        $platformCert = $this->merchant->getPlatformCert($identifier);\n\n        if (empty($platformCert)) {\n            throw new InvalidConfigException('Missing platform certificate.');\n        }\n\n        if (! openssl_public_encrypt($plaintext, $encrypted, $platformCert, OPENSSL_PKCS1_OAEP_PADDING)) {\n            throw new EncryptionFailureException('Encrypt failed.');\n        }\n\n        return base64_encode($encrypted);\n    }\n\n    /**\n     * @throws InvalidConfigException\n     */\n    public function createV2Signature(array $params): string\n    {\n        $secretKey = $this->merchant->getV2SecretKey();\n\n        if (empty($secretKey)) {\n            throw new InvalidConfigException('Missing v2 secret key.');","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/w7corp/easywechat/blob/f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8/src/Pay/Utils.php#L146-L182","documentation":"Thrown by Pay/Utils::encryptWithRsaPublicKey() when no platform certificate/public key can be resolved for the target serial: either Merchant has zero platform certs, or the explicit $serial (e.g. taken from a notification's Wechatpay-Serial header) is not among the loaded ones. Sensitive fields (bank card, real name, etc.) must be RSA-OAEP encrypted with WeChat Pay's platform public key identified by serial.","triggerScenarios":"Calling utils->encryptWithRsaPublicKey($name/number) for sensitive-payment APIs: with no certs loaded at all, with $serial from the callback header while you only loaded an older cert generation, or after WeChat rotated platform certs (new serial in headers, old cert in config).","commonSituations":"Never downloading platform certs (fresh setup); WeChat's 2024 switch from platform certificates to the 'wechatpay public key' with new serial format (PUB_KEY_ID_xxx) not loaded; certs cached in config but rotation happened after they expire (every 5 years for public key).","solutions":["Download the current platform cert(s)/public key via GET /v3/certificates or the public key from console, load them into Merchant, and key them by their serial number","When $serial comes from a notification header, keep multiple cert generations loaded simultaneously so old and new serials both resolve","At boot, assert count($merchant->getPlatformCerts()) > 0 and re-download on a schedule"],"exampleFix":"// before\n$merchant = new Merchant($mchId, $privateKey, $certificate, $secretKey, $v2Key); // no certs\n$utils->encryptWithRsaPublicKey($idNumber); // throws\n// after - load keyed by serial so header serials resolve\n$merchant = new Merchant($mchId, $privateKey, $certificate, $secretKey, $v2Key, [\n    $platformCertPem, // auto-keyed by its serial number\n]);","handlingStrategy":"validation","validationCode":"$serial = $notificationHeaderWechatpaySerial ?? null;\nif ($serial !== null && $merchant->getPlatformCert($serial) === null) {\n    // cert generation not loaded: fetch via GET /v3/certificates and register before encrypting\n}","typeGuard":"function canResolvePlatformCert(\\EasyWeChat\\Pay\\Merchant $m, ?string $serial): bool\n{\n    return $m->getPlatformCert($serial ?? (string) array_key_first($m->getPlatformCerts())) !== null;\n}","tryCatchPattern":"try {\n    $enc = $app->utils->encryptWithRsaPublicKey($value, $serial);\n} catch (\\EasyWeChat\\Kernel\\Exceptions\\InvalidConfigException $e) {\n    if ($e->getMessage() === 'Missing platform certificate.') {\n        // load cert generation for $serial, then retry once\n    }\n}","preventionTips":["Load all current platform cert generations, keyed by serial","Run a scheduled refresh of /v3/certificates and alert on rotation","Boot-assert platformCerts is non-empty in every environment"],"tags":["wechat-pay","platform-certificate","rsa-encryption","merchant-config"],"backgroundTag":"missing-platform-certificate","analyzedSha":"f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8","analyzedAt":"2026-08-21T05:29:19.565Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}