第三方平台小程序如何开通支付宝支付功能?
结论
这类业务不能只开通一个支付宝收款账号,把平台会员费和所有商家的订单款全部收进平台账户,再在线下结算。
通常需要拆成两条支付链路:
- 平台会员费:由平台主体签约支付宝支付,交易款直接进入平台的支付宝商户账户。
- 店铺订单费:入驻商家分别完成支付宝商户签约,并授权第三方平台代为调用支付接口,交易款直接进入对应商家的支付宝账户。
如果业务确实需要统一收款,再按比例分配资金,应申请支付宝目前为平台型业务提供的分账或资金结算产品。没有合规产品支持时,不建议自行代收后转账,否则可能产生二清、账务混同、退款责任不清等问题。
支付宝开放平台的政策可能调整,具体产品名称、准入行业和签约条件应以开放平台控制台显示的内容及支付宝审核结果为准。
推荐的账户和应用结构
建议至少划分三个角色:
| 角色 | 主要职责 | 收款范围 |
|---|---|---|
| 第三方平台主体 | 运营小程序、维护应用和支付系统 | 会员费、平台自营订单 |
| 入驻商家 | 提供商品或服务 | 本店订单款 |
| 买家 | 在小程序内完成付款 | 会员费或店铺订单 |
平台需要保存每家商户的签约和授权状态,例如:
merchant_id
alipay_user_id
authorization_status
app_auth_token
app_auth_token_expire_time
payment_enabled
app_auth_token 是敏感凭证,只能保存在服务端,不能写入小程序代码、网页源码或接口响应。
开通流程
1. 平台主体完成支付签约
平台先在支付宝开放平台创建或绑定小程序应用,然后完成主体认证、应用审核和支付能力签约。
平台的会员订单应使用平台商户身份创建,并在订单表中明确标记收款主体:
order_type = MEMBERSHIP
payee_type = PLATFORM
payee_id = 平台商户标识
会员服务还要确认所属行业是否允许经营,并在支付页面写明会员权益、有效期、续费方式和退款规则。如果包含自动续费,还需满足代扣产品的签约和用户授权要求,不能把普通单次支付直接用于自动续费。
2. 入驻商家完成商户签约
商家入驻平台后,并不会自动获得支付宝收款能力。平台应引导商家完成以下事项:
- 商户主体认证;
- 经营类目和资质提交;
- 支付产品签约;
- 结算账户配置;
- 对第三方应用的授权。
审核通过后,平台会取得代表该商家调用接口所需的授权信息。不同接入产品的授权流程和令牌有效期可能不同,具体应以当前开放平台文档为准。
3. 创建支付订单时确定真实收款方
发起支付前,必须根据订单类型选择支付身份:
会员订单 -> 平台收款
店铺订单 -> 订单所属商家收款
混合订单 -> 拆单,或使用已获准的分账产品
如果一个订单同时包含会员费和不同商家的商品,不宜直接生成一笔普通交易。较稳妥的处理方式是拆成多个支付订单,并分别记录收款方。如果产品要求买家一次付款,则需要评估支付宝的分账方案是否适用。
4. 服务端调用支付宝接口
小程序端只负责发起支付。订单金额、收款商户、支付标题和业务单号都必须由服务端生成并校验。
常见流程如下:
- 服务端创建业务订单;
- 根据订单类型选择平台身份或商家授权身份;
- 服务端调用支付宝交易创建接口;
- 将支付宝返回的交易号交给小程序;
- 小程序调用
my.tradePay; - 服务端根据异步通知或主动查询结果更新订单。
小程序端的 success 回调不能作为最终到账依据。
服务端示例
下面是简化后的 Java 风格示例,用来说明如何选择支付身份。SDK 类名和请求字段需要根据项目实际使用的支付宝 SDK 版本核对。
public String createAlipayTrade(Order order) throws Exception {
if (!order.isPending()) {
throw new IllegalStateException("订单状态不允许支付");
}
AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
Map<String, Object> bizContent = new HashMap<>();
bizContent.put("out_trade_no", order.getPaymentNo());
bizContent.put("total_amount", order.getAmount().toPlainString());
bizContent.put("subject", order.getSubject());
bizContent.put("buyer_id", order.getAlipayBuyerId());
request.setBizContent(toJson(bizContent));
request.setNotifyUrl("https://api.example.com/pay/alipay/notify");
if (order.getPayeeType() == PayeeType.MERCHANT) {
Merchant merchant = merchantService.get(order.getMerchantId());
if (!merchant.isPaymentEnabled()) {
throw new IllegalStateException("商家尚未开通支付宝收款");
}
// 代表入驻商家调用接口时使用其授权令牌。
request.putOtherTextParam(
"app_auth_token",
merchant.getAppAuthToken()
);
}
AlipayTradeCreateResponse response = alipayClient.execute(request);
if (!response.isSuccess()) {
throw new IllegalStateException(
"支付宝下单失败:" + response.getSubCode()
);
}
return response.getTradeNo();
}
app_auth_token 不能由前端传入。服务端应根据可信的订单数据确定收款商户,同时核对订单中的 merchant_id 是否与商品、门店和授权记录一致,防止有人通过篡改请求改变收款方。
小程序端示例
小程序从业务服务端取得 tradeNO 后调用支付:
my.request({
url: 'https://api.example.com/orders/123/pay',
method: 'POST',
success: (res) => {
my.tradePay({
tradeNO: res.data.tradeNO,
success: () => {
// 这里只表示客户端支付流程返回成功。
// 最终订单状态仍需向服务端查询。
my.navigateTo({
url: '/pages/pay-result/pay-result?orderId=123'
});
},
fail: (error) => {
console.error('支付未完成', error);
}
});
}
});
服务端返回的内容应尽量精简:
{
"tradeNO": "支付宝返回的交易号"
}
不要返回应用私钥、支付宝公钥、app_auth_token 或完整的签名配置。
异步通知处理
支付通知接口必须验证签名,并检查业务数据是否匹配:
public String handleNotify(Map<String, String> params) {
boolean signatureValid = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
"UTF-8",
"RSA2"
);
if (!signatureValid) {
return "failure";
}
String paymentNo = params.get("out_trade_no");
String tradeStatus = params.get("trade_status");
BigDecimal paidAmount =
new BigDecimal(params.get("total_amount"));
Order order = orderService.getByPaymentNo(paymentNo);
if (order == null) {
return "failure";
}
if (paidAmount.compareTo(order.getAmount()) != 0) {
return "failure";
}
// 还应按接入模式核对 app_id、收款方标识等字段。
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
orderService.markPaidIdempotently(
order.getId(),
params.get("trade_no")
);
}
return "success";
}
通知处理必须具备幂等性。支付宝可能重复发送通知,但同一业务订单不能重复发放会员权益、扣减库存或记账。
退款和对账也要按收款方隔离
通常由实际收款方负责对应订单的退款和账务处理。平台至少需要分别记录:
payment_no
alipay_trade_no
payee_type
payee_id
app_auth_context
paid_amount
refunded_amount
payment_status
为商家订单发起退款时,应使用与原交易相同的商家授权身份,不能用平台自己的支付身份处理商家收取的交易。
每日对账也应分别核对:
- 平台会员收入;
- 各入驻商家的交易收入;
- 退款;
- 支付渠道手续费;
- 平台服务费;
- 分账或结算记录。
需要特别注意的问题
避免形成二清
平台先以自己的名义收取本应属于商家的订单款,再自行计算并转给商家,通常会产生二清风险。系统能够实现这条资金路径,并不意味着它符合支付和结算要求。
应优先让商家直接收款。需要统一收款、抽佣或分账时,应使用支付宝认可的产品方案,并确保合同、交易信息和资金流保持一致。
平台抽佣不能随意从商家货款中扣除
如果平台需要收取技术服务费,可以根据实际产品能力选择以下方式:
- 单独向商家收取服务费;
- 使用获准的分账产品按规则分配;
- 按结算周期开具账单,由商家另行支付。
是否支持支付时实时分账、延迟分账、退款后回退分账等能力,需要结合行业、主体资质和签约产品确认。
商家授权需要管理生命周期
平台需要处理授权取消、令牌失效、商户签约失效以及商户被限制收款等情况。创建订单前,应实时或定期检查授权状态。授权失效后,需要停止该店铺收款,并提示商家重新签约或授权。
金额必须以服务端数据为准
小程序端提交的金额、商户编号和商品名称都不能直接信任。服务端应重新读取商品价格、优惠规则和订单归属,使用 BigDecimal 计算金额,并确认金额大于零、小数位符合接口要求。
先确认产品准入再开发
支付宝可能针对平台型商户、服务商、商家代调用和分账能力设置不同的主体、行业、资质及交易场景要求。开发前,需要在开放平台控制台确认:
- 平台主体是否可以签约所需产品;
- 入驻商家是否必须逐户签约;
- 应采用第三方应用授权还是其他服务商模式;
- 是否允许分账以及分账比例限制;
- 退款、结算和手续费由哪一方承担。
适合这一场景的架构是:平台直接收取会员费,店铺订单款直接进入对应商家的账户。只有取得正式的分账能力后,才使用统一收款并分配资金的方案。
备注:内容仅供参考。
