前提:需要一台nginx服務器做反向代理
前言:支付寶支付手機支付一共分4個頁面分別為1、輸入手機號頁面,2、輸入驗證碼頁面,3、輸入支付密碼頁面,4、支付結果頁面
1、配置nginx服務器為 https://mclient.alipay.com做反向代理,對app和支付寶做反向代理如下,要求支付寶的反向代理后的域名與app的域名為同域。
server { listen 80; server_name app.domin.com; access_log /var/website/app/logs/access.log; error_log /var/website/app/logs/error.log; location /app { proxy_pass http://xxx.xx.xxx.xxx:8080/app; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 100m; } location / { proxy_pass https://mclient.alipay.com/; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; client_max_body_size 100m; } }
2、前端需要嵌套一個iframe鏈接支付寶的反向代理地址,看js部分,對第1、2個頁面做前端域名替換因為不涉及到js問題,所以沒有做處理,第3、4個頁面涉及到js的調用,因為支付寶js的寫法導致原本綁定的事件不能執行,第3個頁面包含了【確認付款】這幾個字,所以以此為判斷條件,如果是第三個頁面就把一個表單插入到iframe的body中,用js把頁面的html提交到本文第5節的地址中去,又這個方法處理后增加一個chuliguo的標志重新返回html,使輸入密碼的js可以正常執行,第4步同理。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <c:set var="ctx" value="${ pageContext.request.contextPath }" /> <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>支付</title> <script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script> <style> .scroll-wrapper{ position: fixed; right: 0; bottom: 0; left: 0; top: 0; -webkit-overflow-scrolling: touch; overflow-y: scroll; } .scroll-wrapper { height: 100%; width: 100%; border: none; } </style> </head> <body> <iframe class="scroll-wrapper" src="${ ctx }/alipay/pay?id=${ id }&type=${ type }"> </iframe> <script> var form = "<form id='sbform' action='${ ctx }/alipay/rerender' method='post'><input type='hidden' id='submit_html' value='' name='html'></form>"; function buildRegex() { var op = "g"; return new RegExp("https://mclient.alipay.com", op); } $(".scroll-wrapper").get(0).onload = function(){ var docuemnt = $("iframe").get(0).contentWindow.document; var html = $(docuemnt).find("html").html(); if(html.indexOf("確認付款") > 0 ){ if(html.indexOf("chuliguo") < 0){ $(docuemnt).find("html").find('body').append($(form)); $(docuemnt).find("html").find('body').find('#sbform').find('#submit_html').val($(docuemnt).find("html").html()); $(docuemnt).find("html").find('body').find('#sbform').submit(); } }else if(html.indexOf("付款結果") > 0 ){ if(html.indexOf("chuliguo") < 0){ $(docuemnt).find("html").find('body').append($(form)); $(docuemnt).find("html").find('body').find('#sbform').find('#submit_html').val($(docuemnt).find("html").html()); $(docuemnt).find("html").find('body').find('#sbform').submit(); }else{ location.href = $(docuemnt).find("html").find('body').find(".J-return-link").find("a").attr("href"); } }else{ html = html.replace(buildRegex(), "http://app.domin.com") $(docuemnt).find("html").html(html); } $("iframe").show(); } </script> </body> </html>
3、生成支付寶訂單的時候把openapi的地址替換成項目中的一個action,其中urlService.getUrl返回的是http://app.domin.com/alipay/middle/
form = form.replace("https://openapi.alipay.com", urlService.getUrl("alipay", "middle", request))
4、表單提交到的本地方法如下,因為上一步中支付寶生成的表單中的地址是https://openapi.alipay.com/gateway.do,上一步替換完了就是app.domin.com/alipay/middle/gateway.do。或者上一步連gateway.do一起替換。不清楚可以此處打斷點調試。此方法就是對原本的openapi.alipay的請求做一個中轉,返回支付寶給的表單html。這一步不能直接對openapi域名反向代理,因為提交以后支付寶端直接跳轉到mclient地址,沒有替換域名的機會。此處把mclient.alipay.com替換成項目域名也就是app.domin.com,項目域名根目錄第一節中反向代理了mclient.alipay.com
@RequestMapping(value = "middle/gateway.do") public void middle(HttpServletRequest request,HttpServletResponse response) throws IOException{ String queryString = request.getQueryString(); String biz_content = request.getParameter("biz_content"); Map<String, String> param = new HashMap<>(); param.put("biz_content", biz_content); String res = HttpUtil.http("https://openapi.alipay.com/gateway.do?" + queryString, param,null); res = res.replaceAll("https://mclient.alipay.com", urlService.serverPath(request, "")); response.getWriter().write(res); }
5、后台需要一個重新渲染支付寶html的方法,把支付寶的地址換成我們反向代理支付寶的地址 ,此代碼在輸完手機號驗證碼輸入支付密碼的頁面調用見第2節的說明,因為有的步驟是前台做html中域名替換,導致這一步輸入不了密碼。處理完成后拼接一個chuliguo的input作為前台判斷的標志,防止循環重載此頁面。可結合前台代碼一起理解這一步的作用。
@RequestMapping(value = "rerender") public void rerender(HttpServletRequest request,HttpServletResponse response) throws IOException{ String html = request.getParameter("html"); html = html.replaceAll("https://mclient.alipay.com", "http://app.domin.com"); html += "<input type='hidden' value='chuliguo'>"; response.setHeader("X-XSS-Protection", "0"); response.getWriter().write(html); }