{"record":{"id":"95d66b49589f9915","repo":"JeffreySu/WeiXinMPSDK","slug":"resource-requestmessageevent-miniprogrampay","errorCode":null,"errorMessage":"通知中不包含 resource 节点。","messagePattern":"通知中不包含 resource 节点。","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Senparc.Weixin.Work/Senparc.Weixin.Work/Entities/Request/Event/RequestMessageEvent_MiniProgramPay.cs","lineNumber":77,"sourceCode":"        /// <summary>\n        /// 使用“对外收款”应用回调配置中的 EncodingAESKey 解密业务资源。\n        /// .NET Framework 与 netstandard2.0 不提供平台 AES-GCM 实现，将抛出\n        /// <see cref=\"PlatformNotSupportedException\"/>；这些目标仍可接收完整加密通知模型。\n        /// </summary>\n        public string DecryptResource(string encodingAesKey)\n        {\n#if NET462 || NETSTANDARD2_0\n            throw new PlatformNotSupportedException(\n                \"当前目标框架不提供 AES-GCM。请在 netstandard2.1、netcoreapp3.1 或更新目标中解密通知资源。\");\n#else\n            if (string.IsNullOrEmpty(encodingAesKey) || encodingAesKey.Length != 43)\n            {\n                throw new ArgumentException(\"EncodingAESKey 必须为 43 个字符。\", nameof(encodingAesKey));\n            }\n\n            if (resource == null)\n            {\n                throw new InvalidOperationException(\"通知中不包含 resource 节点。\");\n            }\n\n            if (!string.Equals(resource.algorithm, \"AEAD_AES_256_GCM\", StringComparison.OrdinalIgnoreCase))\n            {\n                throw new NotSupportedException($\"不支持的通知资源加密算法：{resource.algorithm}\");\n            }\n\n            var key = Convert.FromBase64String(encodingAesKey + \"=\");\n            var encrypted = Convert.FromBase64String(resource.ciphertext);\n            const int tagSize = 16;\n            if (encrypted.Length <= tagSize)\n            {\n                throw new CryptographicException(\"通知资源密文长度无效。\");\n            }\n\n            var ciphertext = new byte[encrypted.Length - tagSize];\n            var tag = new byte[tagSize];\n            var plaintext = new byte[ciphertext.Length];","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/JeffreySu/WeiXinMPSDK/blob/be573f6f94bdbf718dd5f6cdecb137fbc7ff651e/src/Senparc.Weixin.Work/Senparc.Weixin.Work/Entities/Request/Event/RequestMessageEvent_MiniProgramPay.cs#L59-L95","documentation":"The notification carries no `resource` node, which is where the encrypted business payload lives, so there is nothing to decrypt. The library throws InvalidOperationException to signal the message entity was populated without the required encrypted payload.","triggerScenarios":"Calling DecryptResource on a RequestMessageEvent_MiniProgramPay whose Resource property is null — e.g. the entity was created manually or deserialized from a message without the resource field.","commonSituations":"Testing with hand-crafted callback XML/JSON missing the resource block, or handling a different event type whose payload is unencrypted and mistakenly calling DecryptResource on it.","solutions":["Check resource != null before calling DecryptResource and skip decryption when absent","Verify the incoming notification actually is an encrypted mini-program payment event by inspecting the raw callback body","If parsing from XML, confirm the deserialization maps the resource node onto the entity correctly"],"exampleFix":"// before\nvar plain = message.DecryptResource(aesKey);\n// after\nvar plain = message.Resource == null ? null : message.DecryptResource(aesKey);","handlingStrategy":"validation","validationCode":"if (message.Resource == null)\n{\n    log.Info(\"Notification has no resource node; skipping decryption\");\n    return;\n}","typeGuard":"static bool HasDecryptableResource(RequestMessageEvent_MiniProgramPay m) => m?.Resource != null;","tryCatchPattern":"try { var plain = msg.DecryptResource(aesKey); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"resource\")) { log.Warn(\"No resource node in notification\"); }","preventionTips":["Check Resource before decrypting; some events ship without an encrypted payload","Validate the raw callback body contains the resource block during webhook integration testing","Keep deserialization mappings for the resource node up to date with SDK upgrades"],"tags":["null-argument","payload-validation","cryptography"],"backgroundTag":"null-argument","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"}