JeffreySu/WeiXinMPSDK · critical · TenpayApiRequestException

RequestAsync 签名验证失败:

Error message

RequestAsync 签名验证失败:

What it means

The response signature verification block in RequestAsyncCore wraps its work in a try/catch and rethrows any failure (key load errors, signature mismatch, missing headers, key ID mismatch) as TenpayApiRequestException with the prefix 'RequestAsync 签名验证失败:' plus the inner exception message. It signals that WeChat Pay's response could not be authenticated.

Solutions

  1. Read the InnerException message to identify the precise cause (unknown serial / bad signature / missing header).
  2. Refresh the WeChat Pay platform certificates (call the certificates download API) so the response serial is known.
  3. If using brand credentials, confirm WechatpayPublicKeyId matches the response serial exactly.
  4. Ensure server clock is NTP-synced (timestamp validation is time-sensitive).
  5. Catch TenpayApiRequestException in your code and fail safely rather than trusting unverified responses.

Example fix

// before
var result = await apiRequest.RequestAsync<JObject>(url, json);
// after
try
{
    var result = await apiRequest.RequestAsync<JObject>(url, json);
}
catch (TenpayApiRequestException ex)
{
    logger.LogError(ex, "WeChat Pay signature verification failed: {Inner}", ex.InnerException?.Message);
    throw; // do not process unverified responses
}
Defensive patterns

Strategy: try-catch

Try / catch

try
{
    var result = await apiRequest.RequestAsync<TResponse>(url, json);
}
catch (TenpayApiRequestException ex)
{
    logger.LogError(ex, "WeChat Pay signature verification failed: {Inner}", ex.InnerException?.Message);
    throw; // fail closed on unverified responses
}

Prevention

When it happens

Trigger: RequestAsync / RequestWithoutBodyAsync where signature verification throws: missing Wechatpay-* headers, serial not found among known platform certificates/public keys, malformed timestamp/nonce, or signature that doesn't verify against the platform key.

Common situations: Platform certificate not downloaded/refreshed (new serial introduced by WeChat rotation); brand key ID mismatch (see error 144); clock skew making timestamp validation fail; corrupted cached certificates.

Related errors


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

Appendix: source

Thrown at src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3/HttpHandlers/TenPayApiRequest.cs:799

                                        var isTenpayPubKey =
                                            TenPaySignHelper.IsPublicKey(
                                                wechatpaySerial);
                                        result.VerifySignSuccess =
                                            TenPaySignHelper
                                                .VerifyTenpaySign(
                                                    _tenpayV3Setting
                                                        .EncryptionType.Value,
                                                    wechatpayTimestamp,
                                                    wechatpayNonce,
                                                    wechatpaySignatureBase64,
                                                    content, pubKey,
                                                    isTenpayPubKey);
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                throw new TenpayApiRequestException("RequestAsync 签名验证失败:" + ex.Message, ex);
                            }
                        }
                    }
                }
                else
                {
                    result = createDefaultInstance?.Invoke() ?? GetInstance<T>(true);
                    resultCode.Additional = content;
                }
                //T result = resultCode.Success ? (await responseMessage.Content.ReadAsStringAsync()).GetObject<T>() : new T();
                result.ResultCode = resultCode;

                return result;
            }
            catch (OperationCanceledException)
            {
                throw;
            }

View on GitHub (pinned to be573f6f94)