小红书小程序如何跳转并唤起支付宝完成订单支付?
结论
前端流程可以分为五个阶段:订单确认页 → 收银台页 → 服务端创建支付宝支付 → 唤起支付宝 → 返回后查询订单状态。
需要注意,小红书小程序是否能直接唤起支付宝、具体使用哪个 API,取决于开放平台当前提供的能力、客户端版本和应用资质。不要自行拼接 alipays:// Scheme,也不要在前端存放支付宝私钥或执行签名。如果平台不支持直接唤起,通常需要跳转到已加入白名单的 H5 收银台,再使用服务端返回的支付宝支付地址。
推荐流程
1. 创建订单
用户点击“立即支付”后,前端先请求业务服务端创建订单。服务端返回业务订单号,此时订单仍不能视为已支付。
async function createOrder() {
const result = await request({
url: '/api/orders',
method: 'POST',
data: {
skuId: currentSkuId,
quantity: 1,
addressId: selectedAddressId
}
});
return result.orderNo;
}
服务端需要重新计算商品价格、优惠和运费,不能直接采用前端提交的金额。
2. 跳转到收银台页面
订单创建成功后,前端跳转到小程序内的收银台页,并通过订单号加载支付信息。
下面用常见的 xhs.navigateTo 写法演示,实际 API 名称和参数应以接入的小红书小程序 SDK 文档为准。
async function submitOrder() {
try {
const orderNo = await createOrder();
xhs.navigateTo({
url: `/pages/cashier/index?orderNo=${encodeURIComponent(orderNo)}`
});
} catch (error) {
showToast('订单创建失败,请稍后重试');
}
}
不要通过页面参数直接传递金额、支付宝交易号或完整支付地址。收银台页应使用 orderNo 向服务端重新查询。
唤起支付宝
收银台页加载后,需要调用服务端支付接口。服务端负责:
- 校验订单状态和支付金额。
- 调用支付宝服务端接口创建支付。
- 保存业务订单号与支付宝交易信息的对应关系。
- 对请求参数进行签名。
- 返回平台允许使用的唤起参数或 H5 支付地址。
async function createAlipay(orderNo) {
return request({
url: `/api/orders/${encodeURIComponent(orderNo)}/payments`,
method: 'POST',
data: {
channel: 'ALIPAY'
}
});
}
方案一:使用小红书开放平台提供的支付或外部应用能力
如果开放平台明确提供了支付宝唤起 API,应把服务端返回的参数原样传给该 API:
async function payWithPlatformApi(orderNo) {
const payment = await createAlipay(orderNo);
// platformPaymentApi 仅为占位名称,
// 必须替换为小红书开放平台实际提供的 API。
platformPaymentApi({
paymentData: payment.paymentData,
success() {
confirmPaymentResult(orderNo);
},
fail(error) {
handlePaymentInterrupt(orderNo, error);
}
});
}
这里没有指定具体的唤起 API,因为不同接入模式、应用资质和 SDK 版本可能支持不同能力。接入前应确认开放平台是否允许小程序跳转支付宝,以及需要配置哪些域名、Scheme 和支付权限。
方案二:跳转到 H5 收银台
如果接入方案要求先进入指定页面,可以由服务端返回经过校验的 H5 收银台地址,再让小程序打开已配置业务域名的 WebView 页面。
小程序端示例:
async function openH5Cashier(orderNo) {
const payment = await createAlipay(orderNo);
if (!payment.cashierUrl) {
throw new Error('服务端未返回收银台地址');
}
xhs.navigateTo({
url:
'/pages/webview/index?url=' +
encodeURIComponent(payment.cashierUrl)
});
}
WebView 页面接收并加载该地址:
<web-view src="{{url}}"></web-view>
H5 收银台使用服务端返回的支付宝支付地址发起跳转:
async function startAlipay(orderNo) {
const response = await fetch(`/api/h5/orders/${encodeURIComponent(orderNo)}/alipay`, {
method: 'POST',
credentials: 'include'
});
if (!response.ok) {
throw new Error('创建支付失败');
}
const result = await response.json();
// payUrl 必须由可信服务端生成并校验。
window.location.assign(result.payUrl);
}
该地址能否在小红书内置 WebView 中唤起支付宝,需要通过真机测试确认,同时还要符合小红书和支付宝双方的域名、跳转和安全策略。如果客户端拦截外部跳转,应改用开放平台认可的支付能力,不能通过未经允许的 URL Scheme 绕过限制。
支付结果处理
前端回调不能作为最终依据
成功唤起支付宝只说明支付宝页面已经打开,不代表用户付款成功。支付 API 返回成功,也可能仅表示调用成功。实际使用中还可能出现以下情况:
- 用户付款后没有返回小红书。
- 用户在支付过程中取消。
- 网络中断,前端没有收到结果。
- 前端显示失败,但支付宝异步通知稍后到达。
- 用户反复点击,导致重复创建支付请求。
因此,订单是否支付成功必须由服务端确认。
服务端接收支付宝异步通知
服务端收到支付宝通知后,需要:
- 验证支付宝签名。
- 校验商户号、应用 ID、业务订单号和金额。
- 判断交易状态。
- 以幂等方式把订单更新为已支付。
- 按支付宝协议返回通知响应。
前端不能参与验签,也不能保存支付宝应用私钥。
返回小程序后主动查询
收银台页重新显示时,应主动查询订单状态。从支付宝返回后,页面通常不会重新创建,如果只在 onLoad 中查询,很容易漏掉支付结果。
Page({
data: {
orderNo: '',
checking: false
},
onLoad(options) {
this.setData({
orderNo: decodeURIComponent(options.orderNo || '')
});
},
onShow() {
if (this.data.orderNo) {
this.queryPaymentResult();
}
},
async queryPaymentResult() {
if (this.data.checking) {
return;
}
this.setData({ checking: true });
try {
const order = await request({
url: `/api/orders/${encodeURIComponent(this.data.orderNo)}`,
method: 'GET'
});
if (order.status === 'PAID') {
xhs.redirectTo({
url:
'/pages/pay-result/index?status=success&orderNo=' +
encodeURIComponent(this.data.orderNo)
});
} else if (order.status === 'CLOSED') {
showToast('订单已关闭');
} else {
this.setData({
paymentStatus: 'PENDING'
});
}
} catch (error) {
showToast('支付结果查询失败,请稍后刷新');
} finally {
this.setData({ checking: false });
}
}
});
xhs.redirectTo 也是示意写法,使用前应根据实际 SDK 核对。重定向可以避免用户返回后再次进入支付页。
如果支付宝异步通知存在延迟,可以进行有限次数的轮询:
async function waitForPaid(orderNo) {
const intervals = [1000, 1500, 2500, 4000];
for (const delay of intervals) {
const order = await queryOrder(orderNo);
if (order.status === 'PAID') {
return true;
}
if (order.status === 'CLOSED') {
return false;
}
await new Promise(resolve => setTimeout(resolve, delay));
}
return null;
}
轮询结束后仍无法确认结果时,不要直接显示“支付失败”。更合适的提示是“支付结果确认中”,同时提供“刷新状态”和“重新支付”按钮。
取消和异常处理
用户取消支付宝支付后,订单通常仍是 UNPAID 或 PENDING 状态。前端应保留订单,并允许用户重新发起支付:
async function handlePaymentInterrupt(orderNo, error) {
const order = await queryOrder(orderNo);
if (order.status === 'PAID') {
showPaySuccess(orderNo);
return;
}
if (isUserCancel(error)) {
showToast('支付已取消');
return;
}
showToast('暂未确认支付结果,请刷新后查看');
}
不要仅根据错误码立即关闭订单。未支付订单应由服务端按照超时规则关闭。
注意事项
- 不要默认使用小红书开放平台未明确支持的外部跳转方式,应以当前平台文档和审核规则为准。
- 支付宝订单创建、金额计算、签名和异步通知验签都应在服务端完成。
- 前端只传业务订单号,不传可能被篡改并直接生效的支付金额。
- 支付接口需要支持幂等,避免同一业务订单因重复点击产生多次履约。
- 收银台按钮需要防止重复点击,但不能只依靠前端锁定。
- H5 收银台域名、接口域名和回跳地址需要提前加入白名单,并使用 HTTPS。
- 不要在日志、页面参数或埋点中记录签名、令牌和完整支付凭证。
- 支付结果以服务端订单状态为准,前端回调只负责触发查询和改善交互。
- 必须在 Android、iOS 和不同客户端版本上进行真机测试,重点检查唤起、取消、返回、断网和重复支付场景。
备注:内容仅供参考。
