APP內(nèi)嵌網(wǎng)頁使用微信或支付寶的H5支付
微信和支付寶的H5支付下單成功后都會(huì)返回一個(gè)跳轉(zhuǎn)支付的url連接呻疹,通過這個(gè)連接可以拉起微信或支付寶進(jìn)行支付操作。
如果直接訪問柏靶,支付寶會(huì)有一個(gè)中間的頁面,而微信有個(gè)麻煩的refresh驗(yàn)證問題;那么是否可以跳過這個(gè)步驟直接將微信支付寶拉起進(jìn)行支付呢袁余?
網(wǎng)上大部分的教程都是讓做安卓和IOS的自己去攔截微信和支付寶的地址進(jìn)行處理。但是對(duì)這種內(nèi)嵌網(wǎng)頁咱揍,特別是那種直接通過前端HTML代碼生成多端的情況颖榜,前端的同學(xué)就非常不好操作了;那么這個(gè)活就需要后端的同學(xué)辛苦哈來解決了(? ?_?)?煤裙。
首先需要知道的是每一個(gè)手機(jī)APP都有一個(gè)唯一的URL Scheme地址掩完,訪問這個(gè)地址即可將對(duì)應(yīng)的APP打開。
基于這個(gè)原理硼砰,那么微信和支付寶的支付最終肯定也是基于此來實(shí)現(xiàn)將其APP拉起然后讓用戶進(jìn)行支付的且蓬。
因此讓后端對(duì)支付地址處理下,直接返回可以拉起微信和支付寶的支付URL Scheme题翰;這樣就可以直接用了恶阴,微信的refresh驗(yàn)證也可以跳過了。
首先是微信H5支付
通過程序直接請(qǐng)求微信H5支付下單返回的支付鏈接遍愿,返回如下(下面是返回的部分html代碼):
在Html代碼中有以weixin://
開頭的鏈接存淫;而weixin://
正好是微信的URL Scheme,這個(gè)就是之后調(diào)用微信支付的鏈接沼填,在手機(jī)瀏覽器上打開這個(gè)鏈接正好可以調(diào)起微信支付進(jìn)行支付桅咆。
說明微信在這個(gè)頁面上并沒有做其他的騷操作,那些Referer攔截只是一些簡單的前臺(tái)攔截坞笙。那么我們通過后端程序直接去請(qǐng)求微信返回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");
// 這個(gè)地方寫你自己在微信支付后臺(tái)配置的安全域名
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("請(qǐng)求無響應(yīng)");
return url;
}
// 通過正則表達(dá)式提取需要的字符串
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("請(qǐng)求異常");
}
return url;
需要注意的是使用這種方式就不要再將回跳地址傳入了,同時(shí)需要自己做個(gè)是否支付成功的判斷梯澜;也就是說在支付成功后我們的頁面是不會(huì)自動(dòng)回跳的寞冯,因此需要讓前端的同學(xué)辛苦處理哈;比如弄個(gè)支付確認(rèn)彈窗讓用戶主動(dòng)點(diǎn)擊,或者是定期去輪詢幾次訂單狀態(tài)吮龄,同時(shí)如果不條支付中間頁那么最好把支付按鈕暫時(shí)禁用了俭茧,免得由于網(wǎng)絡(luò)延遲這些問題導(dǎo)致重復(fù)支付的問題。
其實(shí)去查看微信支付寶的HTML頁面漓帚,你會(huì)發(fā)現(xiàn)它們的就是通過簡單的js來直接跳轉(zhuǎn)的
支付寶H5支付
基于剛才微信的思路母债,使用同樣的方式來處理支付寶的。支付寶返回的HTML內(nèi)容中沒有現(xiàn)成的支付寶支付的URL Scheme尝抖。通過調(diào)試和HTML代碼分析毡们,提取出其支付URL Scheme如下:
# 安卓的(實(shí)際測試中,蘋果手機(jī)使用這個(gè)也可以拉起支付寶昧辽,可以直接使用一個(gè))
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的內(nèi)容是使用url encoder編碼了的衙熔;其中蘋果的內(nèi)容是如下的JSON串:
{
"requestType": "SafePay",
"fromAppUrlScheme": "alipays",
"dataString": "h5_route_token=\"FPwoiehfPAWuiofw\"&is_h5_route=\"true\"&need_invoke_app=\"true\""
}
安卓的只有一個(gè)dataString的值。通過字段的值對(duì)比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\"" };
這個(gè)就是上面需要的內(nèi)容青责,同樣通過正則表達(dá)式將inData的值提取出來,然后手動(dòng)來拼接這個(gè)支付的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");
// 通過正則表達(dá)式提取需要的字符串脖隶;也可以直接提取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);
// 這個(gè)isAndroid值是讓前端同學(xué)傳入的,如果不想?yún)^(qū)分可以直接就用安卓的這個(gè)支付鏈接暇检;因?yàn)樘O果端的用這個(gè)鏈接也可以拉起支付寶支付
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("請(qǐng)求返回內(nèi)容:"+ body);
}else {
System.out.println("無請(qǐng)求內(nèi)容返回");
}
} catch (IOException e) {
System.out.println("處理異常");
}finally {
try {
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
這種方式對(duì)原生的APP開發(fā)應(yīng)該也適用产阱,即不使用微信支付寶的APP支付方式,直接使用H5的支付方式块仆,這樣就無需再去對(duì)接其APP支付的SDK了构蹬。