JeffreySu/WeiXinMPSDK · error · TenpayApiRequestException

RequestAsync 签名验证失败:

Error message

RequestAsync 签名验证失败:

What it means

VerifyResponseMessage verifies the Wechatpay-Signature on the API response using the public key resolved for the Wechatpay-Serial. Any failure inside that process (missing/unknown serial, bad public key, signature mismatch, network error fetching the key) is wrapped into a TenpayApiRequestException with the prefix 'RequestAsync 签名验证失败:' followed by the inner exception message.

Solutions

  1. Read the inner exception message to identify the root cause (unknown serial vs. signature mismatch).
  2. Update/download the current WeChat Pay platform certificates or public key so the serial in Wechatpay-Serial can be resolved.
  3. Ensure no proxy rewrites the response body or strips Wechatpay-* headers; bypass intermediaries to test.
  4. If TenPayV3_TenPayPubKeyEnable is toggled, confirm it matches your merchant platform mode (public key mode vs certificate mode).

Example fix

// before (catching generic)
catch (Exception ex) { log(ex.Message); }
// after
catch (TenpayApiRequestException ex)
{
    log("Response signature verify failed: " + ex.InnerException?.Message);
    // refresh platform certs / public key, then retry
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure key material is current before the call
var serial = response.Headers.TryGetValues("Wechatpay-Serial", out var v) ? v.First() : null;
if (serial != null && !await keyStore.ContainsSerialAsync(serial)) await keyStore.RefreshAsync();

Try / catch

try { var result = await client.SendAsync(...); }
catch (TenpayApiRequestException ex) when (ex.Message.StartsWith("RequestAsync 签名验证失败"))
{ logger.LogWarning(ex.InnerException, "Response signature verification failed"); /* refresh certs, retry once */ }

Prevention

When it happens

Trigger: Calling any TenPayV3 API through SendAsync where the response signature cannot be verified: the Wechatpay-Serial does not match a known key, GetAPIv3PublicKeyAsync fails or returns the wrong key, the response body was altered/proxied, or the timestamp/nonce headers were missing.

Common situations: A reverse proxy or gateway rewrites the response (breaking the signature); merchant switched between platform certificates and public key mode so the serial lookup fails; expired platform certificate after WeChat rotation; clock skew making timestamp verification fail.

Related errors


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

Appendix: source

Thrown at src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3/TenPayHttpClient/TenPayHttpClient.cs:295

        {
            return VerifyResponseMessage(responseMessage, content, CancellationToken.None);
        }

        protected async Task<bool> VerifyResponseMessage(HttpResponseMessage responseMessage, string content, CancellationToken cancellationToken)
        {
            var wechatpayTimestamp = responseMessage.Headers.GetValues("Wechatpay-Timestamp").First();
            var wechatpayNonce = responseMessage.Headers.GetValues("Wechatpay-Nonce").First();
            var wechatpaySignatureBase64 = responseMessage.Headers.GetValues("Wechatpay-Signature").First();//后续需要base64解码
            var wechatpaySerial = responseMessage.Headers.GetValues("Wechatpay-Serial").First();

            try
            {
                var pubKey = await TenPayV3InfoCollection.GetAPIv3PublicKeyAsync(this._tenpayV3Setting, wechatpaySerial, cancellationToken).ConfigureAwait(false);
                return _verifier.Verify(wechatpayTimestamp, wechatpayNonce, wechatpaySignatureBase64, content, pubKey, this._tenpayV3Setting.TenPayV3_TenPayPubKeyEnable);
            }
            catch (Exception ex)
            {
                throw new TenpayApiRequestException("RequestAsync 签名验证失败:" + ex.Message, ex);
            }
        }

        /// <summary>
        /// 获取实例
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="throwIfFaild"></param>
        /// <returns></returns>
        private T GetInstance<T>(bool throwIfFaild)
            where T : ReturnJsonBase
        {
            if (typeof(T).IsClass)
            {
                return Senparc.CO2NET.Helpers.ReflectionHelper.CreateInstance<T>(typeof(T).FullName, typeof(T).Assembly.GetName().Name);
            }
            else if (throwIfFaild)
            {

View on GitHub (pinned to be573f6f94)