Java

APP内嵌网页使用微信或支付宝的H5支付

2020-09-25  本文已影响0人  Vchar_Fred

APP内嵌网页使用微信或支付宝的H5支付

微信和支付宝的H5支付下单成功后都会返回一个跳转支付的url连接,通过这个连接可以拉起微信或支付宝进行支付操作。

如果直接访问,支付宝会有一个中间的页面,而微信有个麻烦的refresh验证问题;那么是否可以跳过这个步骤直接将微信支付宝拉起进行支付呢?

网上大部分的教程都是让做安卓和IOS的自己去拦截微信和支付宝的地址进行处理。但是对这种内嵌网页,特别是那种直接通过前端HTML代码生成多端的情况,前端的同学就非常不好操作了;那么这个活就需要后端的同学辛苦哈来解决了(ง •_•)ง。

首先需要知道的是每一个手机APP都有一个唯一的URL Scheme地址,访问这个地址即可将对应的APP打开。
基于这个原理,那么微信和支付宝的支付最终肯定也是基于此来实现将其APP拉起然后让用户进行支付的。
因此让后端对支付地址处理下,直接返回可以拉起微信和支付宝的支付URL Scheme;这样就可以直接用了,微信的refresh验证也可以跳过了。

首先是微信H5支付

通过程序直接请求微信H5支付下单返回的支付链接,返回如下(下面是返回的部分html代码):

微信支付.jpg

在Html代码中有以weixin://开头的链接;而weixin://正好是微信的URL Scheme,这个就是之后调用微信支付的链接,在手机浏览器上打开这个链接正好可以调起微信支付进行支付。
说明微信在这个页面上并没有做其他的骚操作,那些Referer拦截只是一些简单的前台拦截。那么我们通过后端程序直接去请求微信返回H5支付链接,然后将返回的HTML中的微信支付URL Scheme提取出来直接返回给前端即可。

下面是Java示例代码

    HttpHeaders headers = new HttpHeaders();
    headers.add("Host", "wx.tenpay.com");
    headers.add("Accept-Language", "en, zh-CN; q=0.8,zh; q=0.6,en-US; q=0.4");
    headers.add("Accept", "text/html,application/xhtml+xml, application/xml ; g=0. 9 ,image/webp,*/* ; q=0.8");
    headers.add("Upgrade-Insecure-Requests", "1");
    // 这个地方写你自己在微信支付后台配置的安全域名
    headers.add("Referer", "https://www.baidu.com");
    HttpEntity<String> httpEntity = new HttpEntity<>(headers);

    try{
        // 使用spring的 RestTemplate; mweb_url是微信的H5支付链接
        ResponseEntity<String> exchange = this.restTemplate.exchange(mweb_url, HttpMethod.GET, httpEntity, String.class);
        String body = exchange.getBody();
        if(StringUtils.isBlank(body)){
            System.out.println("请求无响应");
            return url;
        }
        // 通过正则表达式提取需要的字符串
        String pattern= "\"weixin(.*?)\"";
        Pattern p = Pattern.compile(pattern);
        Matcher matcher = p.matcher(body);
        if(matcher.find()){
            String pullUrl = matcher.group();
            return pullUrl.substring(1, pullUrl.length()-1);
        }
    }catch (Exception e){
        System.out.println("请求异常");
    }
    return url;

需要注意的是使用这种方式就不要再将回跳地址传入了,同时需要自己做个是否支付成功的判断;也就是说在支付成功后我们的页面是不会自动回跳的,因此需要让前端的同学辛苦处理哈;比如弄个支付确认弹窗让用户主动点击,或者是定期去轮询几次订单状态,同时如果不条支付中间页那么最好把支付按钮暂时禁用了,免得由于网络延迟这些问题导致重复支付的问题。

其实去查看微信支付宝的HTML页面,你会发现它们的就是通过简单的js来直接跳转的

支付宝H5支付

基于刚才微信的思路,使用同样的方式来处理支付宝的。支付宝返回的HTML内容中没有现成的支付宝支付的URL Scheme。通过调试和HTML代码分析,提取出其支付URL Scheme如下:

# 安卓的(实际测试中,苹果手机使用这个也可以拉起支付宝,可以直接使用一个)
alipays://platformapi/startApp?appId=102564&orderSuffix=' + o.android + '#Intent;scheme=alipays;package=com.eg.android.AlipayGphone;end
# 苹果的
alipay://alipayclient/?o.ios

其中o.ios和o.android的内容是使用url encoder编码了的;其中苹果的内容是如下的JSON串:

{
  "requestType": "SafePay",
  "fromAppUrlScheme": "alipays",
  "dataString": "h5_route_token=\"FPwoiehfPAWuiofw\"&is_h5_route=\"true\"&need_invoke_app=\"true\""
}

安卓的只有一个dataString的值。通过字段的值对比h5_route_token其值就是HTML中的session的值;在返回的HTML代码中有如下代码:

var inData = { "requestType": "SafePay", "fromAppUrlScheme": "alipays", "dataString": "h5_route_token=\"FPwoiehfPAWuiofw\"&is_h5_route=\"true\"&need_invoke_app=\"true\"" };

这个就是上面需要的内容,同样通过正则表达式将inData的值提取出来,然后手动来拼接这个支付的URL Scheme。

下面是Java示例代码:

HttpGet httpGet = new HttpGet(h5Url);
httpGet.setConfig(RequestConfig.custom()
        .setConnectTimeout(HttpConstants.CONNECT_TIMEOUT)
        .setConnectionRequestTimeout(HttpConstants.CONNECTION_REQUEST_TIMEOUT)
        .setSocketTimeout(HttpConstants.SOCKET_TIMEOUT).build());

CloseableHttpClient httpClient = HttpClientBuilder.create().build();
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
    if(response.getEntity()!=null){
        String body = EntityUtils.toString(response.getEntity(), "UTF-8");

        // 通过正则表达式提取需要的字符串;也可以直接提取session的值 `pattern = "'session':(.*)'";`
        String pattern= "inData =(.*)";
        Pattern p = Pattern.compile(pattern);
        Matcher matcher = p.matcher(body);
        if(matcher.find()){
            String pullUrl = matcher.group();
            if(pullUrl.length()>9){
                pullUrl = pullUrl.substring(9, pullUrl.length()-1);
                // 这个isAndroid值是让前端同学传入的,如果不想区分可以直接就用安卓的这个支付链接;因为苹果端的用这个链接也可以拉起支付宝支付
                if(isAndroid){
                    JSONObject params = JSONObject.parseObject(pullUrl);
                    if(params.getString("dataString")!=null){
                        pullUrl = params.getString("dataString");
                        // 安卓
                        return String.format("alipays://platformapi/startApp?appId=549984&orderSuffix=%s#Intent;scheme=alipays;package=com.eg.android.AlipayGphone;end", URLEncoder.encode(pullUrl, "utf-8"));
                    }
                }else {
                    // iso
                    return String.format("alipay://alipayclient/?%s", URLEncoder.encode(pullUrl.trim(), "utf-8"));
                }
            }
        }
        System.out.println("请求返回内容:"+ body);
    }else {
        System.out.println("无请求内容返回");
    }
} catch (IOException e) {
    System.out.println("处理异常");
}finally {
    try {
        httpClient.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
return null;

这种方式对原生的APP开发应该也适用,即不使用微信支付宝的APP支付方式,直接使用H5的支付方式,这样就无需再去对接其APP支付的SDK了。

上一篇下一篇

猜你喜欢

热点阅读