w7corp/easywechat · error · InvalidConfigException
Missing v2 secret key.
Error message
Missing v2 secret key.
What it means
Thrown by Utils::createV2Signature() (src/Pay/Utils.php:182) when the merchant has no WeChat Pay APIv2 key. This method implements legacy v2 signing: ksort() the params, append the key as `key`, then produce an HMAC-SHA256 or MD5 signature over the urldecoded query string. The v2 key (config `v2_secret_key`, see src/Pay/Merchant.php:31 and src/Pay/Application.php:46) is a separate 32-character key from the v3 `secret_key`, so a fully working v3 setup can still hit this. It is raised as InvalidConfigException before any crypto runs, via empty() on getV2SecretKey().
Source
Thrown at src/Pay/Utils.php:182
throw new InvalidConfigException('Missing platform certificate.');
}
if (! openssl_public_encrypt($plaintext, $encrypted, $platformCert, OPENSSL_PKCS1_OAEP_PADDING)) {
throw new EncryptionFailureException('Encrypt failed.');
}
return base64_encode($encrypted);
}
/**
* @throws InvalidConfigException
*/
public function createV2Signature(array $params): string
{
$secretKey = $this->merchant->getV2SecretKey();
if (empty($secretKey)) {
throw new InvalidConfigException('Missing v2 secret key.');
}
ksort($params);
$params['key'] = $secretKey;
$message = urldecode(http_build_query($params));
if ($params['signType'] === 'HMAC-SHA256') {
$signature = hash_hmac('sha256', $message, $secretKey);
} else {
$signature = md5($message);
}
return strtoupper($signature);
}
}
View on GitHub (pinned to f0cf0a8b83)
Solutions
- Add 'v2_secret_key' => '<32-char key>' to the pay config — get it in the WeChat Pay merchant console under Account Center > API Security > APIv2 key (账户中心 > API安全 > APIv2密钥).
- If the integration only needs v3, switch the offending call to the v3 API equivalent and drop the v2 signing path.
- Verify the value actually loads where the call runs: dump the pay config, check .env, config cache and environment-specific overrides.
- Confirm the key is exactly 32 characters with no surrounding whitespace or quotes.
Example fix
// before
$config = [
'mch_id' => '1900000000',
'secret_key' => '<api-v3-key>',
'private_key' => '...',
'certificate' => '...',
// v2_secret_key missing -> createV2Signature() throws
];
$sign = $app->getUtils()->createV2Signature($params);
// after
$config = [
'mch_id' => '1900000000',
'secret_key' => '<api-v3-key>',
'private_key' => '...',
'certificate' => '...',
'v2_secret_key' => '<32-char-api-v2-key>',
];
$sign = $app->getUtils()->createV2Signature($params); Defensive patterns
Strategy: validation
Validate before calling
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
if (empty($app->getMerchant()->getV2SecretKey())) {
throw new InvalidConfigException(
'WeChat Pay v2_secret_key is not configured; set it before using v2 signing.'
);
}
$sign = $app->getUtils()->createV2Signature($params); Try / catch
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
try {
$sign = $utils->createV2Signature($params);
} catch (InvalidConfigException $e) {
// Config problem, not runtime: surface it — never send unsigned v2 requests
Log::error('wechat-pay v2 signing unavailable: {msg}', ['msg' => $e->getMessage()]);
throw $e;
} Prevention
- Keep v2_secret_key next to secret_key in the pay config so v3-only setups don't silently omit it
- Run a boot-time check that required config keys are non-empty (secret_key, private_key, certificate, and v2_secret_key whenever any v2 path is used)
- Load keys from env or a secret manager; never fall back to empty strings
- Add a canary v2 sign call to deployment smoke tests when v2 APIs are in use
When it happens
Trigger: Calling Utils::createV2Signature() or any v2-style signing path (LegacySignature, src/Pay/LegacySignature.php:48-57), or handling v2 XML callbacks whose req_info is decrypted with md5(v2 key) (src/Pay/Server.php:223-226), while the pay config has no `v2_secret_key` set — Application.php:46 casts the missing value to an empty string, so getV2SecretKey() returns '' and empty() trips.
Common situations: Config only sets v3 credentials (secret_key, private_key, certificate) but a refund, red-pack or other legacy endpoint still uses v2; key pasted under the wrong config name; .env value not loaded or config cache stale in one environment; secrets manager returning an empty string.
Related errors
- No platform certs found for serial: {$serial},
- Invalid Signature
- -40001
- Encrypt AES ECB failed.
- Read the $certificate failed, please check it whether or nor
AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21).
Data as JSON: /api/errors/140e8b948e3a68cf.
Report an issue: GitHub.