w7corp/easywechat · error · InvalidConfigException

Missing platform certificate.

Error message

Missing platform certificate.

What it means

Thrown by Pay/Client::withSerialHeader() when the merchant config carries no platform certificates, so there is no serial number to put in the Wechatpay-Serial header. Sensitive-request encryption and certificate-related endpoints require telling WeChat Pay which platform cert/public key you encrypt with; with zero certs loaded there is nothing to choose from.

Source

Thrown at src/Pay/Client.php:198

        $uri = '/'.ltrim((new Uri($url))->getPath(), '/');

        foreach (self::V3_URI_PREFIXES as $prefix) {
            if (str_starts_with($uri, $prefix)) {
                return true;
            }
        }

        return false;
    }

    /**
     * @throws InvalidArgumentException
     */
    public function withSerialHeader(?string $serial = null): static
    {
        $platformCerts = $this->merchant->getPlatformCerts();
        if (empty($platformCerts)) {
            throw new InvalidConfigException('Missing platform certificate.');
        }

        $serial ??= array_key_first($platformCerts);
        $this->withHeader('Wechatpay-Serial', $serial);

        return $this;
    }

    /**
     * @param  array<int, mixed>  $arguments
     */
    public function __call(string $name, array $arguments): mixed
    {
        if (\str_starts_with($name, 'with')) {
            return $this->handleMagicWithCall($name, $arguments[0] ?? null);
        }

        return $this->client->$name(...$arguments);

View on GitHub (pinned to f0cf0a8b83)

Solutions

  1. Download current platform certificate(s) via GET /v3/certificates (the official CertificateDownloader tool) and pass them as the platformCerts array when constructing Merchant
  2. Load the PEM file contents (or PublicKey instances) from a persistent path instead of env-only config
  3. Verify the array is non-empty right after building the Application and fail fast at boot

Example fix

// before
$merchant = new \EasyWeChat\Pay\Merchant($mchId, $privateKey, $cert, $secretKey, null /* v2key */);
// no platform certs -> withSerialHeader() throws
// after
$merchant = new \EasyWeChat\Pay\Merchant(
    $mchId, $privateKey, $cert, $secretKey, $v2Key,
    [ file_get_contents('/certs/wechatpay-platform.pem') ]
);
Defensive patterns

Strategy: validation

Validate before calling

$merchant = new Merchant($mchId, $priv, $cert, $key, $v2, $platformCerts);
if (count($merchant->getPlatformCerts()) === 0) {
    throw new \RuntimeException('No WeChat Pay platform certs loaded - run CertificateDownloader');
}

Type guard

function hasPlatformCerts(\EasyWeChat\Pay\Contracts\Merchant $m): bool
{
    return count($m->getPlatformCerts()) > 0;
}

Try / catch

try {
    $app->client->withSerialHeader();
} catch (\EasyWeChat\Kernel\Exceptions\InvalidConfigException $e) {
    // download certs, rebuild Merchant, retry once
}

Prevention

When it happens

Trigger: Calling pay APIs that trigger withSerialHeader() — e.g. POST with encryption (marketing/redemption codes), GET /v3/certificates flow setups — when the Merchant was constructed without the platformCerts array (default []) or it resolved empty.

Common situations: Fresh integration that only set mchId/privateKey/certificate/secretKey but never downloaded platform certs; certs loaded from env var that is empty; passing an empty string instead of an array; new deployments after cert rotation removed old files.

Understand the failure class

Related errors


AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21). Data as JSON: /api/errors/60c3817c3d10a458. Report an issue: GitHub.