{"record":{"id":"63df3493956901da","repo":"JeffreySu/WeiXinMPSDK","slug":"sig-api","errorCode":null,"errorMessage":"请求未提供 sig 时必须提供收银台 API 调用密钥。","messagePattern":"请求未提供 sig 时必须提供收银台 API 调用密钥。","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Senparc.Weixin.Work/Senparc.Weixin.Work/AdvancedAPIs/PayTool/PayToolSignatureHelper.cs","lineNumber":56,"sourceCode":"            if (request == null)\n            {\n                throw new ArgumentNullException(nameof(request));\n            }\n\n            if (!string.IsNullOrEmpty(request.sig))\n            {\n                if (string.IsNullOrEmpty(request.nonce_str) || request.ts <= 0)\n                {\n                    throw new ArgumentException(\"预签名请求必须同时提供 nonce_str 和 ts。\",\n                        nameof(request));\n                }\n\n                return;\n            }\n\n            if (string.IsNullOrEmpty(payToolApiSecret))\n            {\n                throw new ArgumentException(\"请求未提供 sig 时必须提供收银台 API 调用密钥。\",\n                    nameof(payToolApiSecret));\n            }\n\n            if (string.IsNullOrEmpty(request.nonce_str))\n            {\n                request.nonce_str = Guid.NewGuid().ToString(\"N\");\n            }\n\n            if (request.ts <= 0)\n            {\n                request.ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds();\n            }\n\n            request.sig = CreateSignature(request, payToolApiSecret);\n        }\n\n        /// <summary>\n        /// 按企业微信规则递归展开非空叶子参数、按完整 key=value 字符串升序排列，","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/JeffreySu/WeiXinMPSDK/blob/be573f6f94bdbf718dd5f6cdecb137fbc7ff651e/src/Senparc.Weixin.Work/Senparc.Weixin.Work/AdvancedAPIs/PayTool/PayToolSignatureHelper.cs#L38-L74","documentation":"PrepareRequest throws this ArgumentException when the outgoing PayTool (企业微信收银台) request does not carry a sig parameter and no payToolApiSecret was supplied. The library needs the cashier API secret to compute the signature itself; without sig or a secret the request cannot be authenticated by WeChat Work. It is thrown before the request is sent.","triggerScenarios":"Calling a PayTool API through PrepareRequest with request.sig left empty and passing payToolApiSecret as null, empty string, or whitespace (e.g. reading the secret from an unpopulated config field).","commonSituations":"Developers omit the cashier API secret in appsettings/environment config, or pass an empty string because the secret was never issued/configured for their WeChat Work merchant, or rely on sig-in-request mode but forgot to actually sign the request.","solutions":["Pass a non-empty payToolApiSecret to PrepareRequest so the library can generate the sig.","If you sign requests yourself, set request.sig to a valid Base64 signature before calling.","Verify the secret value is actually loaded from configuration (not null/empty) before invoking the API."],"exampleFix":"// before\nPayToolApi.PrepareRequest(request, payToolApiSecret: \"\");\n// after\nif (string.IsNullOrWhiteSpace(secret)) throw new InvalidOperationException(\"配置缺少收银台 API 密钥\");\nPayToolApi.PrepareRequest(request, payToolApiSecret: secret);","handlingStrategy":"validation","validationCode":"bool canCall = !string.IsNullOrWhiteSpace(payToolApiSecret) || !string.IsNullOrEmpty(request.sig);","typeGuard":null,"tryCatchPattern":"try { PrepareRequest(request, secret); } catch (ArgumentException ex) when (ex.ParamName == nameof(payToolApiSecret)) { logger.LogError(ex, \"缺少收银台 API 密钥\"); throw new ConfigurationException(\"收银台 API 密钥未配置\"); }","preventionTips":["Load the cashier API secret in startup configuration with fail-fast checks.","Never pass empty strings for optional-looking parameters; assert secrets at the config boundary.","Decide on one signing mode (sig in request vs. library-signed) per code path."],"tags":["argument","validation","signature","wework-paytool"],"backgroundTag":"missing-required-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"}