三、APP接入

第一步(团油):商务联系确认

1.确认登录方式

静默登陆:需要合作平台提供用户手机号,进入登录页面则不需要注册。

非静默登陆:需要合作平台提供用户编号、用户ID,进入登录页面则需要注册。

2.确认平台类型

商用物流车、 专快车、出租车、私家车(50元注册券)

3.支付方式:微信支付/支付宝支付

第二步(团油):团油配置platform和测试链接。

第三步(合作方):合作方平台技术参考团油文档接入并测试。

第四步(合作方&团油):合作方将接入团油一键加油的测试安装包发送给团油公司进行交叉测试,保证整加油流程流畅。

第五步(团油):团油提供线上链接地址,宙斯系统后台账号。

第六步(合作方):合作方发布新版APP上线。

3.1 APP对接H5模式(传手机号方式)

链接说明:https://test-open.czb365.com/redirection/todo/?platformType=渠道编码&platformCode=用户手机号码

参数名 必填 说明
platformType 合作⽅代码,由团油提供
platformCode 合作方每个⽤户手机号,作为变量参数传输,由合作⽅技术提供

platformCode 传值说明

1.司机手机号,团油系统默认为免注册模式,⽆需绑定司机身份,司机使用体验好。

Android对接文档

(1) 导入jar和so文件

建议使用腾讯TBS浏览器服务,具体步骤可参考 文档

(2) 设置webview属性

 WebSettings webSetting = webView.getSettings();
 webSetting.setGeolocationEnabled(true);//启用地理定位必须设置成true
 webSetting.setUserAgent("合作方代码标识");

合作方代码标识:

⻋主邦识别合作方APP来源,用于区分iOS和Android端。
Android端合作方APP标识:合作方拼⾳缩写+Android。

说明

合作方拼音缩写:不限制长度,支持字母、数字,由合作方提供。如果合作方APP标识和现有团油对接平台的APP标识重复,团油会通知合作方更改。

(3) 拦截url做相应的处理

跳转支付宝或微信app,拦截url,根据如下条件判断

if (url.startsWith("weixin://") || url.contains("alipays://platformapi")) {//如果微信或者支付宝,跳转到相应的app界面,
    x5webView.goBack();
    try {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        intent.setData(Uri.parse(url));
        startActivity(intent);
    } catch (Exception e) {
        Toast.makeText(CustomNavigationActivity.this, "未安装相应的客户端", Toast.LENGTH_LONG).show();
    }
    return true;
}

这里跳转支付宝或微信时,加一个x5webView.goBack();防止加载页面出错。
使用微信支付时需要给webview设置header头方法代码如下:

Map extraHeaders = new HashMap();
extraHeaders.put(key, value);
webView.loadUrl(url, extraHeaders);

这里的key和value获取,会在后面介绍如何获取。

导航

自定义导航:h5会把经纬度传过来,拿到经纬度之后,app可选择自己的导航方式

网页导航:这里需要拦截url,示例代码见(3) 拦截url做相应的处理

跳转微信、支付宝和打开网页导航的示例代码

x5webView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView webView, String url) {
        Log.e("url=", url);
        if (url.startsWith("weixin://") || url.contains("alipays://platformapi")) {//如果微信或者支付宝,跳转到相应的app界面,
            x5webView.goBack();
            try {
                Intent intent = new Intent();
                intent.setAction(Intent.ACTION_VIEW);
                intent.setData(Uri.parse(url));
                startActivity(intent);
            } catch (Exception e) {
                Toast.makeText(WebPageNavigationActivity.this, "未安装相应的客户端", Toast.LENGTH_LONG).show();
            }
            return true;
        }
        // 使用h5默认导航需要添加,如果使用app本地导航,则不需要如下代码
        if (url.startsWith("androidamap://route")) {
            return true;
        }
        if (url.startsWith("http://ditu.amap.com")||url.startsWith("https://ditu.amap.com")){
            return true;
        }
        /**
            *
            * 设置 Header 头方法
            * window.czb.extraHeaders(String key, String value)
            */
        if (webPageNavigationJsObject != null && webPageNavigationJsObject.getKey() != null) {
            Map extraHeaders = new HashMap();
            extraHeaders.put(webPageNavigationJsObject.getKey(), webPageNavigationJsObject.getValue());
            webView.loadUrl(url, extraHeaders);
        } else {
            webView.loadUrl(url);
        }
        return true;

    }
});
}

(4) 接入相机

备注:如App无相机设置,该接入设置,非必须接入功能。

在webview 的 WebChromeClient 中添加用来打开相册:

x5webView.setWebChromeClient(new WebChromeClient() {
    //网页中选择设备中的图片
    // For Android 3.0+
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
        openFileChooserImpl(uploadMsg);
    }
    // For Android < 3.0
    public void openFileChooser(ValueCallback<Uri> uploadMsg) {
        openFileChooser(uploadMsg, "");
    }
    // For Android > 4.1.1
    @Override
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        openFileChooser(uploadMsg, acceptType);
    }
    // For Android > 5.0
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
        mUploadCallBackAboveL = filePathCallback;
        openFileChooserImplForAndroid5(filePathCallback);
        return true;
    }
    private void openFileChooserImpl(ValueCallback<Uri> uploadMsg) {
        mUploadCallBack = uploadMsg;
        takePhoto();
    }
    private void openFileChooserImplForAndroid5(ValueCallback<Uri[]> uploadMsg) {
        takePhoto();
    }
});

添加方法takePhoto获取照片

private void takePhoto() {
    Intent intent1 = new Intent(Intent.ACTION_GET_CONTENT);
    intent1.addCategory(Intent.CATEGORY_OPENABLE);
    intent1.setType("*/*");

    Intent intent2 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    mCameraFilePath = mActivity.getCacheDir().getPath() + File.separator + System.currentTimeMillis() + ".jpg";
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        // android7.0注意uri的获取方式改变
        Uri photoOutputUri = FileProvider.getUriForFile(
                mActivity,
                getPackageName() + ".fileprovider",
                new File(mCameraFilePath));
        intent2.putExtra(MediaStore.EXTRA_OUTPUT, photoOutputUri);
    } else {
        intent2.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mCameraFilePath)));
    }

    Intent chooser = new Intent(Intent.ACTION_CHOOSER);
    chooser.putExtra(Intent.EXTRA_TITLE, "File Chooser");
    chooser.putExtra(Intent.EXTRA_INTENT, intent1);
    chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{intent2});
    startActivityForResult(chooser, REQUEST_CODE_FILE_CHOOSER);
}

添加onActivityResult获取选择返回的照片

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE_FILE_CHOOSER) {
        Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
        if (result == null && !TextUtils.isEmpty(mCameraFilePath)) {
            // 看是否从相机返回
            File cameraFile = new File(mCameraFilePath);
            if (cameraFile.exists()) {
                result = Uri.fromFile(cameraFile);
                sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
            }
        }
        if (result != null) {
            String path = FileUtils.getPath(this, result);
            if (!TextUtils.isEmpty(path)) {
                File f = new File(path);
                if (f.exists() && f.isFile()) {
                    Uri newUri = result;
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        if (mUploadCallBackAboveL != null) {
                            if (newUri != null) {
                                mUploadCallBackAboveL.onReceiveValue(new Uri[]{newUri});
                                mUploadCallBackAboveL = null;
                                return;
                            }
                        }
                    } else if (mUploadCallBack != null) {
                        if (newUri != null) {
                            mUploadCallBack.onReceiveValue(newUri);
                            mUploadCallBack = null;
                            return;
                        }
                    }
                }
            }
        }
        clearUploadMessage();
        return;
    }

}

/**
 * webview没有选择文件也要传null,防止下次无法执行
 */
private void clearUploadMessage() {
    if (mUploadCallBackAboveL != null) {
        mUploadCallBackAboveL.onReceiveValue(null);
        mUploadCallBackAboveL = null;
    }
    if (mUploadCallBack != null) {
        mUploadCallBack.onReceiveValue(null);
        mUploadCallBack = null;
    }
}

(5) h5交互

示例代码:

   x5webView.loadUrl(url);//指定的url
   CustomNavigationJsObject customNavigation = new CustomNavigationJsObject(this);
   x5webView.addJavascriptInterface(customNavigation, "czb");//第二个参数czb不可更改,


   public class CustomNavigationJsObject {
     private Activity activity;
     private String key, value;

     public CustomNavigationJsObject(Activity activity) {
        this.activity = activity;
     }

    /**
     * @return 返回数据给h5
     * @JavascriptInterface 这个注解必须添加,否则js调不到这个方法
     * 这个方法名称也必须要和h5保持一致
     */
    @JavascriptInterface
    public void startNavigate(String startLat, String startLng, String endLat, String endLng) {
        //去做想做的事情。比如导航,直接带着开始和结束的经纬度Intent到导航activity就可以
        if (TextUtils.isEmpty(startLat) || TextUtils.isEmpty(startLng) || TextUtils.isEmpty(endLat)
                || TextUtils.isEmpty(endLng)) {//如果接收的数据不正确,给予提示
            Toast.makeText(activity, "有不正确的数据", Toast.LENGTH_LONG).show();
            return;
        }

        final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setTitle("提示");
        builder.setMessage("请调用自己的导航\n开始经纬度:" +
                startLat + "    " + startLng +
                "\n结束经纬度:" + endLat + "    " + endLng);

        builder.setPositiveButton("确定",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });

        builder.setCancelable(false);
        builder.show();

    }

    //拿到需要设置webView的微信下单属性
    @JavascriptInterface
    public void setExtraInfoHead(String key, String value) {
        setKey(key);
        setValue(value);
        Log.e("添加头信息", key + "," + value);
    }
    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
    public String getValue() {
        return value;
    }
    public void setValue(String value) {
        this.value = value;
    }

    // 除微信、支付宝外其他支付方式,如果不需要微信、支付宝外的其他支付,则不需要提供如下接口
    @JavascriptInterface
    public void requestPayment(String orderSn, String amount) {
        if (TextUtils.isEmpty(orderSn) || TextUtils.isEmpty(amount)) {//如果接收的数据不正确,给予提示
            Toast.makeText(activity, "有不正确的数据", Toast.LENGTH_LONG).show();
            return;
        }

        final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setTitle("提示");
        builder.setMessage("订单号:" +
                orderSn+
                "\n金额:" + amount);

        builder.setPositiveButton("确定",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });

        builder.setCancelable(false);
        builder.show();
    }
 }

IOS对接文档

(1) 导入文件

打开项目, 导入CZBWebProjectDemoForOC下的CZBWebManager文件夹下所有文件(包含CZBInfoManager CZBMapNavgation WHExteriorNavicationController).

(2) 初始化WebView

WKWebView为例, 并遵守WKNavigationDelegate WKUIDelegate代理协议. 注: 这里推荐使用WKWebView, 否则在跳转App支付的时候, 可能会造成Crash. 具体参考UIWebView在应用退出到后台仍然调用OpenGLAPI的问题. 解决方案参考链接https://www.jianshu.com/p/fca892de042f

设置WebView请求

参考DemoprepareWebview代码, 最重要的调用[CZBInfoManager registerWebView: self.webView];执行注册Js交互事件.

(3) 支付功能

微信/支付宝支付方式

*** 注: 该方法内部包含了是否调起App支付的操作, 为必须实现代码, 包含了微信/支付宝支付操作***

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

    BOOL isAllow = [CZBInfoManager webView:webView decidePolicyForNavigationAction:navigationAction];
    decisionHandler(isAllow ? WKNavigationActionPolicyAllow:WKNavigationActionPolicyCancel);
}

其它支付方式

可以找到CZBInfoManager下的+ (BOOL)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction方法, 在方法内部做处理. 例:


+ (BOOL)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction {
    /// 自定义支付请求
    if ([navigationAction.request.URL.absoluteString hasPrefix: @"xxxPay"]) {
        /// 自定义支付操作
    }
}

(4) 地图导航

CZBInfoManager中我们已经处理了导航请求, 默认不需要对接方做任何操作. 如需手动调起导航操作, 调用如下代码:

遵守代理方法

[CZBInfoManager shared].delegate = self;

实现代理事件

/**
 CZBInfoManagerDelegate代理方法,实现代理可定制自己的导航方法

 @param startLat 起点的维度
 @param startLng 起点的经度
 @param endLat 终点的维度
 @param endLng 终点的经度
 */
- (void)czbNavigation:(NSString *)startLat startLng:(NSString *)startLng endLat:(NSString *)endLat endLng:(NSString *)endLng

(5) 设置UA

UA为合作方拼⾳缩写+IOS, 在设置完成后执行loadRequest操作, 参考demoprepareWebview方法内部实现.

例:


[CZBInfoManager setUserAgent:@"CZBIOS" webView:self.webView completion:^{
    [self.webView loadRequest:req];
}];

说明

合作方拼音缩写:不限制长度,支持字母、数字,由合作方提供。如果合作方APP标识和现有团油对接平台的APP标识重复,团油会通知合作方更改。

(6) 接入相机

备注:如App无相机设置,该接入设置,非必须接入功能。

相机文案改为中文设置:

在Info.Plist里面把Localization native development region字段修改成China

在Info.Plist里面添加字段Localized resources can be mixed(Boolean)值为YES, 即可

(7) 支付完成后返回app设置

新增URL Schemes 设置为 msdev.czb365.com

实现wkwebview的代理方法

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSLog(@"最新的加载请求: === %@", navigationAction.request.URL.absoluteString);
    NSString *url = navigationAction.request.URL.absoluteString;
    if ([url hasPrefix:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb"] && ![url hasSuffix:@"redirect_url=msdev.czb365.com://"]) {
        decisionHandler(WKNavigationActionPolicyCancel);
        if ([url containsString:@"redirect_url="]) {
            NSArray *arr = [url componentsSeparatedByString:@"redirect_url="];
            url = [arr firstObject];
            _endPayRedirectURL = [arr lastObject];
            url = [url stringByAppendingFormat:@"redirect_url=msdev.czb365.com://"];
        } else {
            url = [url stringByAppendingFormat:@"redirect_url=msdev.czb365.com://"];
        }
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url] cachePolicy:(NSURLRequestUseProtocolCachePolicy) timeoutInterval:30];
        request.allHTTPHeaderFields = navigationAction.request.allHTTPHeaderFields;
        [webView loadRequest:request];
        return ;
    }
    ///解决微信支付返回后白屏问题
    if ([navigationAction.request.URL.scheme containsString:redirectScheme] && [webView canGoBack]) {
        [webView goBack];
    }
    ////////////////************************* 处理请求
    BOOL isAllow = [CZBInfoManager webView:webView decidePolicyForNavigationAction:navigationAction];
    WKNavigationActionPolicy actionPolicy = isAllow ? WKNavigationActionPolicyAllow:WKNavigationActionPolicyCancel;
    decisionHandler(actionPolicy);
    ////////////////************************* 处理请求
}

3.2 APP对接H5模式(传授权码方式)

链接说明:https://test-open.czb365.com/redirection/todo/?platformType=渠道编码&authCode=授权码

参数名 必填 说明
platformType 合作⽅代码,由团油提供
authCode 根据四、安全认证,五、获取授权码接口 生成授权码,作为参数拼到链接中

Android对接文档

(1) 导入jar和so文件

建议使用腾讯TBS浏览器服务,具体步骤可参考 文档

(2) 设置webview属性

 WebSettings webSetting = webView.getSettings();
 webSetting.setGeolocationEnabled(true);//启用地理定位必须设置成true
 webSetting.setUserAgent("合作方代码标识");

合作方代码标识:

⻋主邦识别合作方APP来源,用于区分iOS和Android端。
Android端合作方APP标识:合作方拼⾳缩写+Android。

说明

合作方拼音缩写:不限制长度,支持字母、数字,由合作方提供。如果合作方APP标识和现有团油对接平台的APP标识重复,团油会通知合作方更改。

(3) 拦截url做相应的处理

跳转支付宝或微信app,拦截url,根据如下条件判断

if (url.startsWith("weixin://") || url.contains("alipays://platformapi")) {//如果微信或者支付宝,跳转到相应的app界面,
    x5webView.goBack();
    try {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        intent.setData(Uri.parse(url));
        startActivity(intent);
    } catch (Exception e) {
        Toast.makeText(CustomNavigationActivity.this, "未安装相应的客户端", Toast.LENGTH_LONG).show();
    }
    return true;
}

这里跳转支付宝或微信时,加一个x5webView.goBack();防止加载页面出错。
使用微信支付时需要给webview设置header头方法代码如下:

Map extraHeaders = new HashMap();
extraHeaders.put(key, value);
webView.loadUrl(url, extraHeaders);

这里的key和value获取,会在后面介绍如何获取。

导航

自定义导航:h5会把经纬度传过来,拿到经纬度之后,app可选择自己的导航方式

网页导航:这里需要拦截url,示例代码见(3) 拦截url做相应的处理

跳转微信、支付宝和打开网页导航的示例代码

x5webView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView webView, String url) {
        Log.e("url=", url);
        if (url.startsWith("weixin://") || url.contains("alipays://platformapi")) {//如果微信或者支付宝,跳转到相应的app界面,
            x5webView.goBack();
            try {
                Intent intent = new Intent();
                intent.setAction(Intent.ACTION_VIEW);
                intent.setData(Uri.parse(url));
                startActivity(intent);
            } catch (Exception e) {
                Toast.makeText(WebPageNavigationActivity.this, "未安装相应的客户端", Toast.LENGTH_LONG).show();
            }
            return true;
        }
        // 使用h5默认导航需要添加,如果使用app本地导航,则不需要如下代码
        if (url.startsWith("androidamap://route")) {
            return true;
        }
        if (url.startsWith("http://ditu.amap.com")||url.startsWith("https://ditu.amap.com")){
            return true;
        }
        /**
            *
            * 设置 Header 头方法
            * window.czb.extraHeaders(String key, String value)
            */
        if (webPageNavigationJsObject != null && webPageNavigationJsObject.getKey() != null) {
            Map extraHeaders = new HashMap();
            extraHeaders.put(webPageNavigationJsObject.getKey(), webPageNavigationJsObject.getValue());
            webView.loadUrl(url, extraHeaders);
        } else {
            webView.loadUrl(url);
        }
        return true;

    }
});
}

(4) 接入相机

备注:如App无相机设置,该接入设置,非必须接入功能。

在webview 的 WebChromeClient 中添加用来打开相册:

x5webView.setWebChromeClient(new WebChromeClient() {
    //网页中选择设备中的图片
    // For Android 3.0+
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
        openFileChooserImpl(uploadMsg);
    }
    // For Android < 3.0
    public void openFileChooser(ValueCallback<Uri> uploadMsg) {
        openFileChooser(uploadMsg, "");
    }
    // For Android > 4.1.1
    @Override
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        openFileChooser(uploadMsg, acceptType);
    }
    // For Android > 5.0
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
        mUploadCallBackAboveL = filePathCallback;
        openFileChooserImplForAndroid5(filePathCallback);
        return true;
    }
    private void openFileChooserImpl(ValueCallback<Uri> uploadMsg) {
        mUploadCallBack = uploadMsg;
        takePhoto();
    }
    private void openFileChooserImplForAndroid5(ValueCallback<Uri[]> uploadMsg) {
        takePhoto();
    }
});

添加方法takePhoto获取照片

private void takePhoto() {
    Intent intent1 = new Intent(Intent.ACTION_GET_CONTENT);
    intent1.addCategory(Intent.CATEGORY_OPENABLE);
    intent1.setType("*/*");

    Intent intent2 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    mCameraFilePath = mActivity.getCacheDir().getPath() + File.separator + System.currentTimeMillis() + ".jpg";
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        // android7.0注意uri的获取方式改变
        Uri photoOutputUri = FileProvider.getUriForFile(
                mActivity,
                getPackageName() + ".fileprovider",
                new File(mCameraFilePath));
        intent2.putExtra(MediaStore.EXTRA_OUTPUT, photoOutputUri);
    } else {
        intent2.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mCameraFilePath)));
    }

    Intent chooser = new Intent(Intent.ACTION_CHOOSER);
    chooser.putExtra(Intent.EXTRA_TITLE, "File Chooser");
    chooser.putExtra(Intent.EXTRA_INTENT, intent1);
    chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{intent2});
    startActivityForResult(chooser, REQUEST_CODE_FILE_CHOOSER);
}

添加onActivityResult获取选择返回的照片

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE_FILE_CHOOSER) {
        Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
        if (result == null && !TextUtils.isEmpty(mCameraFilePath)) {
            // 看是否从相机返回
            File cameraFile = new File(mCameraFilePath);
            if (cameraFile.exists()) {
                result = Uri.fromFile(cameraFile);
                sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
            }
        }
        if (result != null) {
            String path = FileUtils.getPath(this, result);
            if (!TextUtils.isEmpty(path)) {
                File f = new File(path);
                if (f.exists() && f.isFile()) {
                    Uri newUri = result;
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        if (mUploadCallBackAboveL != null) {
                            if (newUri != null) {
                                mUploadCallBackAboveL.onReceiveValue(new Uri[]{newUri});
                                mUploadCallBackAboveL = null;
                                return;
                            }
                        }
                    } else if (mUploadCallBack != null) {
                        if (newUri != null) {
                            mUploadCallBack.onReceiveValue(newUri);
                            mUploadCallBack = null;
                            return;
                        }
                    }
                }
            }
        }
        clearUploadMessage();
        return;
    }

}

/**
 * webview没有选择文件也要传null,防止下次无法执行
 */
private void clearUploadMessage() {
    if (mUploadCallBackAboveL != null) {
        mUploadCallBackAboveL.onReceiveValue(null);
        mUploadCallBackAboveL = null;
    }
    if (mUploadCallBack != null) {
        mUploadCallBack.onReceiveValue(null);
        mUploadCallBack = null;
    }
}

(5) h5交互

示例代码:

   x5webView.loadUrl(url);//指定的url
   CustomNavigationJsObject customNavigation = new CustomNavigationJsObject(this);
   x5webView.addJavascriptInterface(customNavigation, "czb");//第二个参数czb不可更改,


   public class CustomNavigationJsObject {
     private Activity activity;
     private String key, value;

     public CustomNavigationJsObject(Activity activity) {
        this.activity = activity;
     }

    /**
     * @return 返回数据给h5
     * @JavascriptInterface 这个注解必须添加,否则js调不到这个方法
     * 这个方法名称也必须要和h5保持一致
     */
    @JavascriptInterface
    public void startNavigate(String startLat, String startLng, String endLat, String endLng) {
        //去做想做的事情。比如导航,直接带着开始和结束的经纬度Intent到导航activity就可以
        if (TextUtils.isEmpty(startLat) || TextUtils.isEmpty(startLng) || TextUtils.isEmpty(endLat)
                || TextUtils.isEmpty(endLng)) {//如果接收的数据不正确,给予提示
            Toast.makeText(activity, "有不正确的数据", Toast.LENGTH_LONG).show();
            return;
        }

        final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setTitle("提示");
        builder.setMessage("请调用自己的导航\n开始经纬度:" +
                startLat + "    " + startLng +
                "\n结束经纬度:" + endLat + "    " + endLng);

        builder.setPositiveButton("确定",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });

        builder.setCancelable(false);
        builder.show();

    }

    //拿到需要设置webView的微信下单属性
    @JavascriptInterface
    public void setExtraInfoHead(String key, String value) {
        setKey(key);
        setValue(value);
        Log.e("添加头信息", key + "," + value);
    }
    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
    public String getValue() {
        return value;
    }
    public void setValue(String value) {
        this.value = value;
    }

    // 除微信、支付宝外其他支付方式,如果不需要微信、支付宝外的其他支付,则不需要提供如下接口
    @JavascriptInterface
    public void requestPayment(String orderSn, String amount) {
        if (TextUtils.isEmpty(orderSn) || TextUtils.isEmpty(amount)) {//如果接收的数据不正确,给予提示
            Toast.makeText(activity, "有不正确的数据", Toast.LENGTH_LONG).show();
            return;
        }

        final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setTitle("提示");
        builder.setMessage("订单号:" +
                orderSn+
                "\n金额:" + amount);

        builder.setPositiveButton("确定",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });

        builder.setCancelable(false);
        builder.show();
    }
 }

IOS对接文档

(1) 导入文件

打开项目, 导入CZBWebProjectDemoForOC下的CZBWebManager文件夹下所有文件(包含CZBInfoManager CZBMapNavgation WHExteriorNavicationController).

(2) 初始化WebView

WKWebView为例, 并遵守WKNavigationDelegate WKUIDelegate代理协议. 注: 这里推荐使用WKWebView, 否则在跳转App支付的时候, 可能会造成Crash. 具体参考UIWebView在应用退出到后台仍然调用OpenGLAPI的问题. 解决方案参考链接https://www.jianshu.com/p/fca892de042f

设置WebView请求

参考DemoprepareWebview代码, 最重要的调用[CZBInfoManager registerWebView: self.webView];执行注册Js交互事件.

(3) 支付功能

微信/支付宝支付方式

*** 注: 该方法内部包含了是否调起App支付的操作, 为必须实现代码, 包含了微信/支付宝支付操作***

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

    BOOL isAllow = [CZBInfoManager webView:webView decidePolicyForNavigationAction:navigationAction];
    decisionHandler(isAllow ? WKNavigationActionPolicyAllow:WKNavigationActionPolicyCancel);
}

其它支付方式

可以找到CZBInfoManager下的+ (BOOL)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction方法, 在方法内部做处理. 例:


+ (BOOL)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction {
    /// 自定义支付请求
    if ([navigationAction.request.URL.absoluteString hasPrefix: @"xxxPay"]) {
        /// 自定义支付操作
    }
}

(4) 地图导航

CZBInfoManager中我们已经处理了导航请求, 默认不需要对接方做任何操作. 如需手动调起导航操作, 调用如下代码:

遵守代理方法

[CZBInfoManager shared].delegate = self;

实现代理事件

/**
 CZBInfoManagerDelegate代理方法,实现代理可定制自己的导航方法

 @param startLat 起点的维度
 @param startLng 起点的经度
 @param endLat 终点的维度
 @param endLng 终点的经度
 */
- (void)czbNavigation:(NSString *)startLat startLng:(NSString *)startLng endLat:(NSString *)endLat endLng:(NSString *)endLng

(5) 设置UA

UA为合作方拼⾳缩写+IOS, 在设置完成后执行loadRequest操作, 参考demoprepareWebview方法内部实现.

例:


[CZBInfoManager setUserAgent:@"CZBIOS" webView:self.webView completion:^{
    [self.webView loadRequest:req];
}];

说明

合作方拼音缩写:不限制长度,支持字母、数字,由合作方提供。如果合作方APP标识和现有团油对接平台的APP标识重复,团油会通知合作方更改。

(6) 接入相机

备注:如App无相机设置,该接入设置,非必须接入功能。

相机文案改为中文设置:

在Info.Plist里面把Localization native development region字段修改成China

在Info.Plist里面添加字段Localized resources can be mixed(Boolean)值为YES, 即可

(7) 支付完成后返回app设置

新增URL Schemes 设置为 msdev.czb365.com

实现wkwebview的代理方法

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSLog(@"最新的加载请求: === %@", navigationAction.request.URL.absoluteString);
    NSString *url = navigationAction.request.URL.absoluteString;
    if ([url hasPrefix:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb"] && ![url hasSuffix:@"redirect_url=msdev.czb365.com://"]) {
        decisionHandler(WKNavigationActionPolicyCancel);
        if ([url containsString:@"redirect_url="]) {
            NSArray *arr = [url componentsSeparatedByString:@"redirect_url="];
            url = [arr firstObject];
            _endPayRedirectURL = [arr lastObject];
            url = [url stringByAppendingFormat:@"redirect_url=msdev.czb365.com://"];
        } else {
            url = [url stringByAppendingFormat:@"redirect_url=msdev.czb365.com://"];
        }
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url] cachePolicy:(NSURLRequestUseProtocolCachePolicy) timeoutInterval:30];
        request.allHTTPHeaderFields = navigationAction.request.allHTTPHeaderFields;
        [webView loadRequest:request];
        return ;
    }
    ///解决微信支付返回后白屏问题
    if ([navigationAction.request.URL.scheme containsString:redirectScheme] && [webView canGoBack]) {
        [webView goBack];
    }
    ////////////////************************* 处理请求
    BOOL isAllow = [CZBInfoManager webView:webView decidePolicyForNavigationAction:navigationAction];
    WKNavigationActionPolicy actionPolicy = isAllow ? WKNavigationActionPolicyAllow:WKNavigationActionPolicyCancel;
    decisionHandler(actionPolicy);
    ////////////////************************* 处理请求
}

3.3 APP对接API+H5模式

链接说明:

从详情页开始对接H5链接

https://test-open.czb365.com/redirection/todo/?platformType=渠道编码&authCode=授权码&gasId=油站ID

从支付页开始对接H5链接

https://test-open.czb365.com/redirection/todo/?platformType=渠道编码&authCode=授权码&gasId=油站ID&gunNo=油枪号

具体API接口可以参考接口文档,群对接时给出。

Last Updated: 7/1/2020, 6:32:49 PM