支付宝小程序webview中的H5应调用哪种支付API?
结论
这种场景应接入“支付宝小程序支付”,不建议在小程序的 web-view 中直接调用手机网站支付。
建议采用以下流程:
- H5 请求商户服务端创建订单。
- 服务端调用支付宝开放平台的
alipay.trade.create,获取trade_no。 - H5 将支付请求发送给小程序页面。
- 小程序页面调用
my.tradePay唤起收银台。 - 服务端通过支付宝异步通知或主动查单,确认最终支付结果。
手机网站支付主要用于普通移动浏览器。支付过程依赖页面跳转、拉起支付宝客户端以及支付完成后的 URL 回跳。这些操作在小程序 web-view 的受限容器中可能被拦截,也可能无法正常返回,因此不适合作为首选方案。
推荐的调用链路
web-view 中的 H5
↓ 创建业务订单
商户服务端
↓ 调用 alipay.trade.create
支付宝服务端
↓ 返回 trade_no
H5 / 小程序页面
↓ 将 trade_no 交给小程序
小程序调用 my.tradePay
↓
支付宝收银台
这里涉及两个不同的 API:
alipay.trade.create:由商户服务端调用,用来创建支付宝交易。my.tradePay:由支付宝小程序客户端调用,用来唤起支付收银台。
支付宝应用私钥不能保存在 H5 前端,也不能让 H5 自行拼装或签名支付宝服务端请求。
实现步骤
1. H5 请求商户服务端创建订单
async function createOrder() {
const response = await fetch('/api/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify({
productId: 'PRODUCT_001'
})
});
if (!response.ok) {
throw new Error('创建订单失败');
}
return response.json();
}
金额、商品名称和商户订单号应由服务端根据业务数据生成,不能直接使用前端提交的金额。
2. 服务端调用 alipay.trade.create
下面的代码只展示关键逻辑。SDK 初始化、密钥配置和异常处理,需要结合实际使用的服务端技术栈补充:
const result = await alipaySdk.exec('alipay.trade.create', {
bizContent: {
out_trade_no: order.orderNo,
total_amount: order.amount,
subject: order.subject,
buyer_id: buyerId
},
notify_url: 'https://example.com/api/alipay/notify'
});
const tradeNO = result.tradeNo;
不同语言和版本的支付宝 SDK,字段名称可能有所不同,具体应以对应 SDK 的返回结构为准。交易创建成功后,服务端将 trade_no 返回给前端。
3. H5 通知小程序发起支付
H5 可以通过支付宝小程序 WebView Bridge 向宿主小程序发送消息:
<script src="https://appx/web-view.min.js"></script>
<script>
async function pay() {
try {
const order = await createOrder();
my.postMessage({
type: 'PAY',
orderNo: order.orderNo,
tradeNO: order.tradeNO
});
} catch (error) {
console.error(error);
}
}
</script>
WebView Bridge 的引入方式和可用能力可能随运行环境变化,应以当前支付宝小程序文档和实际基础库版本为准。
4. 小程序页面调用 my.tradePay
小程序页面收到 H5 消息后,在小程序环境中唤起支付:
<web-view
src="{{h5Url}}"
onMessage="handleWebViewMessage"
/>
Page({
handleWebViewMessage(event) {
// 消息数据的具体层级需根据当前基础库实际事件结构确认
const message =
event.detail && event.detail.data
? event.detail.data
: event.detail;
if (!message || message.type !== 'PAY') {
return;
}
my.tradePay({
tradeNO: message.tradeNO,
success: (result) => {
this.queryOrderStatus(message.orderNo);
},
fail: (error) => {
console.error('支付未完成', error);
}
});
},
queryOrderStatus(orderNo) {
// 向商户服务端查询订单状态
}
});
更稳妥的方式是让 H5 只传递商户订单号 orderNo,再由小程序向服务端换取对应的 trade_no,以降低支付参数被伪造的风险。
支付结果处理
my.tradePay 的成功回调只表示客户端支付流程返回成功,不能作为发货、充值或开通服务的唯一依据。
服务端还需要完成以下检查:
- 验证支付宝异步通知的签名。
- 校验
app_id、商户订单号、金额和收款方信息。 - 确认交易状态符合业务要求。
- 对同一通知进行幂等处理。
- 必要时调用支付宝交易查询接口,再次确认交易状态。
前端完成支付后,可以轮询商户服务端的订单查询接口:
async function waitForPaid(orderNo) {
const response = await fetch(
`/api/orders/${encodeURIComponent(orderNo)}`
);
const order = await response.json();
if (order.status === 'PAID') {
// 展示支付成功页面
}
}
注意事项
web-view中的页面域名需要加入小程序业务域名或相关白名单。- 支付宝应用、商户账号和小程序需要完成相应的签约、绑定和支付能力配置。
trade_no应由商户服务端创建,私钥不能放在 H5 或小程序代码中。- 不要直接信任 H5 传入的金额、订单归属或支付状态。
- 手机网站支付适用于普通移动网页。即使在某些情况下能够从
web-view中拉起,也可能遇到跳转、回跳和兼容性问题。 - 如果当前小程序账号尚未开通相应的支付能力,仅将接口改为
my.tradePay仍然无法完成支付。需要先检查产品签约、应用绑定和权限配置。
需要调整的并不是让 H5 改调另一个网页支付接口,而是让 H5 负责业务展示和下单,再把收银台调用交给支付宝小程序,由小程序通过 my.tradePay 完成支付。
备注:内容仅供参考。
