如何让软件用户接受 card payments 并收取平台费?
结论
有。适合这种业务的做法,不是先用一个自建的总 merchant account 归集款项,再手工分账,而是采用支付服务商提供的 platform payments、marketplace payments 或 PayFac(支付便利商)方案。
常见产品包括 Stripe Connect + Stripe Terminal、Adyen for Platforms + Terminal API,以及其他同时支持线下读卡、商户入驻和自动分账的平台型收单服务。通常可以实现以下流程:
- 用户通过你的软件完成商户入驻和身份验证;
- 软件向读卡器下发收款金额;
- 支付服务商处理 card payment;
- 自动扣除 merchant processing fee;
- 将约 0.1% 的 application fee 或 platform fee 计入你的平台余额;
- 将剩余资金结算到用户的银行账户。
不过,用户拿到读卡器后完全不做任何设置,通常无法实现。每个实际收款企业仍要完成 KYC/KYB、银行账户验证,并确认服务条款。平台可以把这些步骤嵌入软件,尽量减少用户操作,但不能合法绕过。
为什么不应自行归集再转账
如果所有客户款项先进入你名下的普通 merchant account,再由你转给各个用户,可能带来这些问题:
- 普通 merchant agreement 往往禁止商户替第三方收款;
- 你可能被认定为 payment aggregator、payment facilitator、merchant of record,甚至受到资金传输或支付机构相关监管;
- chargeback、退款、负余额和欺诈责任会集中到你的公司;
- 如果收单机构发现商户名称、实际商品提供者和资金接收者不一致,可能冻结账户或延迟结算;
- 由你维护商户余额并安排提款,会大幅增加合规、审计和对账的复杂度。
平台型支付产品解决的正是这些问题。支付服务商负责持牌收单、商户审核和资金结算,平台则通过其支持的分账机制收取费用。
推荐的业务结构
每个软件用户都应在支付服务商内部拥有一个 connected account、sub-merchant account 或 balance account。用户不一定需要登录支付服务商的网站,可以通过 hosted onboarding 或 embedded onboarding,直接在你的软件中创建账户。
典型资金流如下:
持卡人
│
│ card-present payment
▼
支付服务商
├── processing fee
├── platform fee → 你的平台余额
└── net amount → 用户的 connected account
│
▼
用户银行账户
资金分配应由支付服务商的账本和 API 完成。你的服务器只保存交易标识和分账结果,不直接保管用户资金。
实施步骤
1. 申请平台型支付账户
向候选支付服务商完整说明业务模式,不要只申请普通 merchant account。至少需要说明:
- 用户所在的国家或地区;
- 用户经营的行业;
- 每位用户预计每年约
$250,000的 card volume; - 平均每天约 15 笔交易;
- 平均客单价;
- 是否涉及高风险行业、订阅、押金或延迟交付;
- 业务会使用线下 card reader;
- 平台计划收取 10 basis points,也就是
0.1%; - 由谁负责退款、chargeback 和负余额。
服务商会根据这些信息判断你适合 marketplace、PayFac、referral、ISO,还是其他合作模式。不同国家对资金流和责任划分的要求不同,选型时不能只看 API 是否具备相应功能。
2. 为用户建立 connected account
用户仍需提交企业和实际控制人的资料,例如:
- 企业名称、注册地址和注册编号;
- 法定代表人或受益所有人的信息;
- 收款银行账户;
- 税务信息;
- 身份证明;
- 接受支付服务条款的记录。
建议优先使用支付服务商托管或嵌入式的 onboarding 页面,避免自行收集和保存身份证件。
3. 注册读卡器
不要让自己的后端接触 PAN、磁道数据或 PIN。应使用服务商认证的 EMV reader 和官方 Terminal SDK。
读卡器通常需要关联到:
- 某个 connected account;
- 某个门店或
location; - 某台 POS 设备;
- 对应的操作员或软件用户。
读卡器能否在多个 connected accounts 之间切换,以及是否必须按门店注册,取决于服务商和终端型号。这些限制应在选型阶段确认清楚。
4. 创建付款并设置平台费
下面使用 Stripe Connect 风格的 Node.js 代码说明资金拆分方式。这只是结构示例。账户类型、国家、API 版本和 Terminal 集成方式都会影响可用参数,实际实施时应以支付服务商的当前文档和审核结果为准。
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
function calculatePlatformFee(amountInCents) {
// 0.1% = 10 basis points
return Math.round((amountInCents * 10) / 10_000);
}
export async function createTerminalPayment({
amountInCents,
currency,
connectedAccountId
}) {
if (!Number.isInteger(amountInCents) || amountInCents <= 0) {
throw new Error("amountInCents must be a positive integer");
}
const applicationFeeAmount = calculatePlatformFee(amountInCents);
return stripe.paymentIntents.create({
amount: amountInCents,
currency,
payment_method_types: ["card_present"],
application_fee_amount: applicationFeeAmount,
transfer_data: {
destination: connectedAccountId
},
metadata: {
connected_account_id: connectedAccountId
}
});
}
创建 PaymentIntent 后,再由 Terminal SDK 或服务端驱动的终端接口,通过读卡器收集并确认付款。密钥和费用计算必须放在服务器端,不能信任 POS 客户端传入的 application_fee_amount。
一种常见的结算逻辑是:
gross amount
- payment processing fee
- application/platform fee
= connected account net balance
processing fee 具体由哪一方承担,取决于采用 direct charge、destination charge,还是 separate charges and transfers 等资金模型。做账时不能只看交易成功后的入账金额,还应使用 provider 提供的 balance transaction、transfer 和 fee records。
5. 使用 webhook 更新订单状态
读卡器界面显示成功后,不能直接把订单永久标记为已付款。服务器应验证 webhook,并以服务商返回的最终支付状态为准。
export function handleWebhook(req, res, stripe) {
let event;
try {
event = stripe.webhooks.constructEvent(
req.body,
req.headers["stripe-signature"],
process.env.STRIPE_WEBHOOK_SECRET
);
} catch {
return res.status(400).send("Invalid webhook signature");
}
switch (event.type) {
case "payment_intent.succeeded": {
const paymentIntent = event.data.object;
// 使用 paymentIntent.id 做幂等更新。
// 不要仅凭金额匹配订单。
markOrderAsPaid(paymentIntent.id);
break;
}
case "payment_intent.payment_failed": {
const paymentIntent = event.data.object;
markOrderAsFailed(paymentIntent.id);
break;
}
}
return res.sendStatus(200);
}
实际实现还要处理重复 webhook、乱序事件、退款、撤销、部分退款、chargeback 和异步终端状态。
0.1% 的经济性需要重新核算
0.1% 等于 10 个 basis points。按每位用户每年 $250,000 的 card volume 计算:
$250,000 × 0.1% = $250/年
每位用户每年只能带来约 $250 的平台交易收入。如果平均每天约 15 笔交易,一年大约有 5,475 笔,平台每笔平均收入只有约 $0.046。
这些收入需要承担:
- 平台支付产品的额外费用;
- 读卡器的采购、物流、更换和远程管理费用;
- 商户审核和客服成本;
- 退款及 chargeback 处理成本;
- 对账、税务和财务成本;
- 欺诈和负余额风险;
- PCI、安全测试和系统维护成本。
因此,技术上可以扣取 10 basis points,但这笔费用未必足以覆盖业务成本。更常见的定价方式有:
- 软件订阅费加较低的 transaction fee;
- 每笔固定费用加百分比费用;
- 按月收取 terminal rental;
- 与收单机构协商批量费率,再保留一部分 margin;
- 设置最低月度平台费。
处理费中的固定 per-transaction 部分,也不会因为平台费较低而消失。交易笔数较多且客单价较低时,这项固定费用尤其需要纳入测算。
“所有费用都加入客户付款金额”需要谨慎
如果你准备向持卡人额外收取 surcharge 或 convenience fee,需要分别确认:
- 当地法律是否允许;
- card network rules 是否允许;
- debit card 是否禁止加收;
- 是否设有费率上限;
- 是否必须在付款前明确展示;
- 收据上应如何列示;
- 退款时是否必须退还这笔费用。
不能直接把 merchant processing fee 和平台费全部加到客户应付金额中。比较稳妥的默认方案,是由商户把这些费用作为经营成本承担,平台再从商户的交易结算款或月度账单中收费。如果确实需要收取 surcharge,应使用支付服务商正式支持的合规功能。
选型时必须确认的问题
签约前,应要求支付服务商书面确认以下事项:
- 是否支持所在地区的 card-present platform payments;
- connected merchant 的开户和 KYC 由谁负责;
- 能否在单笔终端交易中扣取 application fee;
- processing fee 从平台还是 connected account 扣除;
- 读卡器的归属、配对和远程管理方式;
- 商户名称如何显示在终端、收据和持卡人账单上;
- 退款时平台费是否自动退回;
- chargeback 和负余额由谁承担;
- payout schedule 是否可以配置;
- 是否提供逐笔 balance transaction 和 reconciliation report;
- 是否收取平台月费、账户费、终端费,或设有最低交易量;
- 平台能否自行设定费率,以及是否需要额外的 PayFac 审批。
最终应采用这样的结构:每个用户都是经过审核的 sub-merchant,由支付服务商自动分账。这样既能提供接近开箱即用的读卡体验,也能自动收取平台费,还可以避免用普通 merchant account 替第三方归集和转移资金。上线前,还应请支付领域的律师和会计师审查合同、资金流、税务及 surcharge 设计。
备注:内容仅供参考。
