手机网页获取 alipay.trade.app.pay 应答后如何唤醒支付宝
结论
alipay.trade.app.pay 是支付宝 App 支付接口。返回内容是供 Android/iOS 支付宝 SDK 使用的 orderString,不能直接拼成链接,在普通手机网页中唤醒支付宝。
如果支付入口是 H5 页面,应改用 alipay.trade.wap.pay。服务端生成并签名支付请求后,可以让浏览器跳转到支付宝网关,也可以向页面返回自动提交的 HTML 表单。
如果页面运行在自有 App 的 WebView 中,则可以通过 JSBridge 将完整的 orderString 交给原生代码,再由原生支付宝 SDK 发起支付。
为什么不能直接在网页中使用这段参数
示例中的关键参数是:
method=alipay.trade.app.pay
product_code=QUICK_MSECURITY_PAY
这说明订单是为 App 支付 SDK 创建的。它不是可以直接访问的支付地址,也不是供浏览器提交的 WAP 支付表单。
下面这种处理方式并不可靠:
location.href = 'alipays://...' + orderString;
原因主要有以下几点:
- App 支付的
orderString应由支付宝客户端 SDK 解析和处理。 - 浏览器对自定义 URL Scheme 的支持会受到操作系统、浏览器和支付宝版本的影响。
- 自行拼接 Scheme 可能破坏签名参数,也不符合 H5 支付的标准接入流程。
不要尝试将 alipay.trade.app.pay 的应答转换成所谓的”支付宝唤醒链接”。
手机网页的正确处理方式
1. 服务端改用 alipay.trade.wap.pay
服务端创建支付请求时,将接口改为:
alipay.trade.wap.pay
业务参数通常包括:
{
"out_trade_no": "201800000001201",
"product_code": "QUICK_WAP_WAY",
"subject": "购买套餐",
"total_amount": "9.90",
"quit_url": "https://example.com/order/201800000001201",
"timeout_express": "90m"
}
参数含义如下:
product_code使用QUICK_WAP_WAY。quit_url是用户取消支付后的返回地址。- 公共参数中还需要设置
return_url和notify_url。 return_url用于支付完成后的浏览器页面跳转。notify_url用于接收支付宝发给服务端的异步支付通知。
参数是否必填,以及具体的 SDK 方法名称,应以当前使用的支付宝开放平台文档和 Python SDK 版本为准。
2. 服务端生成支付表单或跳转地址
下面的伪代码展示了基本处理流程。build_alipay_wap_form 代表项目实际使用的支付宝 SDK 或签名封装:
from flask import Flask, Response
app = Flask(__name__)
@app.post("/pay")
def create_payment():
biz_content = {
"out_trade_no": "201800000001201",
"product_code": "QUICK_WAP_WAY",
"subject": "购买套餐",
"total_amount": "9.90",
"quit_url": "https://example.com/order/201800000001201",
"timeout_express": "90m",
}
form_html = build_alipay_wap_form(
method="alipay.trade.wap.pay",
biz_content=biz_content,
return_url="https://example.com/pay/return",
notify_url="https://example.com/pay/notify",
)
return Response(form_html, content_type="text/html; charset=utf-8")
实际生成的内容通常是一个提交到支付宝网关的表单,例如:
<form id="alipay-form"
method="post"
action="https://openapi.alipay.com/gateway.do">
<input type="hidden" name="app_id" value="你的应用 ID">
<input type="hidden" name="method" value="alipay.trade.wap.pay">
<input type="hidden" name="charset" value="utf-8">
<input type="hidden" name="sign_type" value="RSA2">
<input type="hidden" name="timestamp" value="2023-03-16 08:22:20">
<input type="hidden" name="version" value="1.0">
<input type="hidden" name="biz_content" value="经过正确转义的 JSON">
<input type="hidden" name="sign" value="服务端生成的签名">
</form>
<script>
document.getElementById('alipay-form').submit();
</script>
这个示例只用于说明浏览器端的提交方式。生产环境中,应由支付宝 SDK 或经过验证的服务端签名组件生成完整表单,不要在前端自行实现签名。
3. 网页跳转到服务端支付入口
前端可以提交表单,让浏览器直接进入服务端支付路由:
<form method="post" action="/pay">
<button type="submit">支付宝支付</button>
</form>
也可以在用户点击支付按钮后进行跳转:
document.querySelector('#pay-button').addEventListener('click', () => {
window.location.href = '/pay';
});
服务端返回 WAP 支付表单后,浏览器会进入支付宝收银台。支付宝客户端是否会被直接唤起,取决于支付宝收银台、浏览器环境和系统策略,商户页面不需要自行拼接 Scheme。
如果网页运行在自有 App 的 WebView 中
这种情况下仍可使用 alipay.trade.app.pay,但必须由原生层调用支付宝 SDK。
完整的处理链路是:
H5 页面
→ 调用 App 提供的 JSBridge
→ 原生层请求服务端创建订单
→ 服务端返回完整 orderString
→ 原生层调用支付宝 SDK
→ App 接收支付结果
→ 服务端根据异步通知确认最终状态
Android 原生代码通常采用类似下面的调用方式:
PayTask alipay = new PayTask(activity);
Map<String, String> result = alipay.payV2(orderString, true);
orderString 必须使用服务端返回的完整原始字符串。不要拆分参数后重新拼接,也不要随意执行 URL 解码,否则可能改变 +、/、= 等字符,导致验签失败。
H5 与原生层之间可以约定一个桥接方法,例如:
window.NativePay.alipayPay(orderString);
这是业务 App 自行实现的 JSBridge 接口,并非浏览器或支付宝提供的通用 JavaScript API。
签名和参数处理注意事项
不要在网页端签名
RSA2 私钥只能保存在服务端。前端代码、网页源码和 App 安装包都不适合存放应用私钥。
各端应按以下方式分工:
- 服务端创建商户订单。
- 服务端组织请求参数并生成 RSA2 签名。
- 前端负责提交表单、跳转或调用原生桥接。
- 服务端接收异步通知并验签。
不要解析后重新拼接 App 支付字符串
示例中的签名包含:
+
/
=
如果将整段字符串作为普通查询参数再次编码,或者先通过表单解析器解码再重新拼接,+ 可能被转换为空格,导致订单字符串失效。
如果必须通过 JSON 接口传递,应将它作为一个完整的字符串字段:
{
"order_string": "timestamp=...&app_id=...&sign=...&biz_content=..."
}
原生层取出 order_string 后,应直接将其传给支付 SDK。
不要依赖同步跳转判断支付成功
return_url 只表示浏览器跳转到了返回页面,不能证明支付已经成功。用户可能关闭页面,网络也可能中断,返回参数还可能被伪造。
订单的最终状态应以下列结果为准:
- 服务端收到支付宝异步通知,并且验签通过;
- 服务端主动调用交易查询接口,确认交易状态。
服务端还应校验:
app_idout_trade_no- 支付金额
- 收款方信息
- 交易状态
异步通知处理完成后,需要按照支付宝的要求返回成功响应。订单状态更新还必须具备幂等性,避免因重复通知而重复发货。
应该选择哪种方案
如果支付入口是 Safari、Chrome、微信内置浏览器或其他普通手机浏览器,应使用:
alipay.trade.wap.pay
如果入口是自有 App 内嵌的 H5 页面,并且 App 已经集成支付宝 SDK,可以使用:
alipay.trade.app.pay
此时 H5 只能将支付请求交给原生层,无法自行完成 App 支付。
当前这段 alipay.trade.app.pay 应答不能直接用于普通网页。更稳妥的做法是让服务端重新创建 alipay.trade.wap.pay 请求,再由手机浏览器提交支付宝返回的支付表单。
备注:内容仅供参考。
