JeffreySu/WeiXinMPSDK · error · TenpayApiRequestException

当 为 时, 必填!,且 为null!

Error message

当 {nameof(data.partner.type)} 为 {data.partner.type} 时,{nameof(data.partner.merchant_id)} 必填!,且{nameof(data.partner.merchant_id)}为null!

What it means

BuildPartnershipsAsync (MarketingApis.Partnerships.cs:67) validates the MERCHANT-type partner: merchant_id is required and appid must not be set. If type is "MERCHANT" but appid is populated or merchant_id is null, the library throws TenpayApiRequestException before the API call. (Note: the message text erroneously mentions merchant_id twice — a typo in the library's message — but the condition is about merchant_id being required and appid being null.)

Solutions

  1. Set data.partner.merchant_id to the partner merchant number and clear data.partner.appid when type is "MERCHANT"
  2. If the partner should be appid-identified, keep type "APPID" with appid set and merchant_id null
  3. Add pairing validation in request construction to keep type and identifiers consistent

Example fix

// before
data.partner.type = "MERCHANT";
data.partner.appid = "wx1234567890";  // must be null

// after
data.partner.type = "MERCHANT";
data.partner.appid = null;
data.partner.merchant_id = "2480000000";
Defensive patterns

Strategy: validation

Validate before calling

if (data.partner.type == "MERCHANT" && (data.partner.merchant_id is null || data.partner.appid is not null))
    throw new InvalidOperationException("MERCHANT partner requires merchant_id set and appid null");

Type guard

bool MerchantPartnerValid(Partner p) =>
    p.type != "MERCHANT" || (p.merchant_id != null && p.appid == null);

Try / catch

try { await api.BuildPartnershipsAsync(data); }
catch (TenpayApiRequestException ex) when (ex.Message.Contains("merchant_id"))
{ logger.LogError(ex, "Invalid MERCHANT-type partner identification"); throw; }

Prevention

When it happens

Trigger: Calling BuildPartnershipsAsync with data.partner.type == "MERCHANT" while data.partner.merchant_id is null, or while data.partner.appid is still populated.

Common situations: Switching a previously APPID-typed partner to MERCHANT without clearing appid; forgetting to fill merchant_id for merchant-based partnerships; binding a partner object from config where appid is always present.

Related errors


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

Appendix: source

Thrown at src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3/Apis/Marketing/MarketingApis.Partnerships.cs:67

        #region 委托营销接口

        /// <summary>
        /// 建立合作关系接口
        /// <para>该接口主要为商户提供营销资源的授权能力,可授权给其他商户或小程序,方便商户间的互利合作。</para>
        /// <para>更多详细请参考 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_5_1.shtml </para>
        /// </summary>
        /// <param name="data">微信支付需要POST的Data数据</param>
        /// <param name="timeOut">超时时间,单位为ms </param>
        /// <returns></returns>
        public async Task<BuildPartnershipsReturnJson> BuildPartnershipsAsync(BuildPartnershipsRequestData data, int timeOut = Config.TIME_OUT)
        {
            if (data.partner.type == "APPID" && (data.partner.appid is null || data.partner.merchant_id is not null))
            {
                throw new TenpayApiRequestException($"当 {nameof(data.partner.type)} 为 {data.partner.type} 时,{nameof(data.partner.appid)} 必填!,且{nameof(data.partner.merchant_id)}为null!");
            }
            if (data.partner.type == "MERCHANT" && (data.partner.appid is not null || data.partner.merchant_id is null))
            {
                throw new TenpayApiRequestException($"当 {nameof(data.partner.type)} 为 {data.partner.type} 时,{nameof(data.partner.merchant_id)} 必填!,且{nameof(data.partner.merchant_id)}为null!");
            }

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

        /// <summary>
        /// 终止合作关系接口
        /// <para>该接口主要为商户提供营销资源的终止授权能力,便于商户管理运营现存的合作关系。</para>
        /// <para>更多详细请参考 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_5_2.shtml </para>
        /// </summary>
        /// <param name="data">微信支付需要POST的Data数据</param>
        /// <param name="timeOut">超时时间,单位为ms </param>
        /// <returns></returns>
        public async Task<TerminatePartnershipsReturnJson> TerminatePartnershipsAsync(TerminatePartnershipsRequestData data, int timeOut = Config.TIME_OUT)
        {

View on GitHub (pinned to be573f6f94)