w7corp/easywechat · error · InvalidConfigException

Missing V2 API key.

Error message

Missing V2 API key.

What it means

Thrown by LegacySignature when building a V2 (MD5/HMAC-SHA256) signature and merchant->getV2SecretKey() is empty. The V2 key is a separate 32-character API key from the V3 secretKey; V2 endpoints (transfers, red envelopes, some refunds) cannot be signed without it.

Source

Thrown at src/Pay/LegacySignature.php:51

        $params = $attributes = array_filter(
            \array_merge(
                [
                    'nonce_str' => $nonce,
                    'sub_mch_id' => $params['sub_mch_id'] ?? null,
                    'sub_appid' => $params['sub_appid'] ?? null,
                ],
                $params
            ),
            static fn ($value, $key) => ! ($key === 'sign' || $value === '' || is_null($value)),
            ARRAY_FILTER_USE_BOTH
        );

        ksort($attributes);

        $attributes['key'] = $this->merchant->getV2SecretKey();

        if (empty($attributes['key'])) {
            throw new InvalidConfigException('Missing V2 API key.');
        }

        $message = urldecode(http_build_query($attributes));

        if (! empty($params['sign_type']) && $params['sign_type'] === 'HMAC-SHA256') {
            $sign = hash_hmac('sha256', $message, $attributes['key']);
        } else {
            $sign = md5($message);
        }

        $params['sign'] = strtoupper($sign);

        return $params;
    }
}

View on GitHub (pinned to f0cf0a8b83)

Solutions

  1. Set the 32-char APIv2 key in WeChat Pay merchant console (account center -> API security -> APIv2 key) if not yet set
  2. Pass it as the 5th constructor arg (v2SecretKey) of Merchant, or via your config provider
  3. Verify the value is exactly 32 characters and is the V2 key, not the V3 secretKey

Example fix

// before
$merchant = new Merchant($mchId, $privateKey, $cert, $secretKey);
$app->utils->createV2Signature($params); // throws Missing V2 API key
// after
$merchant = new Merchant($mchId, $privateKey, $cert, $secretKey, $v2Key /* 32 chars */);
Defensive patterns

Strategy: validation

Validate before calling

$v2 = $merchant->getV2SecretKey();
if ($v2 === null || $v2 === '' || strlen($v2) !== 32) {
    throw new \InvalidArgumentException('V2 API key must be set and 32 chars');
}
$signature = $app->utils->createV2Signature($params);

Type guard

function hasValidV2Key(\EasyWeChat\Pay\Contracts\Merchant $m): bool
{
    $k = $m->getV2SecretKey();
    return is_string($k) && strlen($k) === 32;
}

Try / catch

try {
    $sign = (new \EasyWeChat\Pay\LegacySignature($merchant))->sign($params);
} catch (\EasyWeChat\Kernel\Exceptions\InvalidConfigException $e) {
    // Missing V2 API key -> configure it, no point retrying
    throw new \RuntimeException('Set APIv2 key before calling V2 endpoints', 0, $e);
}

Prevention

When it happens

Trigger: Calling any V2 API through this client or validating a V2 callback signature when Merchant was constructed with v2SecretKey null or ''. E.g. createV2Signature()/ LegacySignature::sign() with only APIv3 secretKey configured.

Common situations: New V3-only setup later adding a legacy endpoint (mmpaymkttransfers/*); the V2 key never set on the merchant account; passing the V3 key (43 chars) where the V2 key (32 chars) is expected so it reads as unset; .env variable name typo leaving the value null.

Related errors


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