微信公衆號支付

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

 開發以前的準備工做:css

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

 

       appid  //公衆號appid前端

         appsecret //公衆號密鑰java

       mch_id //商戶id算法

       key  //商戶密鑰json

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

    設置支付受權目錄微信

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

      業務流程時序圖

  開發流程能夠分爲三步:

  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轉換爲大寫

相關文章
相關標籤/搜索