zoukankan      html  css  js  c++  java
  • 微信公众号支付

    对开发测试成功后的源码以及在开发中遇到的问题做一下简要说明

     开发之前的准备工作:

        参数:开发之前你要知道请求每个接口地址的参数所代表的含义  //下面四个是必须知道的参数

           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转换为大写

        

  • 相关阅读:
    Luogu 3119 [USACO15JAN]草鉴定Grass Cownoisseur
    Luogu 4514 上帝造题的七分钟
    Luogu 1484 种树
    Luogu【P2904】跨河(DP)
    Luogu【P2065】贪心的果农(DP)
    Luogu【P1725】琪露诺(单调队列,DP)
    二分图匹配
    单调队列
    Tarjan的强联通分量
    手写堆
  • 原文地址:https://www.cnblogs.com/cmyxn/p/5743861.html
Copyright © 2011-2022 走看看