JeffreySu/WeiXinMPSDK · error · ArgumentException

预签名请求必须同时提供 nonce_str 和 ts。

Error message

预签名请求必须同时提供 nonce_str 和 ts。

What it means

When a PayTool request already carries a signature (sig is non-empty), PrepareRequest requires the companion fields nonce_str and ts so the signature can be validated; it throws ArgumentException (预签名请求必须同时提供 nonce_str 和 ts。) if either nonce_str is empty or ts <= 0.

Solutions

  1. Set both request.nonce_str (random string) and request.ts (current Unix timestamp in seconds) whenever request.sig is provided
  2. If the request is not intentionally pre-signed, clear request.sig so PrepareRequest signs it using payToolApiSecret instead
  3. Check deserialization/binding so ts is correctly populated as a positive integer

Example fix

// before
req.sig = ComputeSig(req); // sig set, but nonce_str/ts empty
PayToolSignatureHelper.PrepareRequest(req, apiSecret);
// after
req.nonce_str = Guid.NewGuid().ToString("N");
req.ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
req.sig = ComputeSig(req);
PayToolSignatureHelper.PrepareRequest(req, apiSecret);
Defensive patterns

Strategy: validation

Validate before calling

bool presignOk = string.IsNullOrEmpty(req.sig) || (!string.IsNullOrEmpty(req.nonce_str) && req.ts > 0);
if (!presignOk) throw new InvalidOperationException("Pre-signed requests need both nonce_str and ts");

Type guard

bool IsPreSigned(PayToolSignedRequestBase r) => !string.IsNullOrEmpty(r.sig);
bool PreSignFieldsValid(PayToolSignedRequestBase r) => IsPreSigned(r) && !string.IsNullOrEmpty(r.nonce_str) && r.ts > 0;

Try / catch

try { PayToolSignatureHelper.PrepareRequest(req, apiSecret); }
catch (ArgumentException ex) when (ex.Message.Contains("nonce_str")) { logger.LogError(ex, "Pre-sign fields incomplete"); req.sig = null; PayToolSignatureHelper.PrepareRequest(req, apiSecret); }

Prevention

When it happens

Trigger: Calling PrepareRequest on a request where request.sig is set but request.nonce_str is null/empty or request.ts is 0/negative — e.g. manually setting sig without filling nonce_str/ts, or deserializing a partially filled request.

Common situations: Reusing a request object across retries where nonce_str/ts were cleared; hand-crafting pre-signed requests in tests; binding from a form/JSON where ts was sent as a string and defaulted to 0; forgetting to set ts (Unix seconds) when supplying sig manually.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at src/Senparc.Weixin.Work/Senparc.Weixin.Work/AdvancedAPIs/PayTool/PayToolSignatureHelper.cs:47

        /// <summary>
        /// 为收款工具请求补齐随机串、Unix 时间戳和数字签名。
        /// 已提供签名时不会重新签名,并要求调用方同时提供原签名对应的随机串和时间戳。
        /// </summary>
        /// <param name="request">需要签名的收款工具请求。</param>
        /// <param name="payToolApiSecret">收银台 API 调用密钥;请求未预签名时必填。</param>
        public static void PrepareRequest(PayToolSignedRequestBase request,
            string payToolApiSecret)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (!string.IsNullOrEmpty(request.sig))
            {
                if (string.IsNullOrEmpty(request.nonce_str) || request.ts <= 0)
                {
                    throw new ArgumentException("预签名请求必须同时提供 nonce_str 和 ts。",
                        nameof(request));
                }

                return;
            }

            if (string.IsNullOrEmpty(payToolApiSecret))
            {
                throw new ArgumentException("请求未提供 sig 时必须提供收银台 API 调用密钥。",
                    nameof(payToolApiSecret));
            }

            if (string.IsNullOrEmpty(request.nonce_str))
            {
                request.nonce_str = Guid.NewGuid().ToString("N");
            }

            if (request.ts <= 0)

View on GitHub (pinned to be573f6f94)