Android WebView调用支付宝后如何返回自己的App
结论
只用下面这段代码打开 alipays:// 地址,无法保证支付宝在支付成功或取消后自动返回你的 App:
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
activity.startActivity(intent);
这只是一次普通的 Android 应用跳转。支付宝不知道需要返回哪个 Activity,也不需要通过 setResult() 返回结果。因此,无论改用 startActivityForResult()、添加 Intent Flag,还是监听 WebView URL,都无法获得可靠的回跳能力。
正式支付场景应优先接入支付宝官方的 App 支付 SDK。订单由服务端创建并签名,客户端通过 SDK 发起支付和应用切换。最终支付状态必须以支付宝服务端异步通知或服务端主动查询的结果为准。
fromAppUrlScheme 是与 iOS URL Scheme 回跳机制相关的参数,不能直接用于 Android。也不要自行修改支付宝生成的 alipays://platformapi/startapp 地址。
为什么会停留在支付宝
Intent.ACTION_VIEW 只是让 Android 查找能够处理该 URI 的应用:
你的 App → ACTION_VIEW → 支付宝
这个调用没有建立支付结果回调协议。支付完成后显示什么页面、是否返回来源应用,都取决于支付宝如何处理该业务地址。
下面这些做法通常无法解决问题:
- 将
startActivity()改成startActivityForResult(); - 添加
FLAG_ACTIVITY_*; - 在
onResume()中直接把“重新获得焦点”视为支付成功; - 自行拼接或修改
alipays://platformapi/startapp参数; - 把 iOS 的
fromAppUrlScheme参数直接添加到 Android URL。
尤其不要根据 Activity 再次执行 onResume() 来判断支付成功。用户可能取消了支付、按下返回键,或者只是暂时切换了应用。
推荐方案:接入支付宝 App 支付 SDK
常见的接入流程如下:
- App 向自己的服务端提交订单创建请求。
- 服务端调用支付宝接口生成订单,并使用商户私钥签名。
- 服务端将完整的支付参数字符串返回给 App。
- App 使用支付宝 SDK 发起支付。
- SDK 返回客户端结果后,App 显示“正在确认支付结果”。
- 服务端接收支付宝异步通知,验签后更新订单状态。
- App 向自己的服务端查询订单状态。
客户端调用通常类似下面这样。具体的依赖版本、接口签名和参数要求,应以当前支付宝开放平台文档及所用 SDK 为准:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
PayTask payTask = new PayTask(activity);
// orderInfo 必须由服务端生成并签名,不要在客户端保存商户私钥。
Map<String, String> result = payTask.payV2(orderInfo, true);
activity.runOnUiThread(() -> {
String resultStatus = result.get("resultStatus");
String memo = result.get("memo");
// 此处结果只适合用于界面提示。
// 订单最终是否支付成功,应向自己的服务端查询。
showPaymentResult(resultStatus, memo);
queryOrderStatusFromServer();
});
});
支付调用不能在主线程执行,商户私钥也不能打包进 APK。订单签名必须由服务端完成。
SDK 返回的客户端结果不能作为最终记账依据。客户端结果可能被伪造,网络中断也可能造成“支付宝已经扣款,但 App 没有收到结果”的情况。服务端需要处理以下事项:
- 接收支付宝异步通知;
- 验证通知签名;
- 校验商户号、应用 ID、订单号和金额;
- 对通知进行幂等处理;
- 必要时主动查询订单状态。
如果必须继续使用二维码地址
二维码页面或 alipays://platformapi/startapp 地址并不是通用的 App 支付回调接口。除非对应的支付产品明确支持配置商户回跳地址,否则调用方无法强制支付宝在支付结束后打开自己的 App。
可以采用以下降级方案:
- 支付前提示用户:“支付完成后请返回本应用”;
- 用户回到 App 时,在
onResume()中向服务端查询订单; - 页面提供“我已完成支付”按钮,用户点击后查询服务端状态;
- 订单尚未确认时显示“支付结果确认中”,不要直接显示支付成功;
- 设置合理的查询次数和时间间隔,避免持续轮询。
例如:
private boolean waitingForPayment;
private void openAlipay(String url) {
waitingForPayment = true;
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
}
@Override
protected void onResume() {
super.onResume();
if (waitingForPayment) {
// onResume 只表示用户回到了当前 Activity,
// 不代表支付成功。
queryOrderStatusFromServer();
}
}
这种方案只能在用户主动返回 App 后查询支付状态,不能保证支付宝自动回跳。
自定义 Scheme 能否实现回跳
Android App 可以注册自己的自定义 Scheme,例如:
<activity
android:name=".PaymentResultActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="myapp"
android:host="payment-result" />
</intent-filter>
</activity>
访问下面的地址时,系统可以唤起对应的 Activity:
myapp://payment-result?orderId=123
但只有当支付宝所使用的支付产品明确允许配置回跳地址,并且确实会访问该地址时,这种方式才有效。仅在自己的 App 中注册 Scheme,或者随意将它添加到 alipays:// URL 中,并不会让支付宝自动回调。
对于受控的 HTTPS 页面,应优先考虑经过域名校验的 Android App Links。自定义 Scheme 可能被其他 App 抢占,而且回跳参数不能作为支付成功的凭证。
关于 fromAppUrlScheme
fromAppUrlScheme 与 iOS 通过 URL Scheme 唤回来源 App 的机制有关。即使某些旧资料或特定接入方式中出现过这个参数,也不能说明它适用于当前支付产品,更不能直接将其映射为 Android 参数。
Android 应用跳转可以使用 Intent、自定义 Scheme 或 App Links,但支付宝是否执行回跳,仍取决于所接入支付产品及官方 SDK 的协议支持。不要修改二维码内容、百分号编码参数或支付宝生成的签名字段,否则可能造成验签失败、参数失效,甚至引发安全风险。
正式场景应使用支付宝官方支持的 Android App 支付方案。如果业务只能使用二维码,就需要分别处理“返回 App”和“确认支付状态”,并以服务端查询或支付宝异步通知作为最终依据。
备注:内容仅供参考。
