PAYATHON 2026

通过 W3C Web Payments API 实现 Square 多方交易

支付小周

结论

仅靠 W3C Web Payments API,无法实现或开启 Square 的多方交易功能。

Payment Request API 用于统一浏览器端的支付信息收集和支付方式选择,并不处理分账、平台佣金、子商户结算或多收款方路由。多方交易仍要依靠 Square 提供的账户能力和服务端 API。

如果需要在 Web Payments 的交互流程中提供这项功能,可以开发自定义支付方式和 Payment Handler。不过,它只是前端入口,底层仍需调用 Square API。Square 如果没有向当前账户或集成模式开放相应能力,Payment Handler 也无法绕过限制。

为什么 Web Payments API 无法直接完成分账

一笔典型的多方交易至少需要处理以下问题:

  • 识别平台、卖家等参与方
  • 计算平台手续费或应用费
  • 确定资金进入哪个商户账户
  • 处理退款、拒付和争议责任
  • 遵守 KYC、税务及结算规则
  • 管理服务端幂等和交易状态

这些属于支付服务商的账户和资金处理逻辑。

W3C Web Payments API 中的 PaymentRequest 只描述订单金额、展示项目和浏览器支持的支付方式。例如:

const request = new PaymentRequest(
  [
    {
      supportedMethods: "basic-card"
    }
  ],
  {
    total: {
      label: "订单总额",
      amount: {
        currency: "USD",
        value: "100.00"
      }
    }
  }
);

const response = await request.show();

这里的 total 是消费者看到并授权的总金额。它无法表达卖家获得 90 美元、平台获得 10 美元,也不会替开发者执行分账。

另外,basic-card 已不适合新项目作为通用方案使用,浏览器支持情况也需要单独确认。

推荐的实现方式

比较稳妥的做法是明确划分前后端职责:

  1. 前端使用 Square 支持的 Web 支付组件收集支付凭证。
  2. 后端调用 Square API 创建交易,并传入卖家、平台费用等多方交易信息。
  3. 后端保存订单和 Square 交易标识,同时处理退款、Webhook 与异常重试。
  4. 前端根据后端返回的最终状态展示支付结果。

下面是一个概念性流程。实际使用的 API 字段必须以 Square 当前文档和账户已开通的产品能力为准。

async function submitPayment(sourceId, orderId) {
  const response = await fetch("/api/payments", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      sourceId,
      orderId
    })
  });

  const result = await response.json();

  if (!response.ok) {
    throw new Error(result.message || "Payment failed");
  }

  return result;
}

服务端应根据可信的订单数据计算金额,不能直接接受浏览器提交的分账结果:

app.post("/api/payments", async (req, res) => {
  const { sourceId, orderId } = req.body;

  const order = await loadOrder(orderId);

  if (!order) {
    return res.status(404).json({
      message: "Order not found"
    });
  }

  const paymentRequest = {
    sourceId,
    amount: order.totalAmount,
    currency: order.currency,
    sellerAccountId: order.sellerAccountId,
    platformFee: order.platformFee,
    idempotencyKey: order.paymentAttemptId
  };

  // 根据当前 Square API 和账户能力,将上述业务数据映射为实际请求。
  const payment = await createSquareMarketplacePayment(paymentRequest);

  res.json({
    paymentId: payment.id,
    status: payment.status
  });
});

createSquareMarketplacePayment 是业务层的占位函数,并非 Square 官方 API 名称。示例没有指定 Square 当前使用的具体字段,因为多方交易接口、准入条件和字段命名可能因产品及账户类型而异。

使用自定义 Payment Handler 的可选方案

如果业务需要通过 Web Payments 界面调起支付,可以注册自定义支付方式:

const request = new PaymentRequest(
  [
    {
      supportedMethods: "https://pay.example.com/square-marketplace",
      data: {
        orderId: "order-123"
      }
    }
  ],
  {
    total: {
      label: "订单总额",
      amount: {
        currency: "USD",
        value: "100.00"
      }
    }
  }
);

const response = await request.show();

try {
  const result = await fetch("/api/payments/confirm", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      orderId: "order-123",
      paymentData: response.details
    })
  });

  if (!result.ok) {
    throw new Error("Payment confirmation failed");
  }

  await response.complete("success");
} catch (error) {
  await response.complete("fail");
  throw error;
}

这种支付方式需要自行托管支付方式清单,并实现浏览器支持的 Payment Handler。它能把 Square 支付接入统一的浏览器交互,但仍有以下限制:

  • 浏览器必须支持 Payment Handler。
  • 用户可能需要安装或注册该支付处理程序。
  • 自定义 supportedMethods URL 不会自动获得 Square 支持。
  • Square 支付凭证仍应通过其允许的客户端方案生成。
  • 最终交易和分账必须由可信的服务端完成。

这种方案更适合已经拥有自定义支付生态,同时需要统一 Web Payments 入口的项目。对于普通的 Square 商户集成,它通常不是首选。

注意事项

分账金额、平台费用和收款方身份不能由前端决定。后端应根据订单、卖家协议及账户配置重新计算,并校验所有数据。

不要把 Square 访问令牌或其他服务端密钥写入浏览器代码。客户端只能处理允许公开的应用标识和一次性支付凭证。

创建支付请求时还要设置幂等键,防止网络重试造成重复扣款。支付、退款和争议状态应通过 Square 提供的事件通知机制核对,不能只依据前端返回的 “成功” 结果。

Web Payments API 不能替代支付服务商的合规审查和账户授权。如果 Square 的多方交易能力需要单独申请、使用指定账户模型或仅支持特定地区,就必须先满足这些条件。功能请求、自定义 Payment Handler 和前端封装都无法绕过服务端能力限制。

备注:内容仅供参考。