JeffreySu/WeiXinMPSDK · error · InvalidOperationException
品牌 API 通知的微信支付公钥 ID 与配置不匹配。
Error message
品牌 API 通知的微信支付公钥 ID 与配置不匹配。
What it means
TenPayV3's brand-API notify handler reads the Wechatpay-Serial header from the incoming callback and compares it (Ordinal, exact) with the WechatpayPublicKeyId configured in brandApiCredentials. When the public key ID presented by WeChat Pay does not equal the configured one, decryption of the callback resource is refused and this InvalidOperationException is thrown to prevent decrypting/verifying with the wrong key.
Solutions
- Log in to WeChat Pay merchant platform and copy the current 微信支付公钥 ID (Pub Key ID) exactly into brandApiCredentials.WechatpayPublicKeyId.
- Check the Wechatpay-Serial header of the failing notification against your configured value to confirm which key version WeChat is using.
- If you support multiple key versions, update the handler/config to look up the verifier by the serial from the header instead of a single fixed ID.
- Ensure the configured value has no whitespace/BOM and matches case exactly (the comparison is StringComparison.Ordinal).
Example fix
// before brandApiCredentials.WechatpayPublicKeyId = "OLD-PUB-KEY-ID"; // after brandApiCredentials.WechatpayPublicKeyId = "PUB_KEY_ID_0114xxxxxxxx"; // current value from WeChat Pay merchant platform
Defensive patterns
Strategy: validation
Validate before calling
var serial = request.Headers["Wechatpay-Serial"].ToString();
if (!string.Equals(serial, brandApiCredentials.WechatpayPublicKeyId, StringComparison.Ordinal))
return Results.StatusCode(500); // reject before calling DecryptBrandGetObjectAsync Try / catch
try { var data = await handler.DecryptBrandGetObjectAsync(...); }
catch (InvalidOperationException ex) { logger.LogWarning(ex, "Wechatpay serial mismatch"); return Results.StatusCode(500); } Prevention
- Keep the WechatpayPublicKeyId in config sourced directly from the merchant platform, without manual retyping.
- Support multiple key versions by looking up keys by the incoming serial rather than a single fixed ID.
- Monitor WeChat Pay announcements for public key rotation.
When it happens
Trigger: An inbound brand API notification (DecryptBrandGetObjectAsync) whose Wechatpay-Serial header differs from brandApiCredentials.WechatpayPublicKeyId — e.g. WeChat rotated to a new public key, the merchant platform switched between platform certificate mode and public key mode, or the configured key ID is stale/typo'd.
Common situations: Merchant enabled the new '微信支付公钥' mode but the app still configures an old public key ID; WeChat Pay published a new public key version; config copied from a test merchant account into production; key ID string copied with whitespace or wrong case (Ordinal comparison).
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- 证书中未包含 RSA 公钥。
- RequestAsync 签名验证失败:
- 证书中未包含 RSA 公钥。
- WeixinPayInfoCollection尚未注册Partner:
- TenPayV3InfoCollection尚未注册Mch:
AI-assisted analysis of JeffreySu/WeiXinMPSDK@be573f6f94 (2026-09-12).
Data as JSON: /api/errors/0fa50e62a6522ecf.
Report an issue: GitHub.
Appendix: source
Thrown at src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3/HttpHandlers/TenPayNotifyHandler.cs:376
_ = brandApiCredentials ?? throw new ArgumentNullException(
nameof(brandApiCredentials));
var resource = NotifyRequest?.resource ?? throw new InvalidDataException(
"通知正文中缺少加密资源 resource。");
var wechatpayTimestamp =
_httpContext.Request.Headers?["Wechatpay-Timestamp"].ToString();
var wechatpayNonce =
_httpContext.Request.Headers?["Wechatpay-Nonce"].ToString();
var wechatpaySignature =
_httpContext.Request.Headers?["Wechatpay-Signature"].ToString();
var wechatpaySerial =
_httpContext.Request.Headers?["Wechatpay-Serial"].ToString();
if (!string.Equals(wechatpaySerial,
brandApiCredentials.WechatpayPublicKeyId,
StringComparison.Ordinal))
{
throw new InvalidOperationException(
"品牌 API 通知的微信支付公钥 ID 与配置不匹配。");
}
var verifySignSuccess = TenPaySignHelper.VerifyTenpaySign(
CertType.RSA, wechatpayTimestamp, wechatpayNonce,
wechatpaySignature, Body,
brandApiCredentials.WechatpayPublicKey, true);
var decryptedString = SecurityHelper.AesGcmDecryptCiphertext(
brandApiKey, nonce ?? resource.nonce,
associatedData ?? resource.associated_data,
resource.ciphertext);
var result = decryptedString.GetObject<T>();
result.VerifySignSuccess = verifySignSuccess;
result.ResultCode = new TenPayApiResultCode(
$"{_httpContext.Response.StatusCode} / {_httpContext.Request.Method}",
"", "", "", result.VerifySignSuccess == true);
return Task.FromResult(result);View on GitHub (pinned to be573f6f94)