微信扫码支付 模式一 (JSAPI)

来源:互联网 发布:linux 杀dhcp进程 编辑:程序博客网 时间:2024/05/16 07:23

这个微信支付是静态二维码支付,就是店面贴着一个二维码,让消费者自己扫自己输入金额,自己发起支付的支付方式。

要准备的东西比较麻烦:
1、到微信公众号平台设置Oauth2的网页验证域名(用于获取code,code用于拿到发起支付的openId),格式是www.xxxx.com/file1/file2/,不需要https:// 要精确到发起支付页面的当前路径
2、配置Oauth2网页验证域名的时候,需要下载一个txt,放到发起支付页面的统计目录
3、到微信公众号平台还是商户平台设置支付授权地址(用于发起支付),不会设置可以发邮件到微信工作人员邮箱,申请处理,邮箱要自己挖(这个地址需要有https://)
4、拿到微信号的appid、mch_id、appscret、key、退款证书、sub_mch_id(服务商需要为子商户开这个,而前五个都是用服务商自己的就可以了)
5、对于境内微信商户,Oauth2的网页验证域名和支付授权地址必须是通过了国内的ICP备案,不然不能用,对于境外微信商户则没有这个要求

以上四个东西都拿到做好了 就可以开始了 (开发文档无敌坑)


一、首先写个通过Oauth的验证的页面(注意redirect_url需要urlencode)
jsapi1.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ page language="java" import="java.net.*,java.io.*,java.text.*,java.util.*,com.demo.*,com.demo.dao.*,com.demo.utils.*,java.sql.*" %><%@ page language="java" import="java.net.URLDecoder" %><%@ page language="java" import="org.json.JSONObject" %><%request.setCharacterEncoding("UTF-8");String currCode = request.getParameter("currCode");String oauth2_url = "";String wechat_appid = "数据库读取出来比较好";String wechat_appsecret = "数据库读取出来比较好";String currCode = "RMB";String state = currCode+"|"+wechat_appid+"|end";if(wechat_appid==""){    System.out.println("no wechat appid");}else{    System.out.println("WECHATappid:"+wechat_appid);}String redirect_url = "https://www.myserver.com/jsapi2.jsp";redirect_url = URLEncoder.encode(redirect_url, "UTF-8");oauth2_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+wechat_appid+"&redirect_uri="+redirect_url+"&response_type=code&scope=snsapi_base&state="+state+"#wechat_redirect";%><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title><fmt:message key="pg.eng.payment.payWECHATONL.text.title"/></title></head><body><!-- this page will direct to wechat authorize url and then wechat will redirect to redirect_url with code and state --><script language="JavaScript" type="text/javascript">    window.location.href="<%=oauth2_url%>"; </script></body></html>

二、然后微信会根据上一个页面的redirect_url跳转到对应页面并给出一个openId,在这个页面可以完成输入金额的操作(注意要验证是否微信浏览器打开这个页面,js里面那个is_weixin();方法)
jsapi2.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ page language="java" import="java.net.*,java.io.*,java.text.*,java.util.*,com.demo.*,com.demo.dao.*,com.demo.utils.*,java.sql.*" %><%@ page language="java" import="java.net.URLDecoder" %><%@ page language="java" import="org.json.JSONObject" %><%request.setCharacterEncoding("UTF-8");String openId = "";String code = request.getParameter("code");String state = request.getParameter("state");System.out.println("code:"+code+",state:"+state);String[] statespilt = state.split("\\|");String currCode = statespilt[0];String wechat_appid = statespilt[1];String wechat_appsecret = "用wechat_appid查数据库读取出来比较好";String notify_url = "回调地址";System.out.println("wechat_appid:"+wechat_appid+",wechat_appsecret:"+wechat_appsecret);String access_token_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+wechat_appid+"&secret="+wechat_appsecret+"&code="+code+"&grant_type=authorization_code";System.out.println("access_token_url:"+access_token_url);String accesscode_rs = "";accesscode_rs = HttpUtil.postData("",access_token_url);System.out.println("accesscode_rs"+accesscode_rs);JSONObject json;json = new JSONObject(accesscode_rs);System.out.println("OTTO-------set JSONObject");openId = json.getString("openid");System.out.println("OTTO-------openid:"+openId);%><!-- ******************************** input page start *************************************** --><html>    <head>        <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">        <meta http-equiv="Pragma" content="no-cache">        <meta http-equiv="Expires" content="0">        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">        <meta http-equiv="Content-Style-Type" content="text/css">        <script language="JavaScript" type="text/javascript">            window.onload=function(){                is_weixin();            }               function is_weixin(){                var ua = navigator.userAgent.toLowerCase();                if(ua.match(/MicroMessenger/i)=="micromessenger") {                } else {                    window.location.href="remind.jsp";                    return;                }            }        </script>        <title>            input page        </title>        </head>    <body>    <form action="jsapi3.jsp" method="POST" >        <input type="text" id="amount" name="amount" placeholder="input amount">        <input type="submit" id="submit" name="submit" value="submit">        <input type="hidden" id="currCode" name="currCode" value="<%=currCode %>">        <input type="hidden" id="notify_url" name="notify_url" value="<%=notify_url %>">        <input type="hidden" id="wechat_appid" name="wechat_appid" value="<%=wechat_appid %>">    </form>    </body></html>

三、根据上一个页面获取到的openId和金额、货币种类等参数调用统一订单API,然后获得一个prepay_id,用这个prepay_id放到发起支付的js里面,微信客户端就能跳出输入密码确认支付的框了,调用微信浏览器里面的js方法来发起支付(注意发起支付的js方法,只有在微信浏览器里面才生效)
jsapi3.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ page language="java" import="java.net.*,java.io.*,java.text.*,java.util.*,com.demo.*,com.demo.dao.*,com.demo.utils.*,java.sql.*" %><%@ page language="java" import="java.net.URLDecoder" %><%@ page language="java" import="org.json.JSONObject" %><%request.setCharacterEncoding("UTF-8");String wechat_appid = request.getParameter("wechat_appid")==null?"":request.getParameter("wechat_appid") ;String openid = request.getParameter("openid")==null?"":request.getParameter("openid") ;String amount = request.getParameter("amount")==null?"0":request.getParameter("amount") ;String currCode = request.getParameter("currCode")==null?"":request.getParameter("currCode") ;String notify_url = request.getParameter("notify_url")==null?"":request.getParameter("notify_url") ;String result = "";String out_trade_no="";double orderAmt = Double.parseDouble(amount)*100;System.out.println("orderAmt:"+orderAmt);//-----------------------------//设置支付参数//-----------------------------String sign = "";String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";String trade_type = "JSAPI";String spbill_create_ip = request.getRemoteAddr();String nonce_str = "";String notify_url = notify_url;String body = out_trade_no;String appid = wechat_appid;String sub_mch_id = "用wechat_appid查数据库读取出来比较好";String mch_id = "用wechat_appid查数据库读取出来比较好";System.out.println("wechat_appid:"+wechat_appid+",appid:"+appid);String currTime = Method.getCurrTime();String time_start = currTime.substring(8, currTime.length());String strRandom = Method.buildRandom(4) + "";nonce_str = time_start + strRandom; // Generating random numberString total_fee = String.valueOf((int)orderAmt);String fee_type = CurrCode.getName(request.getParameter("currCode"));String key = "用wechat_appid查数据库读取出来比较好  微信提供的key";if("RMB".equals(fee_type))    fee_type = "CNY";System.out.println("datas:"+"\n"+"key:"+key+"\n"+"appid:"+appid+"\n"+"mch_id:"+mch_id+"\n"+"sub_mch_id:"+sub_mch_id+"\n"+"out_trade_no:"+out_trade_no+"\n"+"fee_type:"+fee_type+"\n"+"total_fee:"+total_fee+"\n"+"body:"+body+"\n"+"spbill_create_ip:"+spbill_create_ip+"\n"+"trade_type:"+trade_type+"\n"+"notify_url:"+notify_url+"\n"+"nonce_str:"+nonce_str);Map<Object, String> map = new TreeMap<Object, String>();map.put("appid", appid);//服務商appidmap.put("sub_mch_id", sub_mch_id);//商戶號map.put("mch_id", mch_id);//服務商號map.put("nonce_str", nonce_str);//隨機字符串map.put("body", body);//商品描述,用orderIdmap.put("out_trade_no", out_trade_no);//商家訂單號map.put("fee_type", fee_type);//幣種map.put("total_fee", total_fee);//商品金額,以分為單位map.put("spbill_create_ip", spbill_create_ip);//用戶公網ipmap.put("notify_url", notify_url);//微信反饋的接收urlmap.put("trade_type", trade_type);//公众号支付用JSAPImap.put("openid", openid);sign = Method.createSign("UTF-8", map,key);map.put("sign", sign);String requestXML = Method.getRequestXml(map);Map map1 = null;try{    String resXml = ServerPost.postByExternalServerPost(requestXML,UFDODER_URL,"UTF-8");    resXml = resXml.substring(1);    System.out.println("resXml:"+resXml);    map1 = XMLUtil.doXMLParse(resXml);}catch (ConnectException ce){    System.out.println("连接超时:"+ce.getMessage());}catch (Exception e){    System.out.println("https请求异常:"+e.getMessage());}if(map1==null){    System.out.println("map null");    result = "appId:"+appid+",timeStamp: ,nonceStr: ,package:prepay_id= ,signType: ,paySign: ";}else{    if(!map1.containsKey("prepay_id")||!map1.containsKey("appid")||map1.get("prepay_id")==""){        System.out.println("prepayid error or appid error");        result = "appId:"+appid+",timeStamp: ,nonceStr: ,package:prepay_id= ,signType: ,paySign: ";    }else{        String prepay_timeStamp = Method.getCurrTime();        String prepay_time = currTime.substring(8, currTime.length());        String prepay_strRandom = Method.buildRandom(4) + "";        String prepay_nonce_str = nonce_str;        String prepay_id = map1.get("prepay_id").toString();        String prepay_SignType = "MD5";        Map<Object, String> prepay_map = new TreeMap<Object, String>();        prepay_map.put("appId", appid);        prepay_map.put("timeStamp", prepay_timeStamp);        prepay_map.put("nonceStr", prepay_nonce_str);        prepay_map.put("package", ("prepay_id="+prepay_id));        prepay_map.put("signType", prepay_SignType);        String paySign = Method.createSign("UTF-8", prepay_map,key);        result = "\"appId\":\""+appid+"\",\"timeStamp\":\""+prepay_timeStamp+"\",\"nonceStr\":\""+prepay_nonce_str+"\",\"package\":\"prepay_id="+prepay_id+"\",\"signType\":\""+prepay_SignType+"\",\"paySign\":\""+paySign+"\"";    }    System.out.println("result:"+result);}%><%if(!("appId:"+appid+",timeStamp: ,nonceStr: ,package:prepay_id= ,signType: ,paySign: ").equals(result)){ %><!-- ********************************* payment page ***************************************** --><html>    <head>        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">        <title>            <fmt:message key="pg.eng.payment.payWECHATONLResult.text.title"/>        </title>        <link rel="stylesheet" type="text/css" media="all" href="../../css/common.css">        <meta name="viewport" content="width=device-width">        </script>        <script src="../../js/jquery-1.4.2.min.js"></script>        <script type="text/javascript">            $(document).ready(function(){              callpay();            });        </script>        <script>          function close_cancel() {              //根据appid,mch_id,out_trade_no,sub_mch_id调用关闭订单接口 关闭订单          }        </script>         <script type="text/javascript">            //调用微信JS api 支付            function jsApiCall()            {                WeixinJSBridge.invoke(                    'getBrandWCPayRequest',{                    <%=result%>                    },                    function(res){                        WeixinJSBridge.log(res.err_msg);                        if(res.err_msg == "get_brand_wcpay_request:cancel"){                              close_cancel();                        }else if(res.err_msg == "get_brand_wcpay_request:fail"){                            close_cancel();                        }                    }                );            }            function callpay()            {                if (typeof WeixinJSBridge == "undefined"){                    if( document.addEventListener ){                        document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);                    }else if (document.attachEvent){                        document.attachEvent('WeixinJSBridgeReady', jsApiCall);                         document.attachEvent('onWeixinJSBridgeReady', jsApiCall);                    }                }else{                    jsApiCall();                }            }        </script>        <script type="text/javascript">            var start = setInterval(function(){order_status()},1000);            function order_status() {                //查询自己数据库 根据状态对page_SorF赋值 然后提交表单                /*                $.ajax({                    type: "POST",                    url: "../../WECHATONLINEorderStatusCheck",                    dataType: "text",                    cache: false,                    data: {                        out_trade_no : '<%=out_trade_no%>'                    },                     success: function(data) {                        if(data!=null){                        var result = eval("(" + data + ")");                        var s_result = result.status_result;                            if("Accepted"==s_result){                                clearInterval(start);                                $("#page_SorF").val("successpage");                                $("#PayWeChatForm").submit();                            }else if("Rejected"==s_result){                                clearInterval(start);                                $("#page_SorF").val("failpage");                                $("#PayWeChatForm").submit();                            }else{}                        }else{                        }                    }                });                */            }        </script>    </head>    <body>        <div id="wrap">            <form name="PayWeChatForm" id="PayWeChatForm" method="post" action="jsapiResult.jsp">                                货币种类:<%=currCode %>                                金额:   <%=amount %>                <input type="hidden" id="amount" name="amount" value="<%=amount%>" >                <input type="hidden" id="currCode" name="currCode" value="<%=request.getParameter("currCode")%>" >                <input type="hidden" id="page_SorF" name="page_SorF" value="" >            </form>        </div>    </body></html><!-- ********************************* payment page ***************************************** --><%}else{    %>    <html>    <head></head>    <body>    <div>Message error</div>    </body>    </html>    <%  }%>

四、写回调(微信会通过回调把交易结果返回到Notify_url所在的地方)
Notify.jsp

<%@ page language="java" import="java.util.*,com.demo.*,com.demo.utils.*" pageEncoding="UTF-8"%><%out.println("success");%><%//for getting wechat respon--------start    String appid = "";    String mch_id = "";    String sub_mch_id = "";    String trade_type = "";    String bank_type = "";     String total_fee = "";    String fee_type = "";    String cash_fee = "";    String cash_fee_type = "";     String transaction_id = "";    String out_trade_no = "";    String bankTxTime = "";    String rate = "";    String return_code = "";     String result_code = "";    //读取参数      InputStream inputStream ;      StringBuffer sb = new StringBuffer();      inputStream = request.getInputStream();      String s ;      BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));      while ((s = in.readLine()) != null){          sb.append(s);      }      in.close();      inputStream.close();      //解析xml成map      Map<String, String> m = new HashMap<String, String>();      m = XMLUtil.doXMLParse(sb.toString());      //过滤空 设置 TreeMap      Map<Object,String> packageParams = new TreeMap<Object,String>();        Iterator it = m.keySet().iterator();      while (it.hasNext()) {          String parameter = (String) it.next();          String parameterValue = m.get(parameter);          String v = "";          if(null != parameterValue) {              v = parameterValue.trim();          }          packageParams.put(parameter, v);      }      //------------------------------    //处理业务开始    //------------------------------    String resXml = "";    if("SUCCESS".equals((String)packageParams.get("result_code"))){      // 这里是支付成功        appid = (String)packageParams.get("appid");       mch_id = (String)packageParams.get("mch_id");       sub_mch_id = (String)packageParams.get("sub_mch_id");       trade_type = (String)packageParams.get("trade_type");       bank_type = (String)packageParams.get("bank_type");       total_fee = (String)packageParams.get("total_fee");       fee_type = (String)packageParams.get("fee_type");       cash_fee = (String)packageParams.get("cash_fee");       cash_fee_type = (String)packageParams.get("cash_fee_type");       transaction_id = (String)packageParams.get("transaction_id");       out_trade_no = (String)packageParams.get("out_trade_no");      bankTxTime = (String)packageParams.get("time_end");      rate = (String)packageParams.get("rate");       return_code = (String)packageParams.get("return_code");       result_code = (String)packageParams.get("result_code");      System.out.println("Notify:"+"\n"+"appid:"+appid+"\n"+"mch_id:"+mch_id+"\n"+"sub_mch_id:"+sub_mch_id+"\n"+"trade_type:"+trade_type+"\n"+"bank_type:"+bank_type+"\n"+"total_fee:"+total_fee+"\n"+"fee_type:"+fee_type+"\n"+"cash_fee:"+cash_fee+"\n"+"cash_fee_type:"+cash_fee_type+"\n"+"transaction_id:"+transaction_id+"\n"+"out_trade_no:"+out_trade_no+"\n"+"rate:"+rate+"\n"+"return_code:"+return_code+"\n"+"result_code:"+result_code);       System.out.println("pay success");        //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.        resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"                    + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";     } else {        System.out.println("支付失败,错误信息:" + packageParams.get("err_code"));      resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"                    + "<return_msg><![CDATA[报文            ></return_msg>" + "</xml> ";     }     //------------------------------     //处理业务完毕     //------------------------------     BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());     bos.write(resXml.getBytes());     bos.flush();     bos.close();  //for getting wechat respon--------end    boolean signValidate = false;            // 账号信息              String key = "set wechat ket here"; // key              //判断签名是否正确              if(Method.isTenpaySign("UTF-8", packageParams,key)) {                  signValidate = true;                System.out.println("NOTIFY : " + "tenpay sign success");             } else{                   signValidate = false;                 System.out.println("NOTIFY : " + "tenpay sign fail");             }             if ( (signValidate && return_code.equals("SUCCESS")) ) {                if (signValidate && return_code.equals("SUCCESS") && result_code.equals("SUCCESS")) {                    //对数据库做交易成功的处理逻辑                } else {                    //对数据库做交易失败的处理逻辑                }            }            out.clear();            out = pageContext.pushBody();%>

五、写结果页面
jsapiResult.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ page language="java" import="java.net.*,java.io.*,java.text.*,java.util.*,com.demo.*,com.demo.dao.*,com.demo.utils.*,java.sql.*" %><%request.setCharacterEncoding("UTF-8");String page_SorF = request.getParameter("page_SorF")==null?"failpage":request.getParameter("page_SorF");  //successpage or failpageString amount = request.getParameter("amount")==null?"":request.getParameter("amount");String currCode = request.getParameter("currCode")==null?"":request.getParameter("currCode");%><%if("successpage".equals(page_SorF)){%><!--************************success page start********************************--><html>    <head>        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">        <title>            Result page        </title>        <link rel="stylesheet" type="text/css" media="all" href="../../css/common.css">        <meta name="viewport" content="width=device-width">        </script>    </head>    <body>        <div>            <form name="form1" method="post" action="">            货币种类:<%=currCode %>            金额:   <%=amount %>            </form>        </div>    </body></html><!--*************************success page end*********************************--><%}else if("failpage".equals(page_SorF)){%><!--************************fail page start********************************--><html>  <head>    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">    <title>        fail    </title>    <link rel="stylesheet" type="text/css" media="all" href="../../css/common.css">    <meta name="viewport" content="width=device-width">    </head>  <body>      <form name="failForm" method="post" action="payForm.jsp">       <div>fail</div>      </form>  </body></html><!--*************************fail page end*********************************--><%}else{    %>    <!--************************fail start********************************-->    <html>      <head>        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">        <title>            failpage value null        </title>        <link rel="stylesheet" type="text/css" media="all" href="../../css/common.css">        <meta name="viewport" content="width=device-width">        </head>      <body>          <form name="failForm" method="post" action="payForm.jsp">           <div>failpage value null</div>          </form>      </body>    </html>    <!--*************************fail end*********************************-->    <%}%>

六、写如果不是微信浏览器的时候的提示页面
remind.jsp

<html> <head>  <title>Wrong Browser</title>  <meta charset="UTF-8" />  <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0" />  <link rel="stylesheet" type="text/css" href="https://res.wx.qq.com/open/libs/weui/0.4.1/weui.css" /> </head>  <body>  <div class="weui_msg">   <div class="weui_icon_area">    <i class="weui_icon_info weui_icon_msg"></i>   </div>   <div class="weui_text_area">    <h4 class="weui_msg_title">Please open with WeChat</h4>   </div>  </div>  </body></html>

总结:代码写出来很快(com.demo.utils.*里面那些Method.xxx()、XMLUtils.xxx之类的方法,微信demo上有,我之前写的微信支付贴也有,只是一些生成随机字符、解析xml之类的方法,自己直接写也可以,很简单的),不过需要注意细节(redirect_url需要先urlencode、判断是否微信浏览器、微信账号的配置、code/openId/prepay_id的获取和使用),这些细节就是坑,做少了做错了就走不通。

0 0
原创粉丝点击