JeffreySu/WeiXinMPSDK · error · InvalidDataException

通知正文中缺少加密资源 resource。

Error message

通知正文中缺少加密资源 resource。

What it means

The decrypted resource must be present in the notification JSON (NotifyRequest.resource); when the parsed body has no resource object, DecryptBrandGetObjectAsync throws InvalidDataException "通知正文中缺少加密资源 resource。". This indicates the notification body is not the expected encrypted brand resource payload.

Solutions

  1. Verify the notification event_type actually carries an encrypted resource before decrypting
  2. Re-read the body with EnableBuffering so deserialization isn't operating on an empty stream
  3. Return 200/OK handling for notifications without resource instead of treating them as brand data
  4. Log NotifyRequest JSON when resource is missing to diagnose the payload shape
  5. Check that the callback URL is only registered for events that include encrypted resources

Example fix

// before
var obj = await handler.DecryptBrandGetObjectAsync<T>(apiKey, creds);
// after
if (handler.NotifyRequest?.resource == null)
{
    return Ok(); // 非加密资源通知,直接确认
}
var obj = await handler.DecryptBrandGetObjectAsync<T>(apiKey, creds);
Defensive patterns

Strategy: try-catch

Validate before calling

if (handler.NotifyRequest?.resource is null) return Ok(); // 非加密资源通知

Type guard

bool HasEncryptedResource(NotificationRequest? r) => r?.resource is not null;

Try / catch

try { var obj = await handler.DecryptBrandGetObjectAsync<T>(apiKey, creds); }
catch (InvalidDataException ex) when (ex.Message.Contains("缺少加密资源"))
{ logger.LogWarning(ex, "通知缺少 resource"); return Ok(); }

Prevention

When it happens

Trigger: Notification body deserialized into NotifyRequest without a resource field — wrong event type, empty/failed POST body read, or an ACK/verification request routed into the decrypt method.

Common situations: WeChat sends non-encrypted notifications (e.g. duplicate-notify ACKs) to the same endpoint, malformed JSON, or the body was consumed before deserialization.

Related errors


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

Appendix: source

Thrown at src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3/HttpHandlers/TenPayNotifyHandler.cs:360

        /// <param name="brandApiKey">品牌 API 密钥。</param>
        /// <param name="brandApiCredentials">品牌 API 鉴权凭据,其中包含回调验签所需的微信支付公钥。</param>
        /// <param name="nonce">加密随机串;为空时读取通知资源中的值。</param>
        /// <param name="associatedData">附加数据;为空时读取通知资源中的值。</param>
        /// <returns>验签并解密后的品牌通知。</returns>
        public Task<T> DecryptBrandGetObjectAsync<T>(string brandApiKey,
            TenPayBrandApiCredentials brandApiCredentials,
            string nonce = null, string associatedData = null)
            where T : ReturnJsonBase, new()
        {
            if (string.IsNullOrWhiteSpace(brandApiKey))
            {
                throw new ArgumentException("品牌 API 密钥不能为空。",
                    nameof(brandApiKey));
            }

            _ = 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 与配置不匹配。");
            }

View on GitHub (pinned to be573f6f94)