【微信支付】微信端的手機網頁支付 開發流程

 

-----------------------------------------------------------------------------------------------1.微信 手機網頁支付 流程圖------------------------------------------------------------------------------------------------------javascript

 

------------------------------------------------------------------------------------------------2.前臺頁面--------------------------------------------------------------------------------------------------php

根據上面的流程,下面一步一步的實現這個微信支付的邏輯。html

前臺頁面   userPayView.jspjava

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>模擬支付頁面--微信支付/支付寶支付</title>
    
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">

  </head>
  
  <body>
    <input type="text" value="" name="openID"/> <!-- 微信    所需OpenID -->
    <input type="text" value="" name="orderID"/><!-- 微信 支付寶[out_trade_no] -->
    <input type="number" value="" name="money"/><!-- 微信[分爲單位,不容許小數] 支付寶[total_amount 元爲單位,精確到小數點後2位] 商品價格  -->
    
    <input type="text" value="" name="subject"/><!-- 支付寶 商品的標題/交易標題/訂單標題/訂單關鍵字等。 -->
    <input type="text" value="" name="product_code"/><!-- 支付寶 銷售產品碼,商家和支付寶簽約的產品碼。該產品請填寫固定值:QUICK_WAP_WAY。 -->
     <input type="text" value="" name="body"/><!-- 支付寶  商品描述 -->
    
    <button class="payButton">微信支付</button>
    <button class="alipayButton">支付寶支付</button>
  </body>
  <script type="text/javascript" src="/resources/bootstrap-3.3.5-dist/js/jquery-1.10.2.min.js"></script>
  <script type="text/javascript" src="/wx/pay/pay.js"></script>
</html>
View Code

 

微信支付按鈕的點擊事件   pay.jsjquery

/**
     * 微信支付按鈕的點擊事件 
     */
    $(".payButton").click(function(){
        
        var openID = $("input[name='openID']").val();
        var orderID = $("inpuut[name='orderID']").val();
        var money = $("input[name='money']").val();
        /**
         * ①ajax  微信支付按鈕點擊事件
         */
        $.ajax({ 
            url: "/wx/PayOrder/unifiedOrder.jhtml", 
            type:"GET",
            data: {"openId":openID,"orderId":orderID,"money":money },
            traditional:true,
            success: function(response){
                if(response.length > 0){
                    /**
                     * ⑤獲取到prepayId  繼續ajax請求商戶服務器
                     */
                    $.ajax({ 
                        url: "/wx/PayOrder/createPayConfig.jhtml", 
                        type:"GET",
                        data: {"prepayId":response },
                        traditional:true,
                        success: function(response){
                            var data = eval('(' + response + ')'); 
                            var obj = data.msg;
                            
                            /**
                             * ⑦ 根據支付配置,使用微信瀏覽器內置對象WeixinJSBridge 調用支付頁面,完成支付操做,將支付結果返回給①中配置的回調函數
                             */
                            WeixinJSBridge.invoke('getBrandWCPayRequest',{  
                                "appId" : obj.appId,              //公衆號名稱,由商戶傳入  
                                "timeStamp":obj.timestamp,        //時間戳,自 1970 年以來的秒數  
                                "nonceStr" : obj.nonce,           //隨機串  
                                "package" : obj.packageName,      //商品包信息 prepay_Id拼接值
                                "signType" : obj.signType,        //微信簽名方式
                                "paySign" : obj.signature         //微信簽名  
                                },function(res){  
                                    
                                    if(res.err_msg == "get_brand_wcpay_request:ok" ) {  
                                        alert('支付成功');  
                                    }else if(res.err_msg == "get_brand_wcpay_request:cancel"){
                                        alert("支付過程當中用戶取消");
                                    }else if(res.err_msg == "get_brand_wcpay_request:fail"){
                                        alert("支付失敗");
                                    }
                            });  
                        }
                    });
                }
        }});
    });
View Code

 

----------------------------------------------------------------------------------------------3.獲取prepay_id時的配置實體類 也就是統一下單實體類-------------------------------------------------------------------------------------------------web

統一下單 入參  參考文檔:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1ajax

package net.shopxx.wx.pay;

/**
 * 微信支付  統一下單 接口提供必填參數的實體類
 * @author SXD
 *
 */
public class Unifiedorder {
    /**
     * 公衆帳號ID
     */
    private String appid;
    /**
     * 商戶號
     */
    private String mch_id;
    /**
     * 隨機字符串,長度要求在32位之內
     */
    private String nonce_str;
    /**
     * 簽名    經過簽名算法計算得出的簽名值
     */
    private String sign;
    /**
     * 商品描述
     */
    private String body;
    /**
     * 商戶系統內部訂單號  要求32個字符內
     */
    private String out_trade_no;
    /**
     * 本次支付總金額  單位爲分 不能帶小數點
     */
    private Integer total_fee;
    /**
     * 用戶IP
     */
    private String spbill_create_ip;
    /**
     * 通知地址
     * 異步接收微信支付結果通知的回調地址,通知url必須爲外網可訪問的url,不能攜帶參數
     */
    private String notify_url;
    /**
     * 交易類型
     * 取值以下:JSAPI,NATIVE,APP等
     * JSAPI  公衆號支付
     * NATIVE 掃描二維碼支付
     * APP  app支付
     */
    private String trade_type; 
    /**
     * 用戶標識  
     * trade_type=JSAPI時(即公衆號支付),此參數必傳,此參數爲微信用戶在商戶對應appid下的惟一標識
     */
    private String openid;
    
    
    
    public Unifiedorder() {
        this.trade_type = "JSAPI";//公衆號支付的方式
    }
    
    
    public String getAppid() {
        return appid;
    }
    public void setAppid(String appid) {
        this.appid = appid;
    }
    public String getMch_id() {
        return mch_id;
    }
    public void setMch_id(String mch_id) {
        this.mch_id = mch_id;
    }
    public String getNonce_str() {
        return nonce_str;
    }
    public void setNonce_str(String nonce_str) {
        this.nonce_str = nonce_str;
    }
    public String getSign() {
        return sign;
    }
    public void setSign(String sign) {
        this.sign = sign;
    }
    public String getBody() {
        return body;
    }
    public void setBody(String body) {
        this.body = body;
    }
    public String getOut_trade_no() {
        return out_trade_no;
    }
    public void setOut_trade_no(String out_trade_no) {
        this.out_trade_no = out_trade_no;
    }
    public Integer getTotal_fee() {
        return total_fee;
    }
    public void setTotal_fee(Integer total_fee) {
        this.total_fee = total_fee;
    }
    public String getSpbill_create_ip() {
        return spbill_create_ip;
    }
    public void setSpbill_create_ip(String spbill_create_ip) {
        this.spbill_create_ip = spbill_create_ip;
    }
    public String getNotify_url() {
        return notify_url;
    }
    public void setNotify_url(String notify_url) {
        this.notify_url = notify_url;
    }
    public String getTrade_type() {
        return trade_type;
    }
    public void setTrade_type(String trade_type) {
        this.trade_type = trade_type;
    }
    public String getOpenid() {
        return openid;
    }
    public void setOpenid(String openid) {
        this.openid = openid;
    }    
    
    
    
    
    
    
}
View Code

----------------------------------------------------------------------------------------------4.微信支付的實體類 也就是JS-SDK使用的配置信息實體類----------------------------------------------------------------------------------------------算法

微信內H5調起支付  配置信息 入參   參考文檔:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6spring

package net.shopxx.wx.pay;

/**
 * 微信支付  
 * JS-SDK使用的配置信息 實體
 * @author SXD
 *
 */
public class JsAPIConfig {
    
    /**
     * 公衆號id
     */
    private String appId;    
    /**
     * 時間戳
     */
    private String timeStamp;
    /**
     * 隨機字符串
     */
    private String nonceStr;
    /**
     * 訂單詳情擴展字符串 prepay_id=***
     */
    private String packageStr;
    /**
     * 簽名方式  暫支持MD5
     */
    private String signType;
    /**
     * 簽名
     * 
     * 簽名算法
     * 第一步,設全部發送或者接收到的數據爲集合M,將集合M內非空參數值的參數按照參數名ASCII碼從小到大排序(字典序),使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
        特別注意如下重要規則:
        ◆ 參數名ASCII碼從小到大排序(字典序);
        ◆ 若是參數的值爲空不參與簽名;
        ◆ 參數名區分大小寫;
        ◆ 驗證調用返回或微信主動通知簽名時,傳送的sign參數不參與簽名,將生成的簽名與該sign值做校驗。
        ◆ 微信接口可能增長字段,驗證簽名時必須支持增長的擴展字段
       第二步,在stringA最後拼接上key獲得stringSignTemp字符串,並對stringSignTemp進行MD5運算,再將獲得的字符串全部字符轉換爲大寫,獲得sign值signValue。
     * 
     */
    private String paySign;

    public JsAPIConfig(){
        signType = "MD5";
    }

    public String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getTimeStamp() {
        return timeStamp;
    }

    public void setTimeStamp(String timeStamp) {
        this.timeStamp = timeStamp;
    }

    public String getNonceStr() {
        return nonceStr;
    }

    public void setNonceStr(String nonceStr) {
        this.nonceStr = nonceStr;
    }

    public String getPackageStr() {
        return packageStr;
    }

    public void setPackageStr(String packageStr) {
        this.packageStr = packageStr;
    }

    public String getSignType() {
        return signType;
    }

    public void setSignType(String signType) {
        this.signType = signType;
    }

    public String getPaySign() {
        return paySign;
    }

    public void setPaySign(String paySign) {
        this.paySign = paySign;
    }
    
    
}
View Code

----------------------------------------------------------------------------------------------5.商戶處理後同步返回給微信   訂單成功的  實體類--------------------------------------------------------------------------------------------------------apache

返回信息實體    參考文檔:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1    返回結果

package net.shopxx.wx.pay;

/**
 * 微信支付    商戶處理後同步返回給微信 訂單成功的  實體
 * @author SXD
 *
 */
public class PayCallback {
    
        private String return_code;
        private String return_msg;

        public PayCallback() {
            this.return_code = "SUCCESS";
            this.return_msg = "OK";
        }

        public String getReturn_code() {
            return return_code;
        }

        public void setReturn_code(String return_code) {
            this.return_code = return_code;
        }

        public String getReturn_msg() {
            return return_msg;
        }

        public void setReturn_msg(String return_msg) {
            this.return_msg = return_msg;
        }
}
View Code

----------------------------------------------------------------------------------------------6.HTTP通訊類  在後臺訪問微信服務器的  通訊工具類----------------------------------------------------------------------------------------------------

package net.shopxx.wx.pay;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Iterator;
import java.util.Map;

import javax.activation.MimetypesFileTypeMap;

import org.springframework.stereotype.Component;


/**
 * 微信支付 
 * HTTP通訊類
 *
 */
@Component
public class HttpConnection {
    /** 
     * 向指定URL發送GET方法的請求 
     * @param url 發送請求的URL 
     * @param param 請求參數,請求參數應該是name1=value1&name2=value2的形式。 
     * @return URL所表明遠程資源的響應 
     */ 
    public  String get(String url, String param) throws Exception {  
        String urlName = url + "?" + param;  
        return get(urlName);
    }  
    
    /**
     * 向指定URL發送GET方法的請求
     * 
     * @param url
     *            發送請求的URL
     * @return URL所表明遠程資源的響應
     */
    public  String get(String url) throws Exception {
        String result = "";
        BufferedReader in = null;
        URL realUrl = new URL(url);
        URLConnection conn = realUrl.openConnection();
        conn.setRequestProperty("accept", "*/*");
        conn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.8");
        conn.setRequestProperty("Content-Type", "text/xml;charset=utf-8");
        conn.setRequestProperty("connection", "keep-alive");
        conn.setRequestProperty("user-agent",
                "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
        // 創建實際的鏈接
        conn.connect();

        // 定義BufferedReader輸入流來讀取URL的響應
        in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
        String line;
        while ((line = in.readLine()) != null) {
            result += line;
        }
        
        in.close();
        return result;
    }
    
    /**  
     * 向指定URL發送POST方法的請求  
     * @param url 發送請求的URL  
     * @param content 內容
     * @return URL所表明遠程資源的響應  
     * @throws Exception 
     */  
    public String post(String url,String content) throws Exception{
        String result = "";
        URL postUrl = new URL(url); 
        HttpURLConnection connection = (HttpURLConnection) postUrl 
                .openConnection(); 
        connection.setDoOutput(true); 
        connection.setDoInput(true); 
        connection.setRequestMethod("POST"); 
        connection.setUseCaches(false); 
        connection.setInstanceFollowRedirects(true); 
        connection.setRequestProperty("Content-Type", 
                "application/x-www-form-urlencoded"); 
        connection.connect(); 
        DataOutputStream out = new DataOutputStream(connection 
                .getOutputStream()); 
//        out.writeBytes(content); 
        out.write(content.getBytes("UTF-8"));
        out.flush(); 
        out.close(); // flush and close 
        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),"utf-8"));//設置編碼,不然中文亂碼 
        String line=""; 
        while ((line = reader.readLine()) != null){ 
            result += line;
        } 
        reader.close(); 
        connection.disconnect();
        return result; 
    }
    /**
     * 向指定URL發送POST方法的請求
     * @Title: post
     * @Description: TODO
     * @param @param url
     * @param @param textMap
     * @param @return    
     * @return String    
     * @throws
     */
    public String post(String url, Map<String, String> textMap){
        String res = "";
        HttpURLConnection conn = null;
        String BOUNDARY = "---------------------------123821742118716"; //boundary就是request頭和上傳文件內容的分隔符
        try {
            URL postUrl = new URL(url);
            conn = (HttpURLConnection) postUrl.openConnection();
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(30000);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("User-Agent",
                            "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)");
            conn.setRequestProperty("Content-Type",
                    "multipart/form-data; boundary=" + BOUNDARY);

            OutputStream out = new DataOutputStream(conn.getOutputStream());
            // text
            if (textMap != null) {
                StringBuffer strBuf = new StringBuffer();
                Iterator iter = textMap.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry entry = (Map.Entry) iter.next();
                    String inputName = (String) entry.getKey();
                    String inputValue = (String) entry.getValue();
                    if (inputValue == null) {
                        continue;
                    }
                    strBuf.append("\r\n").append("--").append(BOUNDARY).append(
                            "\r\n");
                    strBuf.append("Content-Disposition: form-data; name=\""
                            + inputName + "\"\r\n\r\n");
                    strBuf.append(inputValue);
                }
                out.write(strBuf.toString().getBytes());
            }

            byte[] endData = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();
            out.write(endData);
            out.flush();
            out.close();

            // 讀取返回數據
            StringBuffer strBuf = new StringBuffer();
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    conn.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                strBuf.append(line).append("\n");
            }
            res = strBuf.toString();
            reader.close();
            reader = null;
        } catch (Exception e) {
            System.out.println("發送POST請求出錯。" + url);
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
                conn = null;
            }
        }
        return res;
    }
    
    /**  
     * 向指定URL發送POST方法的請求 (帶文件) 
     * @param url 發送請求的URL  
     * @param textMap 文本參數鍵值
     * @param fileMap 文件鍵值
     * @return URL所表明遠程資源的響應  
     * @throws Exception 
     */  
    public String filePost(String url, Map<String, String> textMap,
            Map<String, String> fileMap) {
        String res = "";
        HttpURLConnection conn = null;
        String BOUNDARY = "---------------------------123821742118716"; //boundary就是request頭和上傳文件內容的分隔符
        try {
            URL postUrl = new URL(url);
            conn = (HttpURLConnection) postUrl.openConnection();
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(30000);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("User-Agent",
                            "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)");
            conn.setRequestProperty("Content-Type",
                    "multipart/form-data; boundary=" + BOUNDARY);

            OutputStream out = new DataOutputStream(conn.getOutputStream());
            // text
            if (textMap != null) {
                StringBuffer strBuf = new StringBuffer();
                Iterator iter = textMap.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry entry = (Map.Entry) iter.next();
                    String inputName = (String) entry.getKey();
                    String inputValue = (String) entry.getValue();
                    if (inputValue == null) {
                        continue;
                    }
                    strBuf.append("\r\n").append("--").append(BOUNDARY).append(
                            "\r\n");
                    strBuf.append("Content-Disposition: form-data; name=\""
                            + inputName + "\"\r\n\r\n");
                    strBuf.append(inputValue);
                }
                out.write(strBuf.toString().getBytes());
            }

            // file
            if (fileMap != null) {
                Iterator iter = fileMap.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry entry = (Map.Entry) iter.next();
                    String inputName = (String) entry.getKey();
                    String inputValue = (String) entry.getValue();
                    if (inputValue == null) {
                        continue;
                    }
                    File file = new File(inputValue);
                    String filename = file.getName();
                    String contentType = new MimetypesFileTypeMap()
                            .getContentType(file);
                    if (filename.endsWith(".png")) {
                        contentType = "image/png";
                    }
                    if (contentType == null || contentType.equals("")) {
                        contentType = "application/octet-stream";
                    }

                    StringBuffer strBuf = new StringBuffer();
                    strBuf.append("\r\n").append("--").append(BOUNDARY).append(
                            "\r\n");
                    strBuf.append("Content-Disposition: form-data; name=\""
                            + inputName + "\"; filename=\"" + filename
                            + "\"\r\n");
                    strBuf.append("Content-Type:" + contentType + "\r\n\r\n");

                    out.write(strBuf.toString().getBytes());

                    DataInputStream in = new DataInputStream(
                            new FileInputStream(file));
                    int bytes = 0;
                    byte[] bufferOut = new byte[1024];
                    while ((bytes = in.read(bufferOut)) != -1) {
                        out.write(bufferOut, 0, bytes);
                    }
                    in.close();
                }
            }

            byte[] endData = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();
            out.write(endData);
            out.flush();
            out.close();

            // 讀取返回數據
            StringBuffer strBuf = new StringBuffer();
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    conn.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                strBuf.append(line).append("\n");
            }
            res = strBuf.toString();
            reader.close();
            reader = null;
        } catch (Exception e) {
            System.out.println("發送POST請求出錯。" + url);
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
                conn = null;
            }
        }
        return res;
    }
}
View Code

----------------------------------------------------------------------------------------------7.微信 簽名 規則 工具類-------------------------------------------------------------------------------------------------------------------------------------------

簽名算法 規則:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3

package net.shopxx.wx.pay;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import javax.net.ssl.SSLContext;
import javax.security.cert.CertificateException;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.ssl.SSLContexts;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;


/**
 * 微信支付  微信公衆號發紅包 
 * 工具類
 * @author SXD
 *
 */
public class WeXinUtil {
    
    
    /**
     * 獲取用戶IP
     * @param request
     * @return
     */
    public static String getIp(HttpServletRequest request){
        String ipAddress = null;
         if (request.getHeader("x-forwarded-for") == null) {  
             ipAddress = request.getRemoteAddr();
         }else{
            if(request.getHeader("x-forwarded-for").length()  > 15){
                String [] aStr = request.getHeader("x-forwarded-for").split(",");
                ipAddress = aStr[0];
            } else{
                ipAddress = request.getHeader("x-forwarded-for");
            }
         } 
         return ipAddress;
    }
    
    /**
     * 簽名算法,生成統一下單中 必填項簽名
     * @param unifiedOrder  1.將統一下單實體中各個字段拼接  2.MD5加密  3.所有轉化爲大寫
     * @return    返回通過簽名算法生成的簽名 sign
     * 第一步的規則
     *     ◆ 參數名ASCII碼從小到大排序(字典序);
     *    ◆ 若是參數的值爲空不參與簽名;
     *    ◆ 參數名區分大小寫;
     *    ◆ 驗證調用返回或微信主動通知簽名時,傳送的sign參數不參與簽名,將生成的簽名與該sign值做校驗。
     *    ◆ 微信接口可能增長字段,驗證簽名時必須支持增長的擴展字段
     */
    
    /* 手動拼接方式
    public String createUnifiedOrderSign(Unifiedorder unifiedOrder){
        StringBuffer sign = new StringBuffer();
        sign.append("appid=").append(unifiedOrder.getAppid());
        sign.append("&body=").append(unifiedOrder.getBody());
        sign.append("&mch_id=").append(unifiedOrder.getMch_id());
        sign.append("&nonce_str=").append(unifiedOrder.getNonce_str());
        sign.append("&notify_url=").append(unifiedOrder.getNotify_url());
        sign.append("&openid=").append(unifiedOrder.getOpenid());
        sign.append("&out_trade_no=").append(unifiedOrder.getOut_trade_no());
        sign.append("&spbill_create_ip=").append(unifiedOrder.getSpbill_create_ip());
        sign.append("&total_fee=").append(unifiedOrder.getTotal_fee());
        sign.append("&trade_type=").append(unifiedOrder.getTrade_type());
        sign.append("&key=").append(KEY);

        return DigestUtils.md5Hex(sign.toString()).toUpperCase();
    }
    */
    
    /**
     * 拼接生成sign 簽名
     * @param unifiedOrder
     * @param KEY
     * @return
     * @throws Exception
     */
     public static String createUnifiedOrderSign(Object object,String KEY) throws Exception{
            StringBuffer sign = new StringBuffer();
            Map<String, String> map = getSortMap(object);

            boolean isNotFirst = false;

            for (Map.Entry<String, String> entry : map.entrySet()) {
                if(isNotFirst == true){
                    sign.append("&");
                }else{
                    isNotFirst = true;
                }

                sign.append(entry.getKey()).append("=").append(entry.getValue());
            }
            sign.append("&key=").append(KEY);

            return DigestUtils.md5Hex(sign.toString()).toUpperCase();

        }
     
     /**
      * 使用java反射機制,動態獲取對象的屬性和參數值,排除值爲null的狀況,並按字典序排序
      * @param object
      * @return
      * @throws Exception
      */
     private static Map<String, String> getSortMap(Object object) throws Exception{
            Field[] fields = object.getClass().getDeclaredFields();
            Map<String, String> map = new HashMap<String, String>();

            for(Field field : fields){
                 String name = field.getName();
                 String methodName = "get" + name.replaceFirst(name.substring(0, 1), name.substring(0, 1)
                         .toUpperCase());
                 // 調用getter方法獲取屬性值
//                 Method getter = object.getClass().getMethod(methodName);
//                 String value =  getter.invoke(object)+"";
                 field.setAccessible(true);
                 Object value = field.get(object);
                 if (value != null){
                     map.put(name, value.toString());
                 }
            }

            Map<String, String> sortMap = new TreeMap<String, String>(
                    new Comparator<String>() {
                        @Override
                        public int compare(String arg0, String arg1) {
                           
                            return arg0.compareTo(arg1);
                        }
                    });
            sortMap.putAll(map);
            return sortMap;
        }
     
     
     
     public static SSLContext getSSL() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException, java.security.cert.CertificateException {
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            //證書位置  放在本身的項目下面
            Resource resource = new ClassPathResource("apiclient_cert.p12");
            InputStream instream = resource.getInputStream();
            try {
                keyStore.load(instream, "填寫證書密碼,默認爲商戶號".toCharArray());
            } finally {
                instream.close();
            }
            SSLContext sslcontext = SSLContexts.custom()
                    .loadKeyMaterial(keyStore, "填寫證書密碼,默認爲商戶號".toCharArray())
                    .build();
            return sslcontext;
        }
     
}
View Code

----------------------------------------------------------------------------------------------8.封裝/解析xml消息的工具類-------------------------------------------------------------------------------------------------------------------------------------

package net.shopxx.wx.pay;

import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.naming.NoNameCoder;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;


/**
 * 微信支付   微信公衆號發紅包
 * 封裝/解析xml消息的工具類
 * @author SXD
 *
 */
public class XmlUtil {

    
    public XStream getXstreamInclueUnderline(){
         XStream stream = new XStream(new XppDriver(new NoNameCoder()) {

             @Override
             public PrettyPrintWriter createWriter(Writer out) {
                 return new PrettyPrintWriter(out) {
                     // 對全部xml節點的轉換都增長CDATA標記
                     boolean cdata = true;

                     @Override
                     @SuppressWarnings("rawtypes")
                     public void startNode(String name, Class clazz) {
                         super.startNode(name, clazz);
                     }

                     @Override
                     public String encodeNode(String name) {
                         return name;
                     }


                     @Override
                     protected void writeText(QuickWriter writer, String text) {
                         if (cdata) {
                             writer.write("<![CDATA[");
                             writer.write(text);
                             writer.write("]]>");
                         } else {
                             writer.write(text);
                         }
                     }
                 };
             }
         });
         
         return stream;
    }
    
    /**
     * 根據字符串 解析XML map集合
     * @param xml
     * @return
     * @throws DocumentException
     */
    public Map<String, String> parseXML(String xml) throws DocumentException{
        Document document = DocumentHelper.parseText(xml);
        Element element =document.getRootElement();
        List<Element> childElements = element.elements();
        Map<String,String> map = new HashMap<String, String>();
        
        map = getAllElements(childElements,map);
        
        map.forEach((k,v)->{
            System.out.println(k+">>>>"+v);
        });
        
        return map;
    }
    /**
     * 獲取 子節點的被迭代方法
     * @param childElements
     * @param mapEle
     * @return
     */
    private Map<String, String> getAllElements(List<Element> childElements,Map<String,String> mapEle) {
        for (Element ele : childElements) {
            if(ele.elements().size()>0){
                mapEle = getAllElements(ele.elements(), mapEle);
            }else{
                mapEle.put(ele.getName(), ele.getText());
            }
        }
        return mapEle;
    }

    
}
View Code

----------------------------------------------------------------------------------------------9.微信支付  商戶服務器  邏輯處理中心------------------------------------------------------------------------------------------------------------------------

package net.shopxx.wx.pay;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.thoughtworks.xstream.XStream;


/**
 * 微信支付    邏輯處理
 * @author SXD
 *
 */
@Controller
@RequestMapping("/wx/PayOrder")
public class PayOrderController {
    
        /**
         * 公衆帳號ID
         */
        @Value("${member.appid}")
        private String APPID;
        /**
         * 商戶號  暫未配置
         */
        private String MCHID;
        /**
         * key設置路徑:微信商戶平臺(pay.weixin.qq.com)-->帳戶設置-->API安全-->密鑰設置
         * 暫未配置
         */
        private String KEY;
        /**
         * 統一下單 URL
         */
        @Value("${unifiedOrderUrl}")
        private String unifiedOrderUrl;
        private XmlUtil xmlUtil = new XmlUtil();
        private HttpConnection httpConnection  = new HttpConnection();
    
        
        
        /**
         * ② 用戶支付按鈕後,先進入統一下單這個方法獲取prepay_id
         *進行統一下單 ,獲取預支付訂單的prepay_id
         * @param request
         * 
         * @param openId
         * @param orderId
         * @param money
         * @return
         * @throws Exception
         */
        @ResponseBody
        @RequestMapping("/unifiedOrder")
        public String unifiedOrder(HttpServletRequest request,String openId,String orderId,int money) throws Exception{
            
             Unifiedorder unifiedOrder = new Unifiedorder();
             unifiedOrder.setAppid(APPID);
            unifiedOrder.setMch_id(MCHID);
            String nonce = UUID.randomUUID().toString().replaceAll("-", "");
            unifiedOrder.setNonce_str(nonce);
            unifiedOrder.setBody("商品的描述");
            unifiedOrder.setOut_trade_no(orderId);
            unifiedOrder.setTotal_fee(money);
            unifiedOrder.setSpbill_create_ip(WeXinUtil.getIp(request));
            unifiedOrder.setNotify_url("http://weixin.myagen.com.cn/wx/PayOrder/wechatPayNotify");
            unifiedOrder.setOpenid(openId);
            String sign = WeXinUtil.createUnifiedOrderSign(unifiedOrder,KEY);
            unifiedOrder.setSign(sign);
            

            /**
             * 轉成XML格式 微信可接受的格式
             */
            xmlUtil.getXstreamInclueUnderline().alias("xml", unifiedOrder.getClass());
            String xml = xmlUtil.getXstreamInclueUnderline().toXML(unifiedOrder);

            /**
             * ③請求微信服務器 返回結果 獲取prepay_id
             */
            String response = httpConnection.post(unifiedOrderUrl, xml);
            System.out.println(response);
            Map<String, String> responseMap = xmlUtil.parseXML(response);
            /**
             * ④商戶服務器將prepay_id返回給前臺ajax
             */
            return responseMap.get("prepay_id");
        }
        
        
        /**
         * ⑥根據獲取到的prepay_Id ,組裝支付須要的相關配置參數,返回給前臺網頁
         * 獲取支付  配置
         * @param prepayId
         * @return
         * @throws Exception
         */
        @ResponseBody
        @RequestMapping("/createPayConfig")
         public JsAPIConfig createPayConfig(String prepayId) throws Exception {
                JsAPIConfig config = new JsAPIConfig();

                String nonce = UUID.randomUUID().toString().replaceAll("-", "");
                String timestamp = Long.toString(System.currentTimeMillis() / 1000);
                String packageName = "prepay_id="+prepayId;
                /**
                 * 生成簽名
                 */
                StringBuffer sign = new StringBuffer();
                sign.append("appId=").append(APPID);
                sign.append("&nonceStr=").append(nonce);
                sign.append("&package=").append(packageName);
                sign.append("&signType=").append(config.getSignType());
                sign.append("&timeStamp=").append(timestamp);
                sign.append("&key=").append(KEY);
                String signature = DigestUtils.md5Hex(sign.toString()).toUpperCase();

                config.setAppId(APPID);
                config.setNonceStr(nonce);
                config.setPackageStr(packageName);
                config.setTimeStamp(timestamp);
                config.setPaySign(signature);
                return config;
            }
        
        
        
        
        /**
         * ⑧ ⑨回調方法 接收到支付完成後的相關信息,根據是否支付成功 進行商戶服務器端的業務邏輯操做,並在最後返回給微信服務器 已經確認成功的信息
         * 回調方法 
         * 異步接收微信支付結果通知的回調地址,通知url必須爲外網可訪問的url,不能攜帶參數
         * @param request
         * @return
         */
        @ResponseBody
        @RequestMapping("/wechatPayNotify")
        public String wechatPayNotify (HttpServletRequest request){
            try {
                 Map<String, String> map = getCallbackParams(request);
                 if (map.get("result_code").toString().equalsIgnoreCase("SUCCESS")) {
                     //這裏寫成功後的業務邏輯
//                     String orderId = map.get("out_trade_no");
//                     orderService.updateConfirm(orderId);
                 }
            }catch(Exception e){
                System.out.println(e);
            }
            /**
             * ⑩返回給微信服務器 確認交易完成 
             */
            return getPayCallback(); 
        }
        
        
        
        /**
         * 接收支付完成後 微信服務器返回的request,解析返回字符串爲鍵值對
         * @param request
         * @return
         * @throws Exception
         */
         public Map<String, String> getCallbackParams(HttpServletRequest request)
                    throws Exception {
                InputStream inStream = request.getInputStream();
                ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = inStream.read(buffer)) != -1) {
                    outSteam.write(buffer, 0, len);
                }
                System.out.println("~~~~~~~付款成功~~~~~~~~~");
                outSteam.close();
                inStream.close();
                String result = new String(outSteam.toByteArray(), "utf-8");
                return xmlUtil.parseXML(result);
         }
        
        
         
        
        /**
         * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
         * 商戶處理後同步返回給微信參數
         * @return
         */
          public String getPayCallback(){
                PayCallback callback = new PayCallback();
                XStream stream = new XStream();
                stream.alias("xml",callback.getClass() );
                String xml = stream.toXML(callback);
                return xml;
          }
        
        
}
View Code

 

 

--------------------------------------------------------------------------------------------------------------如上,整個的微信 手機網頁內支付-------------------------------------------------------------------------------------------------

相關文章
相關標籤/搜索