微信公眾號支付


對開發測試成功后的源碼以及在開發中遇到的問題做一下簡要說明

 開發之前的准備工作:

    參數:開發之前你要知道請求每個接口地址的參數所代表的含義  //下面四個是必須知道的參數

 

       appid  //公眾號appid

         appsecret //公眾號密鑰

       mch_id //商戶id

       key  //商戶密鑰

       appid和appsecret 在微信公眾號中獲取,mch_id和key在公眾號對應的微信商戶號中獲取

    設置支付授權目錄

    開發之前先要熟悉一下業務流程 

      業務流程時序圖

  開發流程可以分為三步:

  1, 授權,獲取openid

  2, 調用統一下單API,獲取預支付信息

  3, 在頁面調用微信支付js並且完成支付

 

  一, 獲取openid

  1,  授權

      調用微信OAuth2.0網頁授權

    類 : GetCodeServlet.java  //getCode

public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        
        String appid = "";   //公眾號id
        String state = "0";
        String scope = "snsapi_base";
        String redirect_uri = "http://cmy.tunnel.qydev.com/wx_cmy/pay";  //重定向的url,就是授權后要跳轉的地址
        String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+appid+"&redirect_uri="+redirect_uri+"&response_type=code&scope="+scope+"&state="+state+"#wechat_redirect";
        //System.out.println("code === "+ url.toString());
        response.sendRedirect(url.toString());
    }

 

  scope:應用授權作用域

          snsapi_base:不彈出授權頁面,直接跳轉,只能獲取用戶openid

          snsapi_userinfo:彈出授權頁面,可通過openid拿到昵稱、性別、所在地

 

  2, 獲取用戶微信OpenId

  類:PayServlet.java  //pay   重定向之后的url

public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        /**
         * 獲取openid
         */
      

      //網頁授權后獲取傳遞的參數
      String code = request.getParameter("code");

        String appid = "";
        String appsecret = "";
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appid+"&secret="+secret+"&code="+code+"&grant_type=authorization_code";  //接口地址
        
        //System.out.println("pay code === "+ url.toString());
        
        String requestMethod = "GET";
        String outputStr = "";
        String httpRequest = WeixinUtil.httpRequest(url, requestMethod, outputStr); 
        JSONObject obj = JSONObject.fromObject(httpRequest);
        String openid = obj.get("openid").toString();
        //System.out.println("openid是"+openid);
        

  

   WeixinUtil.httpRequest(url, requestMethod, outputStr);

/**
     * 發起https請求並獲取結果
     * 
     * @param requestUrl 請求地址
     * @param requestMethod 請求方式(GET、POST)
     * @param outputStr 提交的數據
     * @return JSONObject(通過JSONObject.get(key)的方式獲取json對象的屬性值)
     */
    public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {
        StringBuffer buffer = new StringBuffer();
        try {
            // 創建SSLContext對象,並使用我們指定的信任管理器初始化
            TrustManager[] tm = { new MyX509TrustManager() };
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 從上述SSLContext對象中得到SSLSocketFactory對象
            SSLSocketFactory ssf = sslContext.getSocketFactory();

            URL url = new URL(requestUrl);
            HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
            httpUrlConn.setSSLSocketFactory(ssf);

            httpUrlConn.setDoOutput(true);
            httpUrlConn.setDoInput(true);
            httpUrlConn.setUseCaches(false);
            // 設置請求方式(GET/POST)
            httpUrlConn.setRequestMethod(requestMethod);

            if ("GET".equalsIgnoreCase(requestMethod))
                httpUrlConn.connect();

            // 當有數據需要提交時
            if (null != outputStr) {
                OutputStream outputStream = httpUrlConn.getOutputStream();
                // 注意編碼格式,防止中文亂碼
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }

            // 將返回的輸入流轉換成字符串
            InputStream inputStream = httpUrlConn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            String str = null;
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            bufferedReader.close();
            inputStreamReader.close();
            // 釋放資源
            inputStream.close();
            inputStream = null;
            httpUrlConn.disconnect();
            
        } catch (ConnectException ce) {

            System.out.println("---CMY---Weixin server connection timed out.");
        } catch (Exception e) {

            System.out.println("---CMY---https request error:{}");
        }
        return buffer.toString();
    }

 

 

   二, 調用統一下單API,獲取預支付信息

      在得到openid之后,調用統一下單接口

      通過openid獲取prepay_id 

//-------------統一下單---------------
        /**
         *  預支付
         */
        RequestHandler reqHandler = new RequestHandler(request, response);
        reqHandler.init(com.bean.Constants.appid, com.bean.Constants.appsecret, com.bean.Constants.key);
        //獲取openId后調用統一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder
                String requestXml="";
                try {
                    requestXml = CommonUtil.getSign(request, response,reqHandler, request.getRemoteAddr(), openid);   //獲取 請求參數 的xml,
          
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
                //.out.println(requestXml);
                
                 
                String createURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";   //統一下單接口url
    
                String prepay_id="";
                try {
                    prepay_id = new GetWxOrderno().getPayNo(createURL, requestXml);  //獲取 prepay_id
                    if(prepay_id.equals("")){
                        System.out.println("----------- 你好,我是分割線 ------------");
                        response.sendRedirect("error.jsp");
                    }
                } catch (Exception e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                System.out.println("--------------------- 超級華麗分割線 -----------------------");
                System.out.println("訂單id是"+prepay_id);                

CommonUtil.getSign(request, response,reqHandler, request.getRemoteAddr(), openid);
在這個方法里,生成請求參數的簽名,並把簽名放到請求參數拼接成的xml里
/**
     *  請求參數
     * @throws Exception
     */
    public static String getSign(HttpServletRequest request, HttpServletResponse response ,RequestHandler reqHandler,String ip,String openId) throws Exception {
        
        String appid = "";
        String mch_id= "";  //商戶id
        
        String nonce_str =  getNonce_str(); //隨機字符串 //商戶訂單號   自己定義
        String out_trade_no = "A"+TenpayUtil.getCurrTime();
    
        String notify_url  ="http://cmy.tunnel.qydev.com/wx_cmy/notify";  //接收微信支付異步通知回調地址
        
        String trade_type = "JSAPI";
        String openid = openId; //上面取到的openid
        
        
        SortedMap<String, String> packageParams = new TreeMap<String, String>();
        packageParams.put("appid", appid);  
        packageParams.put("mch_id", mch_id);  
        packageParams.put("nonce_str", nonce_str);  
        packageParams.put("body", "my_xn_iphone");  
        packageParams.put("attach", "WeiXin_zf");  
        packageParams.put("out_trade_no", out_trade_no);  
        //這里寫的金額為1 分到時修改
        packageParams.put("total_fee", "1");   
        packageParams.put("spbill_create_ip", ip);  
        packageParams.put("notify_url", notify_url);  
        packageParams.put("trade_type", trade_type);  
        packageParams.put("openid", openid);  

        String sign = reqHandler.createSign(packageParams);  //通過map獲取簽名,這塊也可以把參數拼接成字符串 之后進行sign=MD5(stringSignTemp).toUpperCase();生成簽名,可以參照開發文檔--簽名算法 
    
        //生成請求參數的xml,這個也可以調用字符串或map【前提是 把剛生成的sign已經添加到字符串里或添加到map里了】轉成xml的方法生成xml
        String xml="<xml>"+
                "<appid>"+appid+"</appid>"+
                "<mch_id>"+mch_id+"</mch_id>"+
                "<nonce_str>"+nonce_str+"</nonce_str>"+
                "<sign>"+sign+"</sign>"+
                "<body><![CDATA[my_xn_iphone]]></body>"+
                "<attach>WeiXin_zf</attach>"+
                "<out_trade_no>"+out_trade_no+"</out_trade_no>"+
                //金額,這里寫的1 分到時修改
                "<total_fee>"+1+"</total_fee>"+
                //"<total_fee>"+finalmoney+"</total_fee>"+
                "<spbill_create_ip>"+ip+"</spbill_create_ip>"+
                "<notify_url>"+notify_url+"</notify_url>"+
                "<trade_type>"+trade_type+"</trade_type>"+
                "<openid>"+openid+"</openid>"+
                "</xml>";
       return xml;
    }                

createSign(SortedMap<String, String> packageParams);
 1 /**
 2      * 創建md5摘要,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。
 3      */
 4     public String createSign(SortedMap<String, String> packageParams) {
 5         StringBuffer sb = new StringBuffer();
 6         Set es = packageParams.entrySet();
 7         Iterator it = es.iterator();
 8         while (it.hasNext()) {
 9             Map.Entry entry = (Map.Entry) it.next();
10             String k = (String) entry.getKey();
11             String v = (String) entry.getValue();
12             if (null != v && !"".equals(v) && !"sign".equals(k)
13                     && !"key".equals(k)) {
14                 sb.append(k + "=" + v + "&");
15             }
16         }
17         sb.append("key=" + this.getKey());
18         System.out.println("md5 sb:" + sb+"key="+this.getKey());
19         String sign = MD5Util.MD5Encode(sb.toString(), this.charset)
20                 .toUpperCase();
21         System.out.println("簽名:" + sign);
22         return sign;
23 
24     }

 

   三, 在頁面調用微信支付js並且完成支付

       獲取到prepay_id,

 SortedMap<String, String> finalpackage = new TreeMap<String, String>();
                 String appid = ""; //不用寫你應該也是到是啥了
                 String timestamp = Sha1Util.getTimeStamp();
                 String nonceStr2 = CommonUtil.getNonce_str();
                 String prepay_id2 = "prepay_id="+prepay_id;     //拼接packages
                 String packages = prepay_id2;
                 finalpackage.put("appId", appid);  
                 finalpackage.put("timeStamp", timestamp);  
                 finalpackage.put("nonceStr", nonceStr2);  
                 finalpackage.put("package", packages);  
                 finalpackage.put("signType", "MD5");
                 String finalsign = reqHandler.createSign(finalpackage); //生成攜有以上參數的簽名
                 //請求前端頁面調用微信支付js並完成支付
                 response.sendRedirect("pay.jsp?appid="+appid2+"&timeStamp="+timestamp+"&nonceStr="+nonceStr2+"&package="+packages+"&sign="+finalsign);
     }

     pay.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String appId = request.getParameter("appid");
String timeStamp = request.getParameter("timeStamp");
String nonceStr = request.getParameter("nonceStr");
String packageValue = request.getParameter("package");
String paySign = request.getParameter("sign");
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    
    <title>微信支付</title>
    
    <meta name="viewport" content="width=device-width; initial-scale=1.0">  //h5適應手機
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->

  </head>
  
  <body>
        <br><br><br>
              
        <div style="text-align:center;">
                <h2>xxx __ 微信支付 </h2>
                <h3>價格: 0.01元</h3>
        </div>
        <div style="text-align:center;">
            <input type="button" value="確認支付" onclick="callpay()">
        </div>
  </body>
  <script type="text/javascript">
      function callpay(){
         WeixinJSBridge.invoke('getBrandWCPayRequest',{
           "appId" : "<%=appId%>",
           "timeStamp" : "<%=timeStamp%>", 
           "nonceStr" : "<%=nonceStr%>",
           "package" : "<%=packageValue%>",
           "signType" : "MD5", 
           "paySign" : "<%=paySign%>" 
               },function(res){
                WeixinJSBridge.log(res.err_msg);
                if(res.err_msg == "get_brand_wcpay_request:ok"){  
                    alert("微信支付成功!");
                    
                }else if(res.err_msg == "get_brand_wcpay_request:cancel"){  
                    alert("用戶取消支付!");  
                    
                }else{  
                   alert("支付失敗!");  
                   
                }  
            })
        }
  </script>
  
</html>

 

    生成時間戳以及隨即字符串的方法

 public static String getTimeStamp() {
         return String.valueOf(System.currentTimeMillis() / 1000);
     }
     
 
 public static String getNonce_str(){
 
         String currTime = TenpayUtil.getCurrTime();
         //8位日期
         String strTime = currTime.substring(8, currTime.length());
         //四位隨機數
         String strRandom = TenpayUtil.buildRandom(4) + "";
         //10位序列號,可以自行調整。
         String str = strTime + strRandom;
         
         return str ;
         
     }

   開發中遇到的問題:

     開發中需要注意的問題:

    1,所有簽名中攜帶的key都是商戶密鑰

    2,如果你的簽名通過了微信支付接口簽名校驗工具校驗成功 ,那么你就別懷疑了,重新申請一個商戶密鑰

    3,如果你的簽名未通過了微信支付接口簽名校驗工具校驗,那你就檢查一下,你參與簽名的參數是否按參數名ASCII碼未按升序排列,或者是生成MD5字符串沒有toUpperCase轉換為大寫

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM