PAYATHON 2026

Mobile app 使用 payment gateway 时谁承担交易手续费?

支付小周

明确结论

在常见的 payment gateway 模式下,交易手续费通常由 merchant(商户)向支付服务商支付,不是由 consumer(消费者)直接支付。支付服务商一般会先从订单款项中扣除手续费,再将剩余金额结算给商户。

例如,用户支付 ¥100,费率为 2.9% + ¥0.30:

交易手续费 = 100 × 2.9% + 0.30 = ¥3.20
商户实收 = 100 - 3.20 = ¥96.80

商户可以提高商品价格、收取服务费,或者在合规的前提下收取银行卡附加费,从而将成本间接或直接转给消费者。能否这样做,要看所在地法律、银行卡组织规则、支付服务商条款和应用商店政策。

没有一种通用于所有订单的“完全免费”支付方案。即使支付网关本身不收费,银行转账、退款、拒付、汇率转换、税务处理和系统运维仍可能产生费用。

一般支付流程

Mobile app 中常见的订单流程是:

  1. App 将商品和数量提交给 merchant server。
  2. Server 根据数据库中的商品价格重新计算订单金额。
  3. Server 创建订单,并调用 payment gateway 创建支付请求。
  4. App 通过支付网关提供的 SDK 或托管支付页面完成付款。
  5. Payment gateway 向用户返回支付结果,同时通过 webhook 通知 server。
  6. Server 验证 webhook 签名并检查订单状态。
  7. 确认付款成功后,server 才将订单标记为已支付并安排发货。
  8. Payment gateway 扣除相关费用,再把剩余款项结算给商户。

不要看到 App 显示“支付成功”就直接更新订单。客户端返回结果可能被伪造,也可能因断网而丢失。订单的最终状态应以服务端查询结果或通过签名验证的 webhook 为准。

手续费实际上由谁承担

从合同和结算关系看,手续费通常由 merchant 承担。merchant 与 payment gateway 签订服务协议,支付网关根据交易金额、支付方式和商户类别收费。

但在实际经营中,这笔成本不一定全部由商户消化。商户可以:

  • 将支付成本计入商品售价。
  • 对所有订单统一收取服务费。
  • 只对特定支付方式收取附加费。
  • 为成本较低的付款方式提供折扣。
  • 设置最低订单金额,减少固定手续费对小额订单的影响。

一笔交易的支付成本可以写成:

fee = amount × percentage_rate + fixed_fee

如果商户希望扣除手续费后仍收到目标净额 net_amount,理论售价可以这样计算:

gross_amount = (net_amount + fixed_fee) / (1 - percentage_rate)

假设目标净额为 ¥100,费率为 2.9% + ¥0.30:

gross_amount = (100 + 0.30) / (1 - 0.029)
             ≈ ¥103.30

实际计算时,应使用货币最小单位取整,并遵循支付渠道的计费规则。有些网关还会对税费、跨境交易、货币转换、退款或拒付另行收费。

能否把手续费直接转给消费者

从技术上说,可以在订单中增加一项费用,但法律和合同未必允许。

实施前至少要确认以下事项:

  • 当地法律是否允许针对某种支付方式收取附加费。
  • 银行卡组织是否限制附加费比例、适用卡种或展示方式。
  • Payment gateway 的服务条款是否允许收取这类费用。
  • 是否必须在结账前明确展示费用。
  • 附加费是否需要计税。
  • 退款时是否必须同时退还附加费。
  • Apple App Store 或 Google Play 是否允许当前商品类型使用这种支付方式。

信用卡、借记卡和银行转账可能适用不同规则。部分地区禁止或严格限制银行卡附加费,有些规则还要求附加费不得超过实际处理成本。因此,不能简单地对所有支付方式统一加收“网关手续费”。

较稳妥的方式是将正常支付成本计入商品价格,或者为银行转账等成本较低的支付方式提供折扣。最终方案仍要结合业务所在地和支付服务商协议确定。

推荐的订单金额设计

所有金额都应由 server 计算。App 只能提交商品标识、数量和用户选择,不能直接决定最终付款金额。

下面是一个简化的 Node.js 示例:

function calculateOrder({ items, paymentMethod }) {
  const subtotal = items.reduce((sum, item) => {
    return sum + item.unitPriceMinor * item.quantity;
  }, 0);

  const shipping = calculateShipping(items);
  const tax = calculateTax({ subtotal, shipping });

  const surcharge = paymentMethod === "credit_card"
    ? calculatePermittedSurcharge(subtotal)
    : 0;

  return {
    subtotal,
    shipping,
    tax,
    surcharge,
    total: subtotal + shipping + tax + surcharge,
    currency: "CNY"
  };
}

unitPriceMinor 使用货币最小单位。例如,人民币 ¥19.90 应保存为 1990 分,以避免浮点数误差。

服务端创建支付请求时,应使用已保存的订单金额:

app.post("/api/orders/:orderId/payments", async (req, res) => {
  const order = await orderRepository.findById(req.params.orderId);

  if (!order || order.status !== "pending") {
    return res.status(409).json({ error: "Order cannot be paid" });
  }

  const payment = await paymentGateway.createPayment({
    amount: order.totalMinor,
    currency: order.currency,
    reference: order.id,
    idempotencyKey: `payment:${order.id}`
  });

  await paymentRepository.save({
    orderId: order.id,
    gatewayPaymentId: payment.id,
    status: "pending"
  });

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

示例中的 paymentGateway.createPayment、clientToken 等都是抽象名称,实际开发时要替换为所选支付服务商提供的接口。

处理 webhook 时同样要验证签名,并确保重复通知不会引起重复发货:

app.post("/webhooks/payment", rawBodyMiddleware, async (req, res) => {
  let event;

  try {
    event = paymentGateway.verifyWebhook({
      body: req.body,
      signature: req.headers["payment-signature"]
    });
  } catch {
    return res.sendStatus(400);
  }

  if (event.type === "payment.succeeded") {
    await orderRepository.markPaidOnce({
      orderId: event.reference,
      gatewayPaymentId: event.paymentId,
      paidAmount: event.amount,
      currency: event.currency
    });
  }

  res.sendStatus(200);
});

markPaidOnce 需要做幂等处理,并核对订单编号、币种和金额,不能只检查事件类型。

Mobile app 的特殊限制

如果 App 销售需要在现实世界交付的商品或服务,例如服装、餐饮配送、酒店或交通服务,通常可以接入第三方 payment gateway。

如果销售的是在 App 内消费的数字内容、订阅或虚拟功能,可能必须使用应用商店规定的应用内购买系统。适用范围会因平台、地区、商品类型和政策变化而不同。发布前应检查 Apple、Google 和目标市场的最新规则。

应用商店抽成与 payment gateway 手续费不是一回事:

  • 应用商店抽成通常与数字商品分发有关。
  • Payment gateway 手续费通常与资金处理有关。
  • Marketplace 还可能产生平台佣金、分账费和商户身份验证成本。

降低支付成本的可行办法

支付成本无法完全消除,但可以通过以下方式控制:

  • 比较不同网关的百分比费率、固定费用、退款费和拒付费。
  • 为小额订单设置最低消费金额,或者合并付款。
  • 提供银行转账、即时支付或本地低成本支付方式。
  • 交易量达到一定规模后,与支付服务商协商费率。
  • 减少退款和拒付,完善账单描述与客服流程。
  • 避免不必要的跨境收款和货币转换。
  • 将预计产生的支付成本计入定价和毛利计算。
  • 在合规的情况下,向消费者明确展示附加费,或为低成本支付方式提供折扣。

自行存储银行卡信息通常不能降低成本。这样做无法绕过收单机构和银行卡网络费用,反而会大幅增加 PCI DSS、安全和合规责任。应优先使用 payment gateway 提供的 tokenization、托管支付页面或官方 SDK。

实施时还要注意

选择方案前,应向支付服务商确认完整费率,不能只看首页展示的基础交易费。需要重点了解:

  • 成功交易费用;
  • 固定手续费;
  • 跨境和货币转换费用;
  • 退款后原手续费是否退还;
  • 拒付及争议处理费用;
  • 结算周期和提现费用;
  • 是否支持附加费;
  • 是否支持 marketplace 分账;
  • 是否收取最低月费或账户维护费。

与其寻找“完全不用支付手续费”的支付网关,不如先确定商品类型、目标地区和支付方式,再将全部支付成本计入定价。如果决定向消费者单独收费,应在开发前完成法律、支付协议和应用商店政策检查,并在用户确认付款前清楚展示最终金额。

备注:内容仅供参考。