JeffreySu/WeiXinMPSDK · error · TenpayApiRequestException
当 为 时, 必填!
Error message
当 {nameof(data.stock_type)} 为 {STOCK_TYPE} 时,{nameof(data.coupon_use_rule.fixed_normal_coupon)} 必填! What it means
CreateStockAsync (MarketingApis.Favor.cs:73) validates the coupon-stock creation request locally before calling the WeChat marketing API. For stock_type "NORMAL" (代金券), the coupon_use_rule.fixed_normal_coupon object carries the coupon denomination and rules, so it is mandatory. If stock_type is NORMAL but fixed_normal_coupon is null, the library throws TenpayApiRequestException immediately, avoiding a guaranteed API rejection.
Solutions
- Populate data.coupon_use_rule.fixed_normal_coupon with coupon_amount, transaction_minimum, etc. when stock_type is NORMAL
- Or change stock_type to a value that does not require fixed_normal_coupon (e.g. DISCOUNT) if that matches the intended coupon kind
- Add a pre-call null check / constructor initialization so fixed_normal_coupon is always created with stock_type NORMAL
Example fix
// before
var data = new CreateStockRequsetData { stock_type = "NORMAL", ... }; // fixed_normal_coupon null
// after
data.coupon_use_rule.fixed_normal_coupon = new FixedNormalCoupon {
coupon_amount = 100, transaction_minimum = 1000
}; Defensive patterns
Strategy: validation
Validate before calling
if (data.stock_type == "NORMAL" && data.coupon_use_rule?.fixed_normal_coupon == null)
throw new InvalidOperationException("fixed_normal_coupon is required when stock_type is NORMAL"); Type guard
bool HasNormalCouponRule(CreateStockRequsetData d) =>
d?.stock_type != "NORMAL" || d.coupon_use_rule?.fixed_normal_coupon != null; Try / catch
try { await api.CreateStockAsync(data); }
catch (TenpayApiRequestException ex) when (ex.Message.Contains("fixed_normal_coupon"))
{ logger.LogError(ex, "NORMAL stock created without fixed_normal_coupon"); throw; } Prevention
- Always initialize coupon_use_rule.fixed_normal_coupon when stock_type is NORMAL
- Centralize coupon-stock request building in one factory method with these invariants
- Write unit tests mirroring the library's pre-flight validations
When it happens
Trigger: Calling CreateStockAsync with CreateStockRequsetData.stock_type set to "NORMAL" while leaving data.coupon_use_rule.fixed_normal_coupon null (or unset) in the request object.
Common situations: Building the request from deserialized/partial JSON where only discount-type rules were populated; copying sample code for a DISCOUNT stock but changing stock_type to NORMAL without adding fixed_normal_coupon; model binder creating coupon_use_rule but leaving fixed_normal_coupon null.
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/ef7453271355efa6.
Report an issue: GitHub.
Appendix: source
Thrown at src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3/Apis/Marketing/MarketingApis.Favor.cs:73
public partial class MarketingApis
{
#region 代金券接口
/// <summary>
/// 创建代金券批次接口
/// <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>View on GitHub (pinned to be573f6f94)