{"record":{"id":"e55710d608c35485","repo":"JeffreySu/WeiXinMPSDK","slug":"api","errorCode":null,"errorMessage":"品牌 API 鉴权参数不能为空。","messagePattern":"品牌 API 鉴权参数不能为空。","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3/HttpHandlers/TenPayBrandApiCredentials.cs","lineNumber":92,"sourceCode":"        /// 品牌 API 证书对应的 PKCS#8 RSA 私钥 Base64 DER。\n        /// </summary>\n        public string BrandPrivateKey { get; }\n\n        /// <summary>\n        /// 品牌 ID 关联的微信支付公钥 ID。\n        /// </summary>\n        public string WechatpayPublicKeyId { get; }\n\n        /// <summary>\n        /// 用于校验品牌 API 响应签名的微信支付公钥 Base64 DER。\n        /// </summary>\n        public string WechatpayPublicKey { get; }\n\n        private static string RequireValue(string value, string parameterName)\n        {\n            if (string.IsNullOrWhiteSpace(value))\n            {\n                throw new ArgumentException(\"品牌 API 鉴权参数不能为空。\",\n                    parameterName);\n            }\n\n            return value;\n        }\n\n        private static string NormalizeKey(string value, string parameterName)\n        {\n            var requiredValue = RequireValue(value, parameterName);\n            var builder = new StringBuilder();\n            using (var reader = new StringReader(requiredValue))\n            {\n                string line;\n                while ((line = reader.ReadLine()) != null)\n                {\n                    var trimmedLine = line.Trim();\n                    if (trimmedLine.StartsWith(\"-----\",\n                        StringComparison.Ordinal))","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/JeffreySu/WeiXinMPSDK/blob/be573f6f94bdbf718dd5f6cdecb137fbc7ff651e/src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3/HttpHandlers/TenPayBrandApiCredentials.cs#L74-L110","documentation":"TenPayBrandApiCredentials.RequireValue validates every constructor parameter (merchant ID, serial, keys, etc.) and throws ArgumentException when a value is null, empty, or whitespace. The constructor refuses to build brand API credentials with missing authentication material.","triggerScenarios":"Constructing TenPayBrandApiCredentials with null/empty/whitespace for any required string parameter — e.g. reading mchId or publicKey from configuration that returned empty, or a variable not yet initialized.","commonSituations":"Missing appsettings entries for brand API keys; environment variable not set in the deployment environment; secrets not loaded from the key vault; accidentally passing the wrong variable (null).","solutions":["Check the specific parameterName in the ArgumentException message to identify which field is missing.","Verify the config source (appsettings.json, env var, key vault) actually contains the value in the deployed environment.","Validate all credential strings with string.IsNullOrWhiteSpace before constructing the credentials object.","If credentials are optional, only construct TenPayBrandApiCredentials when all values are present."],"exampleFix":"// before\nvar creds = new TenPayBrandApiCredentials(config[\"MchId\"], config[\"Serial\"], config[\"PublicKey\"], config[\"PrivateKey\"]); // values may be null\n// after\nvar mchId = config[\"MchId\"] ?? throw new InvalidOperationException(\"MchId not configured\");\nvar serial = config[\"Serial\"] ?? throw new InvalidOperationException(\"Serial not configured\");\nvar publicKey = config[\"PublicKey\"] ?? throw new InvalidOperationException(\"PublicKey not configured\");\nvar privateKey = config[\"PrivateKey\"] ?? throw new InvalidOperationException(\"PrivateKey not configured\");\nvar creds = new TenPayBrandApiCredentials(mchId, serial, publicKey, privateKey);","handlingStrategy":"validation","validationCode":"string Require(string? v, string name) =>\n    string.IsNullOrWhiteSpace(v) ? throw new InvalidOperationException($\"{name} is not configured.\") : v;\nvar creds = new TenPayBrandApiCredentials(Require(cfg[\"MchId\"],\"MchId\"), Require(cfg[\"Serial\"],\"Serial\"), Require(cfg[\"PublicKey\"],\"PublicKey\"), Require(cfg[\"PrivateKey\"],\"PrivateKey\"));","typeGuard":"bool HasBrandConfig(ISenparcWeixinSettingForTenpayV3 s) =>\n    !string.IsNullOrWhiteSpace(s?.TenPayV3_MchId) && !string.IsNullOrWhiteSpace(s?.TenPayV3_Key);","tryCatchPattern":"try { creds = new TenPayBrandApiCredentials(mchId, serial, publicKey, privateKey); }\ncatch (ArgumentException ex) { logger.LogError(ex, \"Missing brand credential: {Param}\", ex.ParamName); throw; }","preventionTips":["Fail fast at startup: validate all credential config in app bootstrap.","Use ex.ParamName to identify the missing field.","Ensure secrets are present in every deployment environment (CI, staging, prod).","Never pass possibly-null config indexer results directly into constructors."],"tags":["configuration","validation","null-argument"],"backgroundTag":"empty-required-field","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"}