关于微信支付相关开发
来源:互联网 发布: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
- 关于微信支付相关开发
- 微信app开发支付接口相关
- 微信开发之微信支付相关算法实现
- 微信支付相关
- 关于微信支付
- 关于微信支付
- 关于微信支付
- 关于微信支付
- 关于微信支付
- 关于微信支付
- 关于微信支付
- 关于微信支付
- 微信支付开发相关及测试[未完]
- 微信支付开发
- 微信支付开发
- 微信支付开发
- 微信支付开发
- 微信支付开发
- Mybatis分页插件PageHelper【copy】
- FE
- PAT 1100
- The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path
- 亲友定位助手--具备实时共享位置的智能手机定位地图软件的设计
- 关于微信支付相关开发
- NodeJS命令找不到:'express' 不是内部或外部命令,也不是可运行的程序或批处理文件。
- 30K
- Java线程学习笔记之Lock
- idea 快捷键
- java Socket通信实现步骤
- SpringMVC 4.2 中 使用 ${adminPath} 无法解析成properties文件中的值
- svn is already locked问题解决
- Qemu安装之后如何卸载