{"record":{"id":"5c1a683599a05db1","repo":"JeffreySu/WeiXinMPSDK","slug":"resource","errorCode":null,"errorMessage":"通知正文中缺少加密资源 resource。","messagePattern":"通知正文中缺少加密资源 resource。","errorType":"exception","errorClass":"InvalidDataException","httpStatus":null,"severity":"error","filePath":"src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3/HttpHandlers/TenPayNotifyHandler.cs","lineNumber":360,"sourceCode":"        /// <param name=\"brandApiKey\">品牌 API 密钥。</param>\n        /// <param name=\"brandApiCredentials\">品牌 API 鉴权凭据，其中包含回调验签所需的微信支付公钥。</param>\n        /// <param name=\"nonce\">加密随机串；为空时读取通知资源中的值。</param>\n        /// <param name=\"associatedData\">附加数据；为空时读取通知资源中的值。</param>\n        /// <returns>验签并解密后的品牌通知。</returns>\n        public Task<T> DecryptBrandGetObjectAsync<T>(string brandApiKey,\n            TenPayBrandApiCredentials brandApiCredentials,\n            string nonce = null, string associatedData = null)\n            where T : ReturnJsonBase, new()\n        {\n            if (string.IsNullOrWhiteSpace(brandApiKey))\n            {\n                throw new ArgumentException(\"品牌 API 密钥不能为空。\",\n                    nameof(brandApiKey));\n            }\n\n            _ = brandApiCredentials ?? throw new ArgumentNullException(\n                nameof(brandApiCredentials));\n            var resource = NotifyRequest?.resource ?? throw new InvalidDataException(\n                \"通知正文中缺少加密资源 resource。\");\n\n            var wechatpayTimestamp =\n                _httpContext.Request.Headers?[\"Wechatpay-Timestamp\"].ToString();\n            var wechatpayNonce =\n                _httpContext.Request.Headers?[\"Wechatpay-Nonce\"].ToString();\n            var wechatpaySignature =\n                _httpContext.Request.Headers?[\"Wechatpay-Signature\"].ToString();\n            var wechatpaySerial =\n                _httpContext.Request.Headers?[\"Wechatpay-Serial\"].ToString();\n\n            if (!string.Equals(wechatpaySerial,\n                brandApiCredentials.WechatpayPublicKeyId,\n                StringComparison.Ordinal))\n            {\n                throw new InvalidOperationException(\n                    \"品牌 API 通知的微信支付公钥 ID 与配置不匹配。\");\n            }","sourceCodeStart":342,"sourceCodeEnd":378,"githubUrl":"https://github.com/JeffreySu/WeiXinMPSDK/blob/be573f6f94bdbf718dd5f6cdecb137fbc7ff651e/src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3/HttpHandlers/TenPayNotifyHandler.cs#L342-L378","documentation":"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.","triggerScenarios":"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.","commonSituations":"WeChat sends non-encrypted notifications (e.g. duplicate-notify ACKs) to the same endpoint, malformed JSON, or the body was consumed before deserialization.","solutions":["Verify the notification event_type actually carries an encrypted resource before decrypting","Re-read the body with EnableBuffering so deserialization isn't operating on an empty stream","Return 200/OK handling for notifications without resource instead of treating them as brand data","Log NotifyRequest JSON when resource is missing to diagnose the payload shape","Check that the callback URL is only registered for events that include encrypted resources"],"exampleFix":"// before\nvar obj = await handler.DecryptBrandGetObjectAsync<T>(apiKey, creds);\n// after\nif (handler.NotifyRequest?.resource == null)\n{\n    return Ok(); // 非加密资源通知，直接确认\n}\nvar obj = await handler.DecryptBrandGetObjectAsync<T>(apiKey, creds);","handlingStrategy":"try-catch","validationCode":"if (handler.NotifyRequest?.resource is null) return Ok(); // 非加密资源通知","typeGuard":"bool HasEncryptedResource(NotificationRequest? r) => r?.resource is not null;","tryCatchPattern":"try { var obj = await handler.DecryptBrandGetObjectAsync<T>(apiKey, creds); }\ncatch (InvalidDataException ex) when (ex.Message.Contains(\"缺少加密资源\"))\n{ logger.LogWarning(ex, \"通知缺少 resource\"); return Ok(); }","preventionTips":["Check event_type before decryption","Enable request body buffering to avoid empty re-reads","Log full notification JSON when resource is absent to detect event routing mistakes"],"tags":["tenpay","decryption","unexpected-response-shape"],"backgroundTag":"unexpected-response-shape","analyzedSha":"be573f6f94bdbf718dd5f6cdecb137fbc7ff651e","analyzedAt":"2026-09-12T10:01:50.733Z","contentChangedAt":"2026-09-12T10:01:50.733Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}