JeffreySu/WeiXinMPSDK · error · TenpaySecurityException

公钥序列号不存在!请查看日志!

Error message

公钥序列号不存在!请查看日志!

What it means

TenPayV3Info.GetPublicKeyAsync looks up the public key registered for the given serialNumber in the local key cache; if no entry matches, it first logs a TenpaySecurityException with the serial and merchant IDs (deliberately excluding secrets) and then throws TenpaySecurityException '公钥序列号不存在!请查看日志!'.

Solutions

  1. Check the log line for the exact serialNumber/MchId and refresh the public key list from WeChat Pay (merchant platform or API), then retry.
  2. Enable/confirm public key mode (TenPayV3_TenPayPubKeyEnable) and ensure the 微信支付公钥 is configured and loaded at startup.
  3. Add fallback logic to fetch and cache keys on unknown serials instead of failing permanently.
  4. Verify you are not mixing key material across merchant accounts (MchId/SubMchId).

Example fix

// before
var key = await info.GetPublicKeyAsync("OLD-SERIAL"); // throws
// after
await info.RefreshPublicKeysAsync(); // re-sync keys from WeChat Pay
var key = await info.GetPublicKeyAsync(serialFromHeader);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!await publicKeyCache.ContainsSerialAsync(serialNumber))
    await publicKeyCache.RefreshFromWeChatPayAsync(); // fetch before use

Try / catch

try { var key = await info.GetPublicKeyAsync(serial); }
catch (TenpaySecurityException ex)
{ logger.LogWarning(ex, "Unknown public key serial {Serial}", serial); await info.RefreshPublicKeysAsync(); /* retry once */ }

Prevention

When it happens

Trigger: Verifying a request/response whose Wechatpay-Serial refers to a public key version that was never downloaded/registered locally — e.g. right after WeChat Pay rotated keys, or before the first successful key fetch.

Common situations: WeChat Pay published a new public key and callbacks now arrive with the new serial; app restarted and the key cache is cold; multi-tenant setup where the key belongs to a different MchId/SubMchId; public-key mode not enabled (TenPayV3_TenPayPubKeyEnable) so keys were never populated.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of JeffreySu/WeiXinMPSDK@be573f6f94 (2026-09-12). Data as JSON: /api/errors/987291c0e3bbe0b1. Report an issue: GitHub.

Appendix: source

Thrown at src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3/TenPayV3Info.cs:351

        public async Task<string> GetPublicKeyAsync(string serialNumber, ISenparcWeixinSettingForTenpayV3 tenpayV3Setting, CancellationToken cancellationToken)
        {
            var keys = await GetPublicKeysAsync(tenpayV3Setting, cancellationToken).ConfigureAwait(false);
            if (keys.TryGetValue(serialNumber, out string publicKey))
            {
                return publicKey;
            }

            // 平台证书/公钥可能刚完成轮换;未命中时立即刷新一次,而不是等待常规缓存过期。
            keys = await RefreshPublicKeysAfterMissAsync(keys, tenpayV3Setting, cancellationToken).ConfigureAwait(false);
            if (keys != null && keys.TryGetValue(serialNumber, out publicKey))
            {
                return publicKey;
            }

            // 日志仅记录定位所需标识,禁止序列化整个对象(其中包含私钥、APIv3 Key 等敏感信息)。
            SenparcTrace.BaseExceptionLog(new TenpaySecurityException(
                $"公钥序列号不存在!serialNumber:{serialNumber},MchId:{MchId},SubMchId:{Sub_MchId}"));
            throw new TenpaySecurityException("公钥序列号不存在!请查看日志!", true);
        }
    }
}

View on GitHub (pinned to be573f6f94)