JeffreySu/WeiXinMPSDK · error · TenpayApiRequestException

必须等于 乘以 !

Error message

{nameof(data.stock_use_rule.max_amount)} 必须等于 {nameof(data.stock_use_rule.max_coupons)} 乘以 {nameof(data.coupon_use_rule.fixed_normal_coupon.coupon_amount)}!

What it means

CreateStockAsync (MarketingApis.Favor.cs:78) enforces the WeChat rule that a NORMAL stock's total budget (stock_use_rule.max_amount) must equal max_coupons × fixed_normal_coupon.coupon_amount. This arithmetic is validated client-side and throws TenpayApiRequestException when the numbers are inconsistent, because the API would reject the mismatched budget.

Solutions

  1. Recompute and set stock_use_rule.max_amount = max_coupons * coupon_amount before calling
  2. Ensure coupon_amount and transaction_minimum are both in fen (cents), and the budget is computed from those fen values
  3. Add a guard/validation in the code that builds the request to derive max_amount from the other two values

Example fix

// before
stockUseRule.max_amount = 1000;   // mismatch
stockUseRule.max_coupons = 500;
couponUseRule.fixed_normal_coupon.coupon_amount = 100;

// after
stockUseRule.max_coupons = 500;
couponUseRule.fixed_normal_coupon.coupon_amount = 100; // fen
stockUseRule.max_amount = stockUseRule.max_coupons * couponUseRule.fixed_normal_coupon.coupon_amount; // 50000
Defensive patterns

Strategy: validation

Validate before calling

if (data.stock_use_rule.max_amount != data.stock_use_rule.max_coupons * data.coupon_use_rule.fixed_normal_coupon.coupon_amount)
    throw new InvalidOperationException("max_amount must equal max_coupons * coupon_amount");

Type guard

bool BudgetMatchesCoupons(CreateStockRequsetData d) =>
    d.stock_use_rule.max_amount == d.stock_use_rule.max_coupons * (d.coupon_use_rule.fixed_normal_coupon?.coupon_amount ?? 0);

Try / catch

try { await api.CreateStockAsync(data); }
catch (TenpayApiRequestException ex) when (ex.Message.Contains("max_amount"))
{ logger.LogError(ex, "Stock budget arithmetic mismatch"); throw; }

Prevention

When it happens

Trigger: Calling CreateStockAsync where data.stock_use_rule.max_amount != data.stock_use_rule.max_coupons * data.coupon_use_rule.fixed_normal_coupon.coupon_amount (e.g. budget set independently of coupon count/amount, or units in fen vs yuan mixed up).

Common situations: Entering amounts in yuan while the API expects fen (so 1 yuan coupon × 1000 coupons ≠ 100000 budget typed as 1000); changing max_coupons or coupon_amount after setting max_amount and forgetting to recompute; computing budget with int truncation.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3/Apis/Marketing/MarketingApis.Favor.cs:78

        /// 创建代金券批次接口
        /// <para>调用此接口创建微信支付代金券批次,创建完成后将获得代金券批次id。</para>
        /// <para>更多详细请参考 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_1.shtml </para>
        /// <para>提示:使用此功能必须在后台【产品中心】开通【预充值代金券】功能!</para>
        /// </summary>
        /// <param name="data">微信支付需要POST的Data数据</param>
        /// <param name="timeOut">超时时间,单位为ms </param>
        /// <returns></returns>
        public async Task<CreateStockReturnJson> CreateStockAsync(CreateStockRequsetData data, int timeOut = Config.TIME_OUT)
        {
            const string STOCK_TYPE = "NORMAL";
            if (data.stock_type == STOCK_TYPE && data.coupon_use_rule.fixed_normal_coupon == null)
            {
                throw new TenpayApiRequestException($"当 {nameof(data.stock_type)} 为 {STOCK_TYPE} 时,{nameof(data.coupon_use_rule.fixed_normal_coupon)} 必填!");
            }

            if (data.stock_use_rule.max_amount != data.stock_use_rule.max_coupons * data.coupon_use_rule.fixed_normal_coupon.coupon_amount)
            {
                throw new TenpayApiRequestException($"{nameof(data.stock_use_rule.max_amount)} 必须等于 {nameof(data.stock_use_rule.max_coupons)} 乘以 {nameof(data.coupon_use_rule.fixed_normal_coupon.coupon_amount)}!");
            }

            if (data.coupon_use_rule.fixed_normal_coupon.coupon_amount > data.coupon_use_rule.fixed_normal_coupon.transaction_minimum)
            {
                throw new TenpayApiRequestException($"{nameof(data.coupon_use_rule.fixed_normal_coupon.coupon_amount)} 必须小于等于 {nameof(data.coupon_use_rule.fixed_normal_coupon.transaction_minimum)}!");
            }

            var url = BasePayApis.GetPayApiUrl(Senparc.Weixin.Config.TenPayV3Host + "/{0}v3/marketing/favor/coupon-stocks");
            TenPayApiRequest tenPayApiRequest = new(_tenpayV3Setting);
            return await tenPayApiRequest.RequestAsync<CreateStockReturnJson>(url, data, timeOut);
        }

        /// <summary>
        /// 激活代金券批次接口
        /// <para>制券成功后,通过调用此接口激活批次,如果是预充值代金券,激活时会从商户账户余额中锁定本批次的营销资金</para>
        /// <para>更多详细请参考 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_3.shtml </para>
        /// </summary>
        /// <param name="stock_id">批次号 微信为每个代金券批次分配的唯一id</param>

View on GitHub (pinned to be573f6f94)