PAYATHON 2026

小程序如何关联服务商下的两个子商户进行收款?

支付小周

结论

小程序不需要,也无法在运行时“切换关联”两个子商户。应按以下方式处理:

  1. 分别为两个子商户建立与同一小程序 AppID 的支付授权关系。
  2. 服务端根据当前订单的业务归属,选择对应的 sub_mchid 创建支付订单。
  3. 小程序使用服务端返回的支付参数调用 wx.requestPayment。
  4. 微信支付根据订单中的 sub_mchid 确定交易所属的子商户,并按照该子商户的结算规则处理资金。

同一个小程序 AppID 可以作为两个子商户共同的交易入口。每笔订单实际归哪个收款主体,由下单时传入的子商户号决定,不需要在前端重新绑定小程序。

为什么要这样处理

微信支付服务商模式主要涉及以下身份:

  • sp_appid:服务商关联的小程序 AppID。
  • sp_mchid:服务商商户号。
  • sub_mchid:实际收款的子商户号。
  • sub_appid:子商户关联的 AppID,是否使用取决于具体授权关系和下单方式。

即使两个子商户共用同一个小程序,也需要分别完成支付授权。下单时,微信支付通过 sub_mchid 判断订单属于哪个子商户。

不能让前端直接决定子商户号,否则用户可能篡改请求,导致订单支付给错误的商户。服务端必须根据可信的业务数据确定收款主体。

配置步骤

1. 确认两个子商户均可正常交易

检查两个子商户是否已经完成以下配置:

  • 以服务商模式完成子商户入驻。
  • 开通所需的支付产品。
  • 完成小程序 AppID 的授权或绑定。
  • 配置结算账户及相关资料。
  • 配置 API v3 密钥、平台证书或微信支付公钥等服务端信息。

微信支付后台的入口和菜单名称可能调整,请以当前商户平台显示的内容为准。两个子商户都必须获得通过该小程序 AppID 发起交易的权限。

2. 分别完成 AppID 授权

为两个子商户分别建立授权关系,例如:

小程序 AppID wx1234567890abcdef
    ├── 子商户 A:sub_mchid = 1900000101
    └── 子商户 B:sub_mchid = 1900000102

这不需要把小程序拆成两个,也不用针对每个子商户发布不同的小程序版本。授权完成后,同一个 AppID 就可以在服务商模式下为两个子商户发起支付。

如果无法直接在后台完成绑定,通常需要子商户管理员确认授权。具体流程取决于子商户的入驻方式、支付产品以及当前商户平台的规则。

3. 在业务系统中保存收款主体关系

服务端需要维护门店、订单或商品与子商户号之间的对应关系:

store_a -> 1900000101
store_b -> 1900000102

创建订单时,服务端根据订单所属的门店或业务主体选择 sub_mchid。不要接受小程序端直接提交的子商户号,并将其作为最终收款主体。

例如:

const SUB_MERCHANTS = {
  store_a: '1900000101',
  store_b: '1900000102'
};

function resolveSubMchid(order) {
  const subMchid = SUB_MERCHANTS[order.storeId];

  if (!subMchid) {
    throw new Error('订单未配置有效的收款子商户');
  }

  return subMchid;
}

服务端下单示例

以下示例使用微信支付 API v3 服务商模式的 JSAPI 下单接口:

POST /v3/pay/partner/transactions/jsapi

假设订单属于子商户 A,请求体可以这样组织:

{
  "sp_appid": "wx1234567890abcdef",
  "sp_mchid": "1900000001",
  "sub_mchid": "1900000101",
  "description": "门店A商品订单",
  "out_trade_no": "ORDER_202609130001",
  "notify_url": "https://pay.example.com/wechat/notify",
  "amount": {
    "total": 100,
    "currency": "CNY"
  },
  "payer": {
    "sp_openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
  }
}

订单属于子商户 B 时,主要修改 sub_mchid:

{
  "sp_appid": "wx1234567890abcdef",
  "sp_mchid": "1900000001",
  "sub_mchid": "1900000102",
  "description": "门店B商品订单",
  "out_trade_no": "ORDER_202609130002",
  "notify_url": "https://pay.example.com/wechat/notify",
  "amount": {
    "total": 100,
    "currency": "CNY"
  },
  "payer": {
    "sp_openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
  }
}

金额单位是分,因此上例中的 100 表示人民币 1 元。

如果实际使用了 sub_appid,OpenID 的获取范围,以及 payer 中应填写 sp_openid 还是 sub_openid,都必须与下单使用的 AppID 一致。不同 AppID 下获取的 OpenID 不能混用。

下单成功后,接口会返回类似结果:

{
  "prepay_id": "wx131122334455..."
}

服务端使用该 prepay_id 生成小程序调起支付所需的签名参数。

小程序调起支付

收到服务端返回的支付参数后,小程序调用:

wx.requestPayment({
  timeStamp: payParams.timeStamp,
  nonceStr: payParams.nonceStr,
  package: payParams.package,
  signType: 'RSA',
  paySign: payParams.paySign,
  success() {
    // 仅表示前端调起结果成功,订单最终状态仍需由服务端确认
  },
  fail(error) {
    console.error('支付未完成', error);
  }
});

其中 package 通常为:

prepay_id=wx131122334455...

服务端必须按照微信支付的要求,使用商户私钥对 appId、timeStamp、nonceStr 和 package 进行签名。私钥不能放在小程序代码中,也不能下发给客户端。

支付结果处理

两个子商户可以共用一个支付结果通知地址。服务端收到通知后,应先根据订单号查询本地订单,并至少核对以下信息:

  • out_trade_no
  • transaction_id
  • sp_mchid
  • sub_mchid
  • 支付金额及币种
  • 交易状态

建议先通过 out_trade_no 找到本地订单,再确认通知中的 sub_mchid 是否与订单预先确定的收款主体一致。不要根据通知内容临时修改订单所属的商户。

后续执行退款、订单查询或关闭订单时,也要继续使用该订单原有的 sub_mchid。

常见误区

在小程序端切换商户号

小程序端不应保存商户私钥,也不应直接调用微信支付商户 API。前端只需向业务服务端提交订单请求,由服务端选择子商户并完成下单。

一个订单同时由两个子商户收款

一笔普通支付订单只能对应一个实际收款子商户。如果同一业务订单需要分别向两个子商户收款,就要拆成两笔支付订单,由用户分别支付。涉及分账时,应评估并申请微信支付提供的分账能力,不能通过同时填写两个 sub_mchid 实现。

只关联服务商,不给子商户授权

小程序与服务商存在关联,并不表示服务商名下的所有子商户会自动获得该 AppID 的支付权限。两个子商户仍需分别完成授权,否则下单时可能出现 AppID 与商户号关系不匹配等错误。

以前端支付成功作为最终结果

wx.requestPayment 的成功回调不能代替服务端对支付结果的确认。订单最终状态应以微信支付回调验签、解密后的结果为准,必要时再调用查单接口确认。

建议的系统设计

服务端可以统一提供一个创建支付的接口:

POST /api/orders/{orderId}/wechat-pay

接口内部按以下顺序处理:

读取订单
→ 判断订单所属业务主体
→ 从服务端配置取得 sub_mchid
→ 校验金额和订单状态
→ 调用服务商模式 JSAPI 下单接口
→ 生成小程序支付签名
→ 返回 wx.requestPayment 参数

采用这种设计后,同一个小程序可以安全地为两个子商户收款。每笔订单及其支付通知、退款和对账记录,也能准确归属到对应的子商户。

备注:内容仅供参考。