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
- Set the 32-char APIv2 key in WeChat Pay merchant console (account center -> API security -> APIv2 key) if not yet set
- Pass it as the 5th constructor arg (v2SecretKey) of Merchant, or via your config provider
- 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
- Set the APIv2 key in the merchant console before enabling any V2 flow
- Keep V2 and V3 keys in separate, clearly named config keys
- Unit-test that V2 code paths fail fast at boot when the key is absent
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
- V2 secret key is required.
- Missing platform certificate.
- Invalid platform certficate.
- Missing platform certificate.
- Encrypt AES ECB failed.
AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21).
Data as JSON: /api/errors/b9472c2f19b46000.
Report an issue: GitHub.