关于微信支付相关开发

来源:互联网 发布:ubuntu 16.04 安装php 编辑:程序博客网 时间:2024/06/06 03:47

       由于公司业务需要,要求项目中添加微信支付功能,想着以前做过银联在线支付,起初还以为没啥难度,看看技术文档啥的应该都能很快完成吧。结果在做的过程中才发现这样没准备好,那样没准备好,我能在规定时间拿出来吗?无事的时候进行了一下小小的总结,好了,不多说,直接开始微信支付开发。

       在这个项目中一共做了三种支付方式:扫码支付、APP微信支付、微信公众号支付;关于销账三种模式都是都是一样的,所以先说生成订单。

      一、微信扫码支付:

      微信扫码支付是三种支付方式中最简单的,微信官方提供了两种模式,我采用的第二种模式,因为第二种模式相较于第一种更为简单,而且第二种就能够满足业务要求了,就没有去管第一种。


    根据上面的时序图,我们需要做的仅仅只有红色的那一部分,获取前台传入的订单金额,调用微信统一下单接口,完成下单,代码如下:

String appid =wxMsg.get("APPID");//公众账号IDString mch_id =wxMsg.get("MCH_ID");//商户号String nonce_str = RandCharsUtils.getRandomString(16);//随机字符串String body = "商品描述";//商品描述  String detail = "微信扫码支付";String attach = "";//私有域信息,在支付回调通知中会原样返回,可以包含销账信息String out_trade_no = RandCharsUtils.getOrderNum();//商户订单号  int total_fee = new Integer(amLong.toString());//单位是分,即是0.01元String spbill_create_ip = "127.0.0.1";String time_start = RandCharsUtils.timeStart();//订单开始时间String time_expire = RandCharsUtils.timeExpire();//订单结束时间String notify_url =WeixinConfigUtils.notify_url;//通知地址,用户支付后的回调地址,需要外网能够访问String trade_type = "NATIVE";//支付类型,三种支付类型为NATIVE,APP,JSAPI//参数:开始生成签名SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();parameters.put("appid", appid);parameters.put("mch_id", mch_id);parameters.put("nonce_str", nonce_str);parameters.put("body", body);parameters.put("detail", detail);parameters.put("attach", attach);parameters.put("out_trade_no", out_trade_no);parameters.put("total_fee", total_fee);parameters.put("time_start", time_start);parameters.put("time_expire", time_expire);parameters.put("notify_url", notify_url);parameters.put("trade_type", trade_type);parameters.put("spbill_create_ip", spbill_create_ip);String sign = WXSignUtils.createSign("UTF-8", parameters);//生成签名Unifiedorder unifiedorder = new Unifiedorder();//订单实体unifiedorder.setAppid(appid);unifiedorder.setMch_id(mch_id);unifiedorder.setNonce_str(nonce_str);unifiedorder.setSign(sign);unifiedorder.setBody(body);unifiedorder.setDetail(detail);unifiedorder.setAttach(attach);unifiedorder.setOut_trade_no(out_trade_no);unifiedorder.setTotal_fee(total_fee);unifiedorder.setSpbill_create_ip(spbill_create_ip);unifiedorder.setTime_start(time_start);unifiedorder.setTime_expire(time_expire);unifiedorder.setNotify_url(notify_url);unifiedorder.setTrade_type(trade_type);//构造xml参数String xmlInfo = HttpXmlUtils.orderXmlInfo(unifiedorder);String wxUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";//统一下单接口String method = "POST";String weixinPost = HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo).toString();//统一下单返回结果
      在下单结果中,如果统一下单成功,在返回结果中会有“result_code”字段为“SUCCESS”,“code_url”为二维码内容,将该内容放入二维码中,将二维码返回给前台即可。
      二、APP微信支付:

      APP微信支付较微信扫码支付要复杂一点,但也是大同小异,按照流程文档流程来基本上没有太多问题,只是比扫码支付在下单成功之后多了一步操作,需要使用下单返回的“prepay_id”进行签名在返回给APP端,起时序图如下:


     下单与签名生成客户端支付信息代码如下:

String appid =WeChatConfigUtils.appid;//APP账号IDString mch_id =WeChatConfigUtils.mch_id;//商户号String nonce_str = RandCharsUtils.getRandomString(16);//随机字符串String body = "商品描述";//商品描述  String detail = "微信在线缴费";String out_trade_no = RandCharsUtils.getOrderNum();//商户订单号  int total_fee = new Integer(orderMoney);//单位是分,即是0.01元String spbill_create_ip = "127.0.0.1";String time_start = RandCharsUtils.timeStart();//订单开始时间String time_expire = RandCharsUtils.timeExpire();//订单结束时间String notify_url =WeChatConfigUtils.notify_url;//通知地址String trade_type = "APP";//支付类型//参数:开始生成签名SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();parameters.put("appid", appid);parameters.put("mch_id", mch_id);parameters.put("nonce_str", nonce_str);parameters.put("body", body);parameters.put("detail", detail);parameters.put("attach", attach);parameters.put("out_trade_no", out_trade_no);parameters.put("total_fee", total_fee);parameters.put("time_start", time_start);parameters.put("time_expire", time_expire);parameters.put("notify_url", notify_url);parameters.put("trade_type", trade_type);parameters.put("spbill_create_ip", spbill_create_ip);String sign = WXSignUtils.createSign("UTF-8", parameters); //签名Unifiedorder unifiedorder = new Unifiedorder();//下单实体类unifiedorder.setAppid(appid);unifiedorder.setMch_id(mch_id);unifiedorder.setNonce_str(nonce_str);unifiedorder.setSign(sign);unifiedorder.setBody(body);unifiedorder.setDetail(detail);unifiedorder.setAttach(attach);unifiedorder.setOut_trade_no(out_trade_no);unifiedorder.setTotal_fee(total_fee);unifiedorder.setSpbill_create_ip(spbill_create_ip);unifiedorder.setTime_start(time_start);unifiedorder.setTime_expire(time_expire);unifiedorder.setNotify_url(notify_url);unifiedorder.setTrade_type(trade_type);//构造xml参数String xmlInfo = HttpXmlUtils.xmlInfo(unifiedorder);String wxUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";//下单地址String method = "POST";String weixinPost = HttpXmlUtils.httpsRequestNew(wxUrl, method, xmlInfo).toString();//Map<String,String> map=ParseXMLUtils.jdomParseXml(weixinPost);//下单返回结果if("SUCCESS".equals(map.get("return_code"))){SortedMap<Object, Object> finalpackage = new TreeMap<Object, Object>();String timestamp = String.valueOf(System.currentTimeMillis() / 1000);nonce_str = RandCharsUtils.getRandomString(16);finalpackage.put("appid",  appid);//公众号  finalpackage.put("partnerid",  mch_id);  //商户号finalpackage.put("prepayid",  map.get("prepay_id"));  //下单返回成功prepay_idfinalpackage.put("package",  "Sign=WXPay"); //固定写法 finalpackage.put("noncestr", nonce_str);  //随机数finalpackage.put("timestamp", timestamp);  //时间sign = WXSignUtils.createSign("UTF-8", finalpackage);//生成签名JSONObject array = new JSONObject();//存放APP端支付信息,返回给APP端array.put("appid", appid);array.put("partnerid",  mch_id);  array.put("prepayid",  map.get("prepay_id"));  array.put("package",  "Sign=WXPay");  array.put("noncestr", nonce_str);  array.put("timestamp", timestamp); array.put("sign", sign); }
      三、微信公众号支付:

      微信公众号支付是三种支付类型中坑最多的,一步小心就会踏入坑中,首先按照正常流程需要生成订单嘛,对的,生成订单,想想还是没啥的,实际却一开始就出现了问他,生成订单时需要使用用户标识:openid这个东西,它是微信公众号中一个微信用户的唯一标识,因此在生成订单之前需要获取这个参数。那么这个参数需要怎样获取呢,根据公众平台开发文档中用户管理-网页授权获取用户基本信息可知:需要在在H5页面中调用类似如下地址进行鉴权获取openidhttps://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect, 其中appid为公众号,response_type为固定值,scope授权作用域,state在调用重定向时会传递该参数,可以用来传递一些参数,#wechat_redirect必加参数,而redirect_uri为重定向地址。

      而这个重定向地址redirect_uri则是这个坑的开始,这个地址需要外网能够访问,这个没得说得了,其次他需要在微信公众平台中进行配置,配置的要求是它不能有端口号,使用的域名需要ICP备案。至于ICP备案就不管我们的事了,而不能使用端口号就是我们的事,不能使用端口号,好说,将端口号改成80端口就完了,改成80之后再内网上都是可以不要输入端口号进行访问了,但是却不能将该端口号开外网,国家限制了,不允许自己私自开80端口的外网,于是就等着去申请80端口的开放权限。等了一段时间后,80端口和ICP备案都完了,继续进行开发;时序图如下:


      在微信公众号支付中需要使用H5页面,具体页面不说了,看JS部分,前面说我们生成订单需要openid,于是需要做的便是去获取openid,JS代码如下:

        var ua = navigator.userAgent.toLowerCase();     if(ua.match(/MicroMessenger/i)=="micromessenger") {      } else {         alert("  不是微信浏览器");     }
    var fromurl="回调地址";  var url="https://open.weixin.qq.com/connect/oauth2/authorize?"+        "appid=公众号&redirect_uri="+encodeURI(fromurl)+        "&response_type=code&scope=snsapi_base&state=123#wechat_redirect";         window.location.href=url; 

在回调服务代码:

String code=request.getParameter("code");//使用code获取openid,在调用该服务时,微信会传递该字段String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="     + WeixinConfigUtils.appid+ "&secret=" +WeixinConfigUtils.secret      + "&code=" + code + "&grant_type=authorization_code";String openidStr = HttpXmlUtils.httpsRequest(url, "POST", null).toString();//调用鉴权返回结果


如果鉴权成功,会在结果中返回openid,获取到openid之后就可以进行接下来的下单了:


String appid =WeixinConfigUtils.appid;//公众账号IDString mch_id =WeixinConfigUtils.mch_id;//商户号String nonce_str = RandCharsUtils.getRandomString(16);//随机字符串String body = "";//商品描述  String detail = "微信在线支付";String attach ="";//使用域String out_trade_no = RandCharsUtils.getOrderNum();//商户订单号  int total_fee = new Integer(amLong.toString());//单位是分,即是0.01元String spbill_create_ip = "127.0.0.1";String time_start = RandCharsUtils.timeStart();//订单开始时间String time_expire = RandCharsUtils.timeExpire();//订单结束时间String notify_url =WeixinConfigUtils.notify_url;//通知地址String trade_type = "JSAPI";//支付模式String openid=obj.getString("openid");//千辛万苦获取到的//参数:开始生成签名SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();parameters.put("appid", appid);parameters.put("mch_id", mch_id);parameters.put("nonce_str", nonce_str);parameters.put("body", body);parameters.put("nonce_str", nonce_str);parameters.put("attach", attach);parameters.put("out_trade_no", out_trade_no);parameters.put("total_fee", total_fee);parameters.put("time_start", time_start);parameters.put("time_expire", time_expire);parameters.put("notify_url", notify_url);parameters.put("trade_type", trade_type);parameters.put("spbill_create_ip", spbill_create_ip);parameters.put("detail", detail);parameters.put("openid", openid);String sign = WXSignUtils.createSign("UTF-8", parameters);//签名Unifiedorder unifiedorder = new Unifiedorder();unifiedorder.setAppid(appid);unifiedorder.setMch_id(mch_id);unifiedorder.setNonce_str(nonce_str);unifiedorder.setSign(sign);unifiedorder.setBody(body);unifiedorder.setDetail(detail);unifiedorder.setAttach(attach);unifiedorder.setOut_trade_no(out_trade_no);unifiedorder.setTotal_fee(total_fee);unifiedorder.setSpbill_create_ip(spbill_create_ip);unifiedorder.setTime_start(time_start);unifiedorder.setTime_expire(time_expire);unifiedorder.setNotify_url(notify_url);unifiedorder.setTrade_type(trade_type);unifiedorder.setOpenid(openid);//构造xml参数String xmlInfo = HttpXmlUtils.xmlH5Info(unifiedorder);String wxUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";//统一下单接口String method = "POST";String weixinPost = HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo).toString();//支付接口返回结果Map<String,String> map=ParseXMLUtils.jdomParseXml(weixinPost);//判断统一下单是否成功,如成功则将下单返回的prepay_id进行签名生成待支付数据返回到页面SortedMap<Object, Object> finalpackage = new TreeMap<Object, Object>();String timestamp = String.valueOf(System.currentTimeMillis() / 1000);nonce_str = RandCharsUtils.getRandomString(16);//随机数String packages = "prepay_id="+map.get("prepay_id");//prepay_idfinalpackage.put("appId",  appid); //公众号 finalpackage.put("timeStamp", timestamp);  finalpackage.put("nonceStr", nonce_str);  finalpackage.put("package", packages);  finalpackage.put("signType", "MD5");//固定值sign = WXSignUtils.createSign("UTF-8", finalpackage);//签名

     

 生成订单到此结束,接下来是H5页面中,用户调用支付:

function onBridgeReady(){           WeixinJSBridge.invoke(               'getBrandWCPayRequest', {                   "appId" : "${appid}",                       "timeStamp": "${timeStamp}",                           "nonceStr" : "${nonceStr}",                    "package" : "${packageValue}",                        "signType" : "MD5",                            "paySign" : "${paySign}"                   },function(res){                if(res.err_msg == "get_brand_wcpay_request:ok"){ //此处的成功不一定是成功,以后台返回结果为准                     alert("支付成功!");                }else if(res.err_msg == "get_brand_wcpay_request:cancel"){                alert("您已取消微信支付!");                      history.go(-1);                }else{                     alert("支付失败!");                }              }); 

     

  至此,三种模式的订单生成就全部完成了,接下来是进行订单的销账,三种订单的销账方式都是一样的,通过一部调用返回支付结果,代码如下:


        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> map = new HashMap<String, String>();          map = ParseXMLUtils.jdomParseXml(sb.toString());         SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();                Iterator it = map.keySet().iterator();          while (it.hasNext()) {              String parameter = (String) it.next();              String parameterValue = map.get(parameter);              String v = "";              if(null != parameterValue) {                  v = parameterValue.trim();              }              packageParams.put(parameter, v);          }          String sign = WXSignUtils.createSign("UTF-8", packageParams);//参数签名        String tenpaySign = ((String)packageParams.get("sign")).toUpperCase();         //判断签名是否正确          if(sign.equals(tenpaySign)) {              //------------------------------              //处理业务            //------------------------------              if("SUCCESS".equals((String)packageParams.get("result_code"))){                  //通知微信.异步确认成功.必写.                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"                          + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";              } else {  //支付失败,错误信息                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"                          + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";              }              //------------------------------              //处理业务完毕              //------------------------------              BufferedOutputStream out = new BufferedOutputStream(                      response.getOutputStream());              out.write(resXml.getBytes());              out.flush();              out.close();          } else{              System.out.println("通知签名验证失败");          } 


相关工具方法:


/** * 微信支付签名算法sign * @param characterEncoding * @param parameters * @return */@SuppressWarnings("rawtypes")public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){StringBuffer sb = new StringBuffer();Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)Iterator it = es.iterator();while(it.hasNext()) {Map.Entry entry = (Map.Entry)it.next();String k = (String)entry.getKey();Object v = entry.getValue();if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {sb.append(k + "=" + v + "&");}}sb.append("key=" + 微信公众号的Key值);String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();return sign;}/**     * post请求并得到返回结果     * @param requestUrl     * @param requestMethod     * @param output     * @return     */    public static String httpsRequest(String requestUrl, String requestMethod, String output) {        try{            URL url = new URL(null,requestUrl,new sun.net.www.protocol.https.Handler());//重点在这里,需要使用带有URLStreamHandler参数的URL构造方法            HttpsURLConnection httpConnection = (HttpsURLConnection) url.openConnection();//由于我调用的是官方给微信API接口,所以采用HTTPS连接            httpConnection.setDoOutput(true);            httpConnection.setDoInput(true);            httpConnection.setUseCaches(false);            httpConnection.setRequestMethod(requestMethod);            if (null != output) {                OutputStream outputStream = httpConnection.getOutputStream();                outputStream.write(output.getBytes("UTF-8"));                outputStream.close();            }            int responseCode = httpConnection.getResponseCode();            if (responseCode == HttpURLConnection.HTTP_OK) {            StringBuffer buffer = new StringBuffer();            InputStream urlStream = httpConnection.getInputStream();            BufferedReader bufferedReader = new BufferedReader(                    new InputStreamReader(urlStream,"utf-8"));            String str = "";            while ((str = bufferedReader.readLine()) != null) {                buffer.append(str);            }            bufferedReader.close();            urlStream.close();            urlStream.close();            urlStream = null;            httpConnection.disconnect();            return buffer.toString();            }                    }catch(Exception ex){            ex.printStackTrace();        }        return "";    }


关于如何拼接xml和解析xml在这里就不详细说了。至此微信三种方式支付全部完成!


0 0
原创粉丝点击