跳转链接使用最佳实践
调用 支付咨询 接口后,针对不同支付方式,支付咨询 接口的响应中会返回不同类型的跳转 URL 用于签约流程的推进。本文为您提供不同端类型对应的跳转 URL 使用最佳实践。关于各个支付方式的 URL 实际返回情况,请参考 部分支付方式的URL返回类型汇总。
跳转 URL 参数
针对不同的商户应用端类型(Web, WAP, App),不同支付方式在 支付咨询 接口的响应中会返回以下三种跳转 URL 中的部分或全部,用于签约推进:
链接类型 | 描述 |
normalUrl | HTTPS 地址的 URL,用于同一个浏览器页面跳转至支付方式页面。 |
applinkUrl | 用于推进签约流程的 Android App Links 或 iOS Universal Links。 |
schemeUrl | 用于打开支付方式 app 的 scheme URL。 示例:boostapp://inAppDeeplink?deeplink_path=deeplink/onetimepayment&source=alipay-connect&codeValue=https%3A%2F%2Fglobal.alipay.com%2F2810020400931LFH2t2mq1jjJ8zSetyv31VU |
【注意】
- 在使用如上链接拉起签约时,可能因为买家没有安装支付方式 app 会发生链接跳转的异常,注意异常的处理。
- 部分支付方式的签约链接并不支持多次打开,因此若拉起失败,建议更换 authState 重新获取签约链接并拉起。
WEB端
在 WEB 端场景下,consult 接口的响应中会返回 normalUrl。
URL打开方式
获取 normalUrl 字段的值后,需要将页面重定向至该签约链接。建议您新建一个标签页打开该签约链接以便引导买家完成签约,新建页面代码样例如下:
if (serverResponse.normalUrl != null) {
window.open(serverResponse.normalUrl, '_blank');
}
买家体验
以 normalUrl 对应的页面是扫码签约或账密登录界面为例,下图展示了 Web 端签约流程的示意图:
WAP端
在 WAP 端场景下, consult 接口的响应中,不同支付方式可能会返回以下三种 URL 中的部分或全部:
- normalUrl
- applinkUrl
- schemeUrl
URL的使用策略
如果您收到的 consult 接口响应中,只返回了三种 URL 中的一种,则直接使用您收到的 URL 作为跳转链接。 如果您收到以上三种URL中的多个, 建议按照以下策略使用对应的URL:
- iOS 系统:优先使用 applinkUrl,次优使用 schemeUrl,最后选择使用 normalUrl。
- 安卓系统:优先使用 schemeUrl,次优使用 applinkurl,最后选择使用 normalUrl。
URL打开方式
使用 JavaScript 的页面跳转方法,可以使用如下代码打开 URL:
window.location.href = URL;
买家体验
链接类型 | 支付体验 |
normalUrl | 买家从商户 WAP 页面跳转至支付方式 WAP 页。支付方式 WAP 页面对买家呈现的内容取决于支付方式,分为以下两种类型:
|
applinkUrl | 根据买家是否安装支付方式 app,自动决定后续签约流程如何推进:
|
schemeUrl | 支付体验取决于买家是否安装了支付方式app:
|
下图展示了 WAP 端签约流程的示意图。浏览器需要做的是将页面重定向至跳转链接对应的页面,或新标签页打开跳转链接对应的页面。
APP端
在 APP 端场景下, consult 接口的响应中,不同支付方式可能会返回以下三种URL中的部分或全部:
- normalUrl
- applinkUrl
- schemeUrl
注意:在App端,除了上述用于跳转的 URL 参数, consult 接口的响应中还会返回 appIdentifier 参数。此参数是支付方式app对应的包名(package name),在app支付中用于判断钱包app是否安装,还可以用于避免在安卓系统中出现消除歧义框。
URL的使用策略
如果您收到的 consult 接口响应中,只返回了三种 URL 中的一种,则直接使用您收到的 URL 作为跳转链接。 如果您收到以上三种URL中的多个, 建议按照以下策略使用对应的URL:
- iOS 系统:优先使用 applinkUrl,次优使用 schemeUrl,最后选择使用 normalUrl。
- 安卓系统:优先使用 schemeUrl,次优使用 applinkurl,最后选择使用 normalUrl。
URL 打开方式
在 iOS 及 Android 系统中,可以通过以下方式打开跳转 URL :
iOS | Android |
|
|
打开方式优缺点对比
每种方式均有其优势,建议您根据系统情况及集成场景选择适合的打开方式。
打开方式 | 说明 | |
应用外跳转:调用 Android/iOS 方法打开跳转 URL | Alipay推荐使用此方案。
| |
应用内打开跳转 URL |
| WKWebView/WebView 是一个内置的控件,可以在应用程序中嵌入一个 Web 浏览器。你可以使用它来加载网站的链接。
|
| 属于基于 WKWebView/WebView 的升级方案,使用 JSBridge/JavascriptInterface 后,WebView 可以直接调用 Android/iOS 方法打开跳转链接。
| |
| 是 iOS/Android 官方提供的应用内打开链接的方式,与 WebView 相比,能力更全面。
| |
判断是否安装支付方式 app 后, 分别采取应用外跳转或应用内打开跳转 URL。 | 根据买家是否安装支付方式 app,分别提供不同的跳转链接打开方式。
|
代码示例(iOS)
方法1: 调用 iOS 方法打开跳转链接
以下代码示例是通过系统打开 URL 的方式,在 iOS 系统中实现从商户 app 跳转至支付方式 app:
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10.0) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:Url] options:@{} completionHandler:nil];
}else{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:Url]];
}
方法2:应用内打开跳转 URL
当您倾向于在支付流程中使用应用内打开网页的方式进行下单处理,使用以下URL类型:
- normalUrl
- applinkUrl
注意:部分支付方式不支持 WAP 页面签约,访问 normalUrl 时会经过重定向并触发 scheme 的跳转来唤醒对应的支付方式 app 。WKWebView 会拦截到 scheme 外跳应用,您需要操作 WKWebView 的 decidePolicyForNavigationAction 方法并调用 iOS 方法打开跳转链接,即可实现商户 WKWebView 跳转到支付方式 App 签约。
使用 WKWebView
以下代码示例使用 WKWebView 在 iOS 的应用内加载支付方式页面:
// 初始化 webview 配置
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
// 初始化 webView
WKWebView *webView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height) configuration:configuration];
webView.navigationDelegate = self;
//加载支付方式 URL
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:Url]]];
[self.view addSubview:self.webView];
以下代码示例展示操作 WKWebView 的 decidePolicyForNavigationAction
方法实现商户 WebView 跳转到支付方式 App 签约:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
// WKWebView默认拦截scheme 需在下面方法手动打开
// 打开外部应用 Safari等操作
NSURL *url = navigationAction.request.URL;
NSString *absoluteString = url.absoluteString;
NSString *scheme = url.baseURL.scheme;
DLog(@"navigationAction.request.URL.absoluteString=====> %@", absoluteString);
if ([absoluteString isEqualToString:@"about:blank"]) {
decisionHandler(WKNavigationActionPolicyCancel);
}
if (!([absoluteString containsString:@"https://"] || [absoluteString containsString:@"http://"])) {
if ([absoluteString containsString:@"://"]) {
NSString *urlHeader = [absoluteString componentsSeparatedByString:@"://"][0];
[MBProgressHUD showAutoMessage:[NSString stringWithFormat:@"Scheme\n%@://",urlHeader] toView:self.view];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self openAppWithUrlStr:navigationAction.request.URL];
decisionHandler(WKNavigationActionPolicyAllow);
});
}
} else {
decisionHandler(WKNavigationActionPolicyAllow);
}
}
使用 WKWebView 及 JSBridge
使用该方法会更加优化买家支付的体验,同时需要较强的开发能力,在通过 WKWebView 打开 URL 的基础上结合使用 JSBridge,有利于项目后续的维护及可扩展性,同时又能提高性能和安全性。使用 JSBridge可以帮助网页和原生应用之间的交互更加方便,实现 WAP 页面可以直接调用 app 的 native 方法。以下代码示例展示了在 WKWebView 中使用 JSBridge 的方法:
[self.wkWebView evaluateJavaScript:bridge completionHandler:nil];
使用SFSafariViewController
SFSafariViewController 支持 normalUrl, applinkUrl, 及 schemeUrl 的打开及渲染。与 WebView 相比,能力更全面,集成方式较为简单。使用 SFSafariViewController 打开链接的实现方式可参考iOS官方提供的 SFSafariViewController 使用文档。以下代码示例展示了使用 SFSafariViewController 的方法:
SFSafariViewController *safariVC = [[SFSafariViewController alloc] initWithURL:Url entersReaderIfAvailable:NO];
safariVC.delegate = self;
[self.navigationController presentViewController:safariVC animated:YES completion:nil];
方法3:判断支付方式 app 是否安装并分别处理
首先需要判断买家是否安装了支付方式 App ,再针对不同情况使用不同的 URL 打开方式进行处理。在 iOS 系统下,需要完成以下步骤:
- 在 info.plist 配置中添加 LSApplicationQueriesSchemes。需注意 info.plist 中对 scheme 有个数限制。
- 在代码中通过 canOpenURL 方法判断是否安装了支付方式 app。代码实现请参考以下示例:
NSString *walletSchemeUrl = @"gcash://";
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:walletSchemeUrl]]){
// 支付方式应用已安装,建议通过原生方式打开
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:Url] options:@{} completionHandler:nil];
return YES;
} else {
// 支付方式应用未安装,建议通过 WEBVIEW 加载 H5 页面
SFSafariViewController *safariVC = [[SFSafariViewController alloc] initWithURL:Url entersReaderIfAvailable:NO];
safariVC.delegate = self;
[self.navigationController presentViewController:safariVC animated:YES completion:nil];
return NO;
}
代码示例(Android)
方法1: 调用 Android 方法打开跳转链接
以下代码示例是通过系统打开 URL 的方式,在 Android 系统中实现从商户 app 跳转至支付方式 app:
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(Url));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// use the startActivity function to redirect to the wallet app
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
使用此方法可能会出现消除歧义对话框。
什么是消歧对话框
Android App Link 在安装应用时向谷歌服务器请求身份认证。 如果身份验证失败,则 Android 应用程序链接将失效,并且当买家尝试通过此类链接打开应用程序时会出现消歧对话框。 买家需要手动选择如何打开链接。
此图显示了当买家点击 Android 应用程序链接时出现的消歧对话框示例。 买家需要手动选择打开链接的方式:使用谷歌地图或谷歌浏览器。 |
防止消歧对话框出现
当出现消歧对话框时,买家需要手动选择应用程序的打开方式,影响支付成功率。 因此,您需要防止出现消歧对话框,请执行以下操作之一:
- 当买家安装了支付方式 app 时,指定支付方式的 packageName,打开支付方式的 URL。
- 当买家没有安装支付方式 app 时,使用下面的示例代码实现离开商户 app, 并打开跳转 URL:
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(applinkUrl));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//设置支付方式的包名
intent.setPackage(walletPackageName);
PackageManager packageManager = MainActivity.this.getPackageManager();
//查询是否有支持打开该链接的应用。
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
if (activities.size() <= 0) {
//支付方式 app 未安装。请重新初始化 Intent,该链接会由系统默认支持的方式打开。
intent = new Intent(Intent.ACTION_VIEW, Uri.parse(applinkUrl));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else {
//支付方式 app 已安装。通过指定支付方式包名打开支付方式 URL,消除歧义对话框不会出现。
startActivity(intent);
}
【注意】 以下列表展示了部分支付方式的 packageName 值:
- AlipayHK: hk.alipay.wallet
- GCash: com.globe.gcash.android
- TrueMoney: th.co.truemoney.wallet
- KakaoPay: com.kakao.talk
- Touch 'n Go: my.com.tngdigital.ewallet
- Dana: id.dana
方法2:应用内打开跳转 URL
当您倾向于在支付流程中使用应用内打开网页的方式进行下单处理,可以使用 WebView 容器加载 normalUrl 或 applinkUrl 为一个 WAP 页面:
- normalUrl
- applinkUrl
注意:部分支付方式不支持 WAP 页面授权, 访问 normalUrl 时会经过重定向并触发 scheme 的跳转来唤醒对应的支付方式 app 。WebView 会拦截到 scheme 外跳应用,您需要操作 WebView 的 shouldOverrideUrlLoading 方法并调用 Android 方法打开跳转链接,即可实现商户 WebView 跳转到支付方式 App 授权。
使用WebView
以下代码示例使用 WebView 在 Android 的应用内加载支付方式页面:
WebView webView = findViewById(R.id.webview);
//设置 webview 客户端
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
view.loadUrl(request.getUrl().toString());
return super.shouldOverrideUrlLoading(view, request);
}});
WebSettings webSettings = webView.getSettings();
//启用 javascript
webSettings.setJavaScriptEnabled(true);
//启用 scaling
webSettings.setSupportZoom(true);
//启用 scaling controls (buttons)
webSettings.setBuiltInZoomControls(true);
//Webview 有两种缓存模式,这里不使用缓存加载
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
//允许 JavaScript 打开新窗口 (默认为 false)
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
//允许 JavaScript 加在本地缓存
webSettings.setDomStorageEnabled(true);
//WAP 缓存大小 (无需手动设置)
//webSettings.setAppCacheMaxSize(1024 * 1024 * 8);
//WAP 缓存路径
String absolutePath = getApplicationContext().getCacheDir().getAbsolutePath();
//WAP 缓存大小
webSettings.setAppCachePath(absolutePath);
//是否允许 WebView 访问文件 (默认为 true)
webSettings.setAllowFileAccess(true);
//允许保存 WAP 缓存
webSettings.setAppCacheEnabled(true);
//使用概览模式时,如果页面宽度超过WebView显示,缩放页面以适应WebView(默认为false)
webSettings.setLoadWithOverviewMode(true);
//支持视口 HTML 元标签
webSettings.setUseWideViewPort(true);
//加载支付方式 URL
webView.loadUrl(Url);
以下代码示例使用 WebView 拦截到 scheme 外跳应用,通过对 WebView 设置 WebViewClient,使用 WebViewClient.shouldOverrideUrlLoading() 方法对 URL 进行拦截,拦截后判断其格式,然后 Native 解析到对应的方法和数据后执行本地方法:
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
if (request.getUrl().toString().startsWith("http")) {
view.loadUrl(request.getUrl().toString());
return super.shouldOverrideUrlLoading(view, request);
} else {
view.onPause();
view.stopLoading();
try {
Intent intent = Intent.parseUri(uri, Intent.URI_INTENT_SCHEME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
view.getContext().startActivity(intent);
} catch (URISyntaxException e) {
//alert not support this scheme
}
return false;
}
}
注意:如果您的应用有跳转外部 app 白名单,需注意白名单的处理与配置,考虑旧版兼容性问题并减少应用的发版次数。
使用Webview及JavascriptInterface
使用该方法会更加优化买家支付的体验,同时需要较强的开发能力,建议在 WevView 中使用 JavascriptInterface。使用 JavaScriptInterface 可以帮助 Android 应用程序和 WebView 之间进行双向通信,使得 WAP 页面可以直接调用 app 的 native 方法,让您的应用程序更加灵活和强大,有利于项目后续的维护及扩展。以下代码示例展示了在 Webview 中使用 JavascriptInterface 的方法:
- 在 WebView 里声明一个 JavascriptInterface
@JavascriptInterface
public void openActivity(String url) {
//根据实际情况添加到代码中
try {
Intent intent = Intent.parseUri(uri, Intent.URI_INTENT_SCHEME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} catch (Exception e) {
Toast.makeText(context, "App not installed", Toast.LENGTH_SHORT).show();
}
}
- 给 WebView 设置 JavascripInterface
mWebView.addJavascriptInterface(this, "bridge");
- 使用以下代码打开跳转 URL
window.bridge.openActivity("Url");
使用 Custom Tabs
Custom Tabs 支持 normalUrl, applinkUrl, 及 schemeUrl 的打开及渲染。与 WebView 相比,能力更全面,集成方式较为简单。以下代码示例展示了使用 Custom Tabs 的方法:
- Gradle 项目文件中引入 Custom Tabs
dependencies {
...
implementation "androidx.browser:browser:1.4.0"
}
- 使用 Custom Tabs 在应用中打开一个 Chrome Tab
// 使用 CustomTabsIntent.Builder 配置 CustomTabsIntent.
// 准备就绪后,调用 CustomTabsIntent.Builder.build() 来创建 CustomTabsIntent
// 使用 CustomTabsIntent.launchUrl() 启动所需的 URL
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.launchUrl(this, Uri.parse(url));
更多关于如何使用 CustomTabs 打开链接的信息,请参考 Android 官方提供的 Custom Tabs使用指南。
方法3:判断支付方式 app 是否安装并分别处理
首先需要判断买家是否安装了支付方式 App ,再针对不同情况使用不同的 URL 打开方式进行处理。
在 Android 系统下,此方案的实现需要完成以下步骤:
- 出于 Android 系统的隐私和安全政策,需要在 AndroidManifest.xml 中添加 <queries> 标签,以确保签约链接能打开对应的支付方式 App。<queries> 标签中需要配置支付方式 App 的包名(package name),对应的值可以从支付宝的 支付 接口返回中通过 appIdentifier 参数获取,或者联系 Alipay 技术支持。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.llw.scandemo">
...
<queries>
<package android:name="th.co.truemoney.wallet" />
<package android:name="com.eg.android.AlipayGphone" />
<package android:name="my.com.tngdigital.ewallet" />
...
</queries>
...
</manifest>
- 通过以下代码判断是否安装支付方式 app 并拉起签约:
String Url = "applinkUrl/schemeUrl";
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(Url));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setPackage("my.com.tngdigital.ewallet");
// 通过以下两行代码判断是否安装支付方式 app
PackageManager packageManager = MainActivity.this.getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
if (activities.size() <= 0) {
// 支付方式 app 未安装。建议在 WEBVIEW 中打开 H5 链接
intent = new Intent(MainActivity.this, WebviewActivity.class);
intent.setData(Uri.parse(Url));
startActivity(intent);
} else {
// 支付方式 app 已安装。通过原生方式唤起交易。
startActivity(intent);
}
} catch (Exception e) {
// 处理报错
e.printStackTrace();
}
买家体验
链接的不同打开方式下所对应的的签约跳转流程如下:
打开方式 | 签约跳转流程 |
应用外跳转:调用Android/iOS方法打开跳转URL | 应用外跳转。签约体验取决于买家是否安装了支付方式 app:
|
应用内打开跳转URL | 有应用外跳转及应用内跳转两种签约体验:
|
最佳实践
建议您根据不同支付方式做打开跳转URL的固定配置,或根据支付方式、设备环境和返回内容进行路由。下图展示了根据不同支付方式做固定配置的示例:
针对每一种支付方式,您在决策使用什么方式打开跳转 URL 时,建议按照以下方式进行判断: