基于Activity上下文调用CMBApi
商户APP应基于Activity上下文对CMBApi进行调用,相关要点如下:
1、实现与CMBApi进行交互的Activity,并在AndroidManifest中配置入口,以作为招行APP回调访问的入口
2、创建CMBApi接口对象
3、通过CMBApi接口对象向招行APP发送请求
4、转发招行APP发送的Intent给CMBApi接口对象
5、处理结果响应
在AndroidManifest.xml中配置与CMBApi进行交互的Activity
<activity
android:name=""
android:label="@string/app_name"
android:exported="true"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="sdksample"/>
</intent-filter>
</activity>
备注:1.android:scheme由招行业务系统与商户之间协商配置
2.activity请配置为商户自己接受回调的activity
创建CMBApi接口对象
@Override
public void onCreate(Bundle savedInstanceState) {
…
CMBApiFactory.createCMBApi(this, APPID);
…
}
通过CMBApi接口对象向招行APP发送请求
final CMBRequest request = new CMBRequest();
request.mRequestData = requestData;
request.mCMBJumpUrl = jumpUrl;
request.mH5Url = h5Url;
request.mMethod = method;
cmbApi.sendReq(request);
转发招行APP发送的Intent给CMBApi接口对象
商户APP需要在onCreate(), onNewIntent()以及onActivityResult方法里面显式调用handleIntent方法,否则将收不到最终的业务处理结果回调。
1、onCreate()
@Override
public void onCreate(Bundle savedInstanceState) {
…
api.createCMBApi(this, APPID);
api.handleIntent(getIntent, this);
…
}
2、onNewIntent()
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
api.handleIntent(intent, this);
}
3、onActivityResult()
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
api.handleIntent(data, this);
}
处理结果响应
商户APP入口Activity应实现CMBEventHandler接口以接收业务结果响应:
public class CMBApiEntryActivity extends Activity implements
CMBApi.CMBEventHandler {
@Override
public void onResp(CMBResponse resp) {
if (0 == resp.mRespCode){
//handle result in resp.mRespMsg
}
else {
//handle errorCode in resp.mRespCode
//handle error in resp.mRespMsg
}
}
常见问题
Q1:scheme如何进行怎么设置?
A:scehme作为应用跳转的标识,推荐设置在当前商户应用的带有<category android:name="android.intent.category.LAUNCHER" />的activity中。商户可以选择配置在其他activity中,具体技术实现可参考网络教程。
参考链接:https://blog.csdn.net/xc765926174/article/details/51397847/
Q2:scheme是否可以设置多个?
A:可以,但不推荐商户这样做。影响:如果同一个应用设置多个相同scheme的话,支付成功返回时,安卓系统会弹框询问想要跳转去哪一个应用,进行一个选择。
Q3:测试招行SDK的app支付时,回调结果没有走回到onReps()方法如何处理?
A:请商户先确定所有机型皆是如此,还是只有特例机型。
特例机型建议查看:1、是否手机安全中心有所限制;2、确定是否是打包时混淆问题,使用非混淆的app进行测试。
所有机型都收不到回调:建议商户自己写一个scheme跳转应用进行测试,看看能否跳转到商户app中。如果自测能够跳转成功,建议联系招行技术支持小组,给出具体的请求报文。
Q4:测试招行SDK的H5支付时,回调结果没有走回到onReps()方法如何处理?
内置H5页面采用intent的方式进行数据回传。请检查onActivityResult中是否添加cmbapi.handleIntent方法,以及此方法中参数是否正确。尤其是data参数,此处传参不能采用getIntent()方法,直接传data即可。
Q5:集成招行支付SDK之后报冲突,报错内容如下,如何进行处理?
Error:Attribute application@allowBackup value=(true) from [:XXX:] AndroidManifest.xml:XXX is also present at
[:cmbsdk-release-1.0:] AndroidManifest.xml:XXX value=(false).Suggestion: add 'tools:replace="android:allowBackup"'
A:商户应用manifest中设置allowBackup为false,并且加上tools:replace="android:allowBackup"
Q6:H5支付时如何定制导航栏?
A:考虑到各应用的导航栏会存在个性化需求,接口和页面不支持导航栏展示,如有显示需求,需应用自己开发访问H5页面。实现时需注意如下几点:
1、通过isCMBAPPInstalled判断是否已安装手机银行,已安装场景下通过sendRequest发送请求,未安装场景下才需访问H5页面。
2、需通过Post发送H5请求,body中包含信息见下图。
3、浏览器需注入CMBSDK.callback(respCode, respMessage)回调方法,用于支付结果知会。
4、HTTP请求头中需设置Content-Type为application/x-www-form-urlencoded,举例:[request setValue:@”application/x-www-form-urlencoded” forHTTPHeaderField:@”Content-Type”];
参数名 |
说明 |
cmbpbsdk_appid |
商户在招商银行业务功能系统中的appid,这里是10位商户号 |
cmbpbsdk_method |
业务功能类型 |
sdk_deviceType |
设备类型(D/E),D: iPhone, E: Android |
… |
支付、协议、领券业务功能等请求参数,具体内容由业务功能码给出具体内容。与CMBRequest的requestData一致。 |
拼接示例如下:
"cmbpbsdk_appid=0025000017& cmbpbsdk_method=pay&sdk_deviceType=D&"+ jsonRequestData
•注:如果是商户自定义实现H5,在H5支付场景下无需通过SDK的回调来识别
1、如果是支付页返回告知,商户通过注入js,在js实现方法中即可获知反馈结果。
2、如果是用户通过导航栏或手势等方式返回,商户可自行侦听这些行为来识别用户取消支付。
iOS具体实现参看如下:(任一方案即可,接入方自行选择)
方案一:WKWebview实现
1、JS注入代码
1. WKWebView *wkwb = WKWebView.new;
2. NSString *js = @"window.CMBSDK = window.CMBSDK || {};window.CMBSDK.callback = function(respCode,respMessage){var tmpResult = {};tmpResult.respCode = respCode;tmpResult.respMessage = respMessage;window.webkit.messageHandlers.CMBSDK.postMessage(JSON.stringify(tmpResult));};";
3.
4. //document开始时注入
5. WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
6. [wkwb.configuration.userContentController addUserScript:wkUScript];
7.
8. //CustomerJSHandler是自己创建的类需要实现WKScriptMessageHandler协议
9. CustomerJSHandler *handler = [[CustomerJSHandler alloc] init];
10. handler.webView = wkwb;
11.
12. [wkwb.configuration.userContentController removeScriptMessageHandlerForName:@"CMBSDK"];
13. [wkwb.configuration.userContentController addScriptMessageHandler:handler name:@"CMBSDK"];
2、实现CustomerJSHandler,该类需要实现WKScriptMessageHandler协议,并实现以下方法接收JS调用事件
1. - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
2. //通过解析 message中的body等信息处理具体接口的转发调用,body为json字符串,格式为{"respCode":"xx","respMessage":"xxxx"}
3. }
方案二:UIWebview注入实现
1、 增加JS方法映射
1. #import <JavaScriptCore/JavaScriptCore.h>
2.
3. @protocol CMBJSDelegate <JSExport>
4.
5. JSExportAs(callback,- (void)call:(NSString *)respCode back:(NSString *)respMessage);
6.
7. @end
2、头文件实现CMBJSDelegate协议
@interface xxxViewController:UIViewController<CMBJSDelegate>
3、页面加载完成后注入JS方法
1. - (void)webViewDidFinishLoad:(UIWebView *)webView {
2. JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
3. context[@"CMBSDK"] = self;
4. context.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
5. context.exception = exceptionValue;
6. };
7.
8. }
9.
10. - (void)call:(NSString *)respCode back:(NSString *)respMessage{
11. //根据响应码及内容进行后续逻辑处理
12. }
android具体实现参看如下:
1、webview注入JS
JS依赖注入,商户需自行定义。注入的交互方法固定为:public int callback(String respCode, String respString)。
以下为参考:
1. CMBSDKExecutor mCmbSdkExecutor = new CMBSDKExecutor();
注:CMBSDKExecutor仅供参考。
2. mWebView.addJavascriptInterface(mCmbSdkExecutor, "CMBSDK");
2、接收页面回调
Native和JS交互的方法为public int callback(String respCode, String respString)。JS页面在有结果回调时,会回调此native方法,请商户自行在此方法中实现想要实现的功能。比如:关闭当前页面。
@JavascriptInterface
public int callback(String respCode, String respString) {
}