PAYATHON 2026

PC端如何实现不同二级商户的合并支付?

支付老李

结论

可以实现,但通常不能直接通过直付通的 APP、H5 或小程序支付接口生成 PC 收银台二维码。

一种可行的做法是:PC 端创建合并支付订单,服务端生成对应的移动端收银页面地址,再把该地址转换为二维码。用户扫码后,通过手机进入直付通支持的 H5、小程序或 APP 支付流程,一次完成付款。支付成功后,系统根据合并订单中的明细,向多个二级商户分账或结算。

需要区分两个概念:“一次付款后分给多个二级商户”与”同一笔交易直接让多个二级商户同时收款”并不完全相同。究竟能使用合单支付、分账还是其他模式,要以商户签约产品、接口权限和支付宝开放平台当前提供的能力为准。

推荐实现方案

整体流程如下:

  1. PC 端向业务服务端提交多个二级商户的订单明细。
  2. 服务端创建唯一的合并订单,保存各二级商户及其对应金额。
  3. 服务端生成手机端收银页面 URL。
  4. PC 端将该 URL 渲染成二维码。
  5. 用户用手机扫码,进入 H5 页面或跳转到支付宝付款。
  6. 服务端通过异步通知确认支付结果。
  7. 支付成功后,根据原始订单明细执行分账、结算或业务侧账务拆分。
  8. PC 端通过轮询、WebSocket 或 Server-Sent Events 获取最终支付状态。

流程结构如下:

PC 二维码
    ↓
移动端收银页面
    ↓
直付通支持的 H5 / 小程序 / APP 支付
    ↓
支付成功通知
    ↓
按订单明细分账或结算到多个二级商户

PC 端只负责展示二维码和查询订单状态,实际支付仍发生在直付通支持的移动端场景中。

服务端创建合并订单

服务端应先创建业务系统自己的合并订单,不要直接在浏览器中拼接支付参数。一笔合并订单至少需要记录:

{
  "mergeOrderNo": "M202609130001",
  "totalAmount": "168.00",
  "status": "WAIT_PAY",
  "subOrders": [
    {
      "subOrderNo": "S202609130001",
      "merchantId": "2088XXXXXXXX0001",
      "amount": "100.00"
    },
    {
      "subOrderNo": "S202609130002",
      "merchantId": "2088XXXXXXXX0002",
      "amount": "68.00"
    }
  ]
}

创建订单时,必须校验总金额:

totalAmount = Σ subOrder.amount

计算金额时,应使用十进制定点类型,或者统一换算成”分”后用整数计算,不要直接累加 JavaScript 浮点数。

例如:

const subOrders = [
  { merchantId: "2088XXXXXXXX0001", amountFen: 10000 },
  { merchantId: "2088XXXXXXXX0002", amountFen: 6800 }
];

const totalAmountFen = subOrders.reduce(
  (sum, item) => sum + item.amountFen,
  0
);

console.log(totalAmountFen); // 16800

生成供扫码访问的地址

服务端创建订单后,可以返回一个短时有效的收银页面地址:

{
  "mergeOrderNo": "M202609130001",
  "cashierUrl": "https://pay.example.com/cashier?t=SIGNED_TOKEN",
  "expireTime": "2026-09-13T15:30:00+08:00"
}

不建议在二维码中直接放入二级商户编号、金额、分账比例或支付接口参数。更稳妥的做法是只携带一次性令牌,再由服务端根据令牌查询真实订单。

示例:

import QRCode from "qrcode";

async function renderPaymentQrCode(cashierUrl) {
  const canvas = document.getElementById("payment-qrcode");

  await QRCode.toCanvas(canvas, cashierUrl, {
    width: 240,
    margin: 2,
    errorCorrectionLevel: "M"
  });
}

页面结构可以保持简单:

<canvas id="payment-qrcode"></canvas>
<p id="payment-status">等待扫码支付</p>

二维码过期后,应停止查询原订单并重新创建支付请求,避免用户向已经关闭或状态不明确的订单付款。

移动端发起直付通支付

用户扫码进入移动端收银页面后,再调用当前签约场景允许使用的直付通接口。

需要满足两个前提:

  • 商户已经开通直付通及相应支付场景权限。
  • 订单模型、二级商户字段和资金处理方式符合当前接口文档及签约规则。

使用 H5 支付时,移动端页面可以向业务服务端请求支付参数:

async function startPayment(token) {
  const response = await fetch("/api/payment/create", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ token })
  });

  if (!response.ok) {
    throw new Error("创建支付请求失败");
  }

  const result = await response.json();

  if (result.redirectUrl) {
    window.location.href = result.redirectUrl;
  }
}

签名、应用私钥、平台公钥和支付接口调用都必须放在服务端。浏览器只能接收服务端生成的跳转地址或已签名参数,不能持有商户私钥。

具体调用哪个 API、各项请求字段如何填写,应以当前直付通产品文档和商户实际开通的权限为准。不能因为产品”支持 H5”,就认定某个合单或分账接口也已开放。

PC 端查询支付状态

PC 页面需要持续获取合并订单的状态。简单场景可以使用轮询:

const mergeOrderNo = "M202609130001";

const timer = setInterval(async () => {
  const response = await fetch(
    `/api/payment/status?mergeOrderNo=${encodeURIComponent(mergeOrderNo)}`
  );

  if (!response.ok) {
    return;
  }

  const result = await response.json();

  if (result.status === "SUCCESS") {
    clearInterval(timer);
    document.getElementById("payment-status").textContent = "支付成功";
  }

  if (
    result.status === "CLOSED" ||
    result.status === "FAILED" ||
    result.status === "EXPIRED"
  ) {
    clearInterval(timer);
    document.getElementById("payment-status").textContent = "订单已关闭";
  }
}, 2000);

订单量较大时,可以改用 WebSocket 或 Server-Sent Events,减轻频繁轮询给服务端带来的压力。

PC 页面展示的状态只能来自业务服务端。用户手机跳转到成功页面,并不能直接证明支付已经成功。最终结果应以平台异步通知为主,并通过主动查询进行补偿确认。

多个二级商户的资金处理方式

落地前,需要先确定业务采用哪种资金模型。

一次支付后再分账

用户先支付总金额,资金进入约定的交易账户。支付成功后,系统再按照订单明细将资金分配给多个二级商户。

这种方式在业务上最接近”合并支付”,在 PC 二维码场景中也比较容易组织。是否支持实时分账、延迟分账,以及最多支持多少个接收方,需要根据签约产品确认。

主单加子单

业务系统创建一笔主订单,并为每个二级商户分别创建子订单。用户只需付款一次,系统内部则保留各商户独立的订单和金额。

直付通是否允许在一次支付请求中直接提交多个子订单,取决于当前接口能力。不能自行假设普通 H5 支付接口具备合单能力。

业务侧统一收款后结算

如果现有支付能力不能直接覆盖多个二级商户,可以由一个主体统一收款,再通过平台允许的结算能力完成后续资金划拨。

这种方式涉及交易主体、开票、退款、对账、税务和资金合规,不能只靠技术上的订单拆分来处理。上线前应由支付服务方和业务合规人员确认。

功能相近的替代方案

如果直付通目前没有适用的合单接口,可以考虑以下方案:

  • 使用支持 PC 扫码的当面付或预创建类产品统一收款,再结合平台允许的分账能力处理多个二级商户。
  • 把直付通 H5 收银页面作为二维码落地页,PC 端只负责展示二维码。
  • 将一笔合并订单拆成多笔支付。实现相对简单,但用户需要付款多次,严格来说不属于一次合并支付。
  • 接入具备”合单支付加多方分账”能力的其他支付产品。切换前,需要确认该产品允许的商户关系、资金路径和结算规则。

当面付或预创建类产品能否与直付通二级商户、分账或结算能力结合使用,必须向支付宝侧确认。不同产品的接口可以在业务层串联,但这不表示它们的资金能力和签约权限可以直接混用。

退款与对账设计

设计合并支付时,除了付款流程,还要提前考虑部分退款和全额退款。

例如,总订单金额为 168 元,两个子订单分别为 100 元和 68 元。如果第二个子订单需要退款,系统必须明确:

  • 是否允许单独退还 68 元;
  • 已经分账的资金是否需要先解冻或退回;
  • 平台退款接口使用主订单号还是子订单号;
  • 手续费和优惠如何分摊;
  • 退款后如何调整各二级商户的可结算金额。

业务库应同时保存支付订单、子订单、分账记录、退款记录和异步通知原文,并为每项操作设置独立的幂等号。

注意事项

  • 不要在前端保存应用私钥、支付签名密钥或完整的支付参数。
  • 二维码应设置有效期,并使用不可预测、可校验且最好只能使用一次的令牌。
  • 服务端必须验证异步通知的签名、应用标识、订单号、金额和收款主体。
  • 支付创建、异步通知、主动查询、分账和退款接口都必须做幂等处理。
  • 合并订单总金额必须等于所有子订单金额之和。
  • 在确认支付成功前,不要发货或执行分账。
  • 异步通知处理失败后应支持重试。长时间未收到通知时,应主动查询订单。
  • 需要确认二级商户数量、单笔金额、分账比例、分账时效和退款限制。
  • PC 二维码只是支付入口,不会扩大直付通已经签约的产品权限。
  • “统一收款后自行转账给商户”可能改变资金性质。在没有确认合规性之前,不应将其作为平台分账的替代方案。

较为稳妥的落地方式是:PC 展示二维码,手机端使用受支持的支付场景,服务端维护合并订单,并在支付成功后按规则分账。如果业务要求支付接口直接生成多个二级商户的收款指令,还需要确认直付通当前是否已向该商户开放对应的合单能力。在没有明确接口文档或签约权限前,不宜基于普通 H5 支付接口自行扩展。

备注:内容仅供参考。