支付宝小程序如何向服务商的特约商户发起支付
结论
可以实现,但不能因为“小程序主体与服务商主体相同”,就在前端把收款方改成某个特约商户。
正确的接入方式是使用支付宝服务商代商户模式:
- 服务商为特约商户完成支付宝小程序支付能力的签约或产品开通。
- 特约商户授权服务商应用代为调用支付接口,服务端取得该商户对应的
app_auth_token。如果接入的产品使用商户编号等专用字段,则按产品文档传入。 - 服务端在目标特约商户的授权上下文中调用
alipay.trade.create创建交易。 - 小程序取得支付宝交易号
trade_no后,调用my.tradePay拉起收银台。 - 支付结果以支付宝服务端的异步通知和主动查询为准。
资金是否进入特约商户账户,由签约关系、商户授权以及创建订单时使用的商户上下文决定,小程序前端无法决定收款方。
为什么不能直接指定收款商户
支付宝小程序只负责展示业务页面和拉起支付。交易创建、签名以及收款主体校验都在服务端完成。
以下做法无法可靠地让款项进入特约商户账户:
- 在小程序端传入特约商户 PID。
- 只修改
seller_id,但仍在服务商自己的签约上下文中创建订单。 - 把服务商私钥或
app_auth_token放入小程序代码。 - 直接使用服务商自己的普通商户支付能力替特约商户收款。
支付宝会校验应用、支付产品、商户签约、应用授权和交易参数。即使小程序与服务商属于同一主体,该小程序也不会因此自动获得所有特约商户的收款权限。
接入前需要完成的配置
1. 确认服务商和特约商户关系
特约商户需要通过支付宝允许的服务商拓展或商户进件流程完成入驻,并确认商户状态正常。至少应核对以下信息:
- 特约商户已经完成必要的实名或企业认证。
- 商户已开通适用于支付宝小程序场景的支付产品。
- 商户号、支付宝用户 ID 和应用授权关系等信息属于同一个真实商户。
- 商户的经营类目与实际业务相符。
仅在服务商后台保存一条商户资料,并不表示支付产品已经签约成功。
2. 准备服务商应用
服务端调用支付宝开放接口时,需要配置开放平台应用,通常包括:
app_id- 应用私钥
- 支付宝公钥或支付宝公钥证书
- 应用公钥证书
- 应用证书
- 支付宝根证书
- 支付结果通知地址
notify_url
如果平台要求通过第三方应用代商户调用接口,还要确认当前应用具备相应的第三方应用或服务商代调用能力。控制台入口名称可能随平台调整,具体应以当前开放平台页面和对应支付产品文档为准。
3. 取得特约商户授权
采用开放平台应用授权模式时,特约商户需要授权服务商应用。授权完成后,服务商通过授权接口取得并保存:
app_auth_tokenapp_refresh_token- 授权商户标识
- Token 到期时间和授权状态
保存 app_auth_token 时,必须将其与具体特约商户绑定,创建订单时不能混用。
部分特约商户产品不通过通用 app_auth_token 识别收款方,而是使用产品规定的商户编号、机构编号或其他字段。遇到这种情况,应严格按照产品文档传参,不能自行用 seller_id 替代。
4. 配置小程序支付能力
小程序需要开通或绑定对应的支付能力,并确认:
- 小程序已经上线,或处于允许支付联调的状态。
- 小程序
app_id与服务端配置一致。 - 支付产品支持当前小程序场景。
- 服务商应用、小程序和特约商户之间的关联符合开放平台要求。
如果一个小程序要为多家特约商户提供交易入口,服务端必须根据实际门店或商户关系选择收款商户,不能接受客户端任意传入商户号后直接下单。
推荐的支付流程
用户在小程序选择商品
↓
小程序向业务服务端提交下单请求
↓
服务端根据商品、门店确定特约商户
↓
服务端读取该商户的授权和签约信息
↓
服务端调用 alipay.trade.create
↓
支付宝返回 trade_no
↓
小程序调用 my.tradePay({ tradeNO })
↓
支付宝收银台完成支付
↓
支付宝向 notify_url 发送异步通知
↓
服务端验签、核对订单并更新状态
服务端创建订单示例
下面使用常见的 alipay.trade.create 加 app_auth_token 模式说明核心逻辑。不同版本的 Java SDK 可能使用不同的类名和初始化方式,应以项目实际依赖的版本为准。
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipay.com/gateway.do",
APP_ID,
APP_PRIVATE_KEY,
"json",
"UTF-8",
ALIPAY_PUBLIC_KEY,
"RSA2"
);
AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
request.setNotifyUrl("https://example.com/alipay/payment/notify");
AlipayTradeCreateModel model = new AlipayTradeCreateModel();
model.setOutTradeNo("ORDER_20260909_000001");
model.setTotalAmount("99.00");
model.setSubject("门店商品订单");
model.setBuyerId(alipayUserId);
request.setBizModel(model);
// 必须使用目标特约商户对应的授权令牌
AlipayTradeCreateResponse response =
alipayClient.execute(request, null, merchantAppAuthToken);
if (!response.isSuccess()) {
throw new IllegalStateException(
"支付宝创建交易失败:" +
response.getCode() + " / " +
response.getSubCode() + " / " +
response.getSubMsg()
);
}
String tradeNo = response.getTradeNo();
其中:
out_trade_no是业务系统订单号,必须保持唯一。total_amount应由服务端根据订单重新计算。buyer_id是当前支付宝用户标识,获取方式取决于小程序登录和用户授权流程。merchantAppAuthToken必须属于本次订单实际收款的特约商户。trade_no是支付宝创建交易成功后返回的交易号,需要传给小程序。
如果使用证书模式,应按照所选 SDK 的证书客户端方式初始化,不能混用证书公钥和普通支付宝公钥配置。
小程序拉起支付示例
my.request({
url: 'https://example.com/api/orders',
method: 'POST',
data: {
productId: 'P10001',
storeId: 'STORE_001'
},
success: (res) => {
const { tradeNo } = res.data;
my.tradePay({
tradeNO: tradeNo,
success: () => {
// 此处只能表示收银台调用结果。
// 最终订单状态仍需向业务服务端查询。
my.navigateTo({
url: '/pages/order-result/order-result'
});
},
fail: (error) => {
console.error('支付未完成', error);
}
});
}
});
客户端提交的 storeId 只能作为业务线索。服务端需要验证该门店确实对应当前商品和目标特约商户,不能直接信任客户端传入的商户信息。
异步通知处理
支付成功后,支付宝会向 notify_url 发送通知。服务端至少需要完成以下校验:
- 使用支付宝公钥或证书验签。
- 检查
app_id是否为本系统的应用。 - 检查
out_trade_no是否存在。 - 核对通知金额与服务端订单金额是否一致。
- 核对收款商户信息是否符合订单记录。
- 判断交易状态是否达到业务要求。
- 以幂等方式更新订单。
- 按照支付宝要求返回成功响应。
不能根据 my.tradePay 的成功回调直接发货。用户可能关闭页面,也可能发生网络中断,而且前端回调不能代替经过服务端验签的支付结果。
多个特约商户的订单设计
建议在业务订单中保存以下信息:
out_trade_no
merchant_id
store_id
app_auth_token_reference
amount
alipay_trade_no
payment_status
notify_received_at
不建议将完整的 app_auth_token 复制到每张订单中。订单可以保存商户授权记录的内部 ID,再通过安全存储读取有效 Token。
下单前应确定唯一的收款商户。一张普通支付订单通常不能把交易款直接结算给多个特约商户。如果业务涉及平台抽佣、多方分账或延迟结算,需要另行申请支付宝支持的分账、资金结算等产品,不能通过修改 seller_id 实现。
常见报错的排查方向
如果创建交易时出现“无权限”“产品未签约”“商户状态异常”或“应用授权无效”等错误,可以依次检查:
- 调用接口的
app_id是否正确。 - 当前应用是否具备代商户调用该接口的权限。
app_auth_token是否属于目标特约商户。- Token 是否过期、刷新失败或已被商户取消授权。
- 特约商户是否已经签约小程序支付产品。
- 小程序与支付应用是否完成必要的绑定或关联。
- 是否混用了沙箱环境和生产环境的应用、密钥、网关或 Token。
- 是否完整记录了接口返回的
code、sub_code和sub_msg。
安全和合规注意事项
- 私钥、证书和
app_auth_token只能保存在服务端。 - 金额、商品名称和收款商户必须由服务端确认。
- 不同特约商户的授权信息应隔离存储。
- Token 刷新后要及时替换,并处理授权撤销状态。
- 创建订单和处理异步通知时都要保证幂等。
- 日志中不能输出私钥、完整 Token 或用户敏感信息。
- 收款主体必须与真实经营者及签约资料一致。
- 如果接口文档要求传递特约商户专用参数,应以对应产品文档为准,不要同时传入彼此冲突的商户标识。
落地时需要确认的并不是“小程序和服务商是否同主体”,而是目标特约商户是否已经完成支付签约、是否授权服务商应用,以及服务端创建交易时是否使用了该商户的调用上下文。
备注:内容仅供参考。
