微信和支付宝支付实战
2016-08-31 17:43
477 查看
最近的项目中要用到移动支付。在此总结下
1、微信支付 :因为需求是扫码支付即时到账,用的是native方式。按照官方文档的说法,调用“https://api.mch.weixin.qq.com/pay/unifiedorder” 这个接口生成一个预支付的订单,微信会返回给你一个订单的二维码的地址,然后把二维码解析展示出来用户扫码就可以支付了。
//微信支付
这一步主要是拼接预下单接口的参数,要注意的是请求的要用utf-8编码下,不然可能会出现签名错误之类的返回。
把MD5签名算法也贴上 在这里被编码坑了。
MD5签名的时候切记要用“utf-8”编码,不然会出现签名错误,这是个深坑,接口文档是没有说明的,
贴上MD5算法
然后创建连接去请求微信接口
调用之后会拿到一个xml 里面是返回值。解析xml
用qrcode.js的生成二维码的时候render 建议用canvas方式 虽然它还提供了table方式,但是我用的时候table方式无法生成二维码的但是canvas是可以的 ,没弄懂为什么。如果顺利的话 二维码就生成了,扫码就可以支付了。解释下这段js代码,请求成功后用一个遮罩层,把二维码放遮罩层上,然后用一个函数去轮询这个订单的状态,因为调用时异步的吗,如果查询到订单已经支付了的话,清除掉轮询并就跳转到支付完成页面。
下面贴微信回调的代码
支付成功后微信会调用这个方法,这个方法主要用于修改订单状态。当订单状态改变了,js的queryOrderStatus方法检测到订单支付完成了,就会跳转到支付完成页面。就完成了。把js轮训方法也贴上
下面说说支付宝支付,因为支付官方提供的有demo 拿过来改改就能直接用了,这个比微信方便多了。直接贴代码
我这里用的是同步回调方式,当然也可以用异步和微信是一样的,支付宝是拼接一个自动提交的form表单,然后把这歌表单写到页面就能跳转到支付宝收银台,代码在上面微信生成二维码那里,拿下来
这样子表单会自动提交,页面会跳转到支付宝收银台,完成支付后会从支付宝那里跳转 需要一个处理的方法,贴代码
这里千万不要一上来就用“UTF-8” 不然会乱码 然后就会出现签名错误的问题。至此微信和支付宝支付都做成功了。
1、微信支付 :因为需求是扫码支付即时到账,用的是native方式。按照官方文档的说法,调用“https://api.mch.weixin.qq.com/pay/unifiedorder” 这个接口生成一个预支付的订单,微信会返回给你一个订单的二维码的地址,然后把二维码解析展示出来用户扫码就可以支付了。
//微信支付
String random = RandomStringGenerator.getRandomStringByLength(32); Map<String,Object> params = new HashMap<String,Object>(); params.put("appid", TenConfig.getAppid()); params.put("mch_id", TenConfig.getMchid()); params.put("nonce_str", random); params.put("body", body.toString().trim()); params.put("out_trade_no", order.getOrderNumber()); params.put("total_fee", sump); params.put("spbill_create_ip", TenConfig.SPBILL_CREATE_IP); params.put("notify_url", TenConfig.getNotify_url()); params.put("trade_type", "NATIVE"); params.put("product_id", productId); String sign = TenUtils.getSign(params); //拼接xml StringBuilder xml = new StringBuilder("<xml>"); xml.append("<appid>").append("<![CDATA["+TenConfig.getAppid()+"]]>").append("</appid>"); xml.append("<mch_id>").append("<![CDATA["+TenConfig.getMchid()+"]]>").append("</mch_id>"); xml.append("<nonce_str>").append("<![CDATA["+random+"]]>").append("</nonce_str>"); xml.append("<body>").append("<![CDATA["+body.toString().trim()+"]]>").append("</body>"); xml.append("<out_trade_no>").append("<![CDATA["+order.getOrderNumber()+"]]>").append("</out_trade_no>"); xml.append("<total_fee>").append("<![CDATA["+sump+"]]>").append("</total_fee>"); xml.append("<spbill_create_ip>").append("<![CDATA["+TenConfig.SPBILL_CREATE_IP+"]]>").append("</spbill_create_ip>"); xml.append("<notify_url>").append("<![CDATA["+TenConfig.getNotify_url()+"]]>").append("</notify_url>"); xml.append("<trade_type>").append("<![CDATA[NATIVE]]>").append("</trade_type>"); xml.append("<product_id>").append("<![CDATA["+productId+"]]>").append("</product_id>"); xml.append("<sign>").append("<![CDATA["+sign+"]]>").append("</sign>"); xml.append("</xml>");<pre name="code" class="java"> logger.info("XML:\n"+xml.toString()); String resStr = TenUtils.postData(TenConfig.UNIFIED_ORDER, xml.toString(), "text/html;charset=utf-8");
这一步主要是拼接预下单接口的参数,要注意的是请求的要用utf-8编码下,不然可能会出现签名错误之类的返回。
把MD5签名算法也贴上 在这里被编码坑了。
ArrayList<String> list = new ArrayList<String>(); for(Map.Entry<String,Object> entry:map.entrySet()){ if(entry.getValue()!=""){ list.add(entry.getKey() + "=" + entry.getValue() + "&"); } } int size = list.size(); String [] arrayToSort = list.toArray(new String[size]); Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER); StringBuilder sb = new StringBuilder(); for(int i = 0; i < size; i ++) { sb.append(arrayToSort[i]); } String result = sb.toString(); result += "key=" + TenConfig.getKey(); result = MD5Encrypt.MD5Encode(result, "utf-8").toUpperCase(); return result;
MD5签名的时候切记要用“utf-8”编码,不然会出现签名错误,这是个深坑,接口文档是没有说明的,
贴上MD5算法
public static String md5(String s) { byte[] input = s.getBytes(); String output = null; char[] hexChar = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(input); byte[] tmp = md.digest(); char[] str = new char[32]; byte b = 0; for (int i = 0; i < 16; i++) { b = tmp[i]; str[(2 * i)] = hexChar[(b >>> 4 & 0xF)]; str[(2 * i + 1)] = hexChar[(b & 0xF)]; } output = new String(str); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return output; } private static String byteArrayToHexString(byte[] b) { StringBuffer resultSb = new StringBuffer(); for (int i = 0; i < b.length; i++) { resultSb.append(byteToHexString(b[i])); } return resultSb.toString(); } private static String byteToHexString(byte b) { int n = b; if (n < 0) { n += 256; } int d1 = n / 16; int d2 = n % 16; return hexDigits[d1] + hexDigits[d2]; } public static String MD5Encode(String origin, String charsetname) { String resultString = null; try { resultString = new String(origin); MessageDigest md = MessageDigest.getInstance("MD5"); if ((charsetname == null) || ("".equals(charsetname))) { resultString = byteArrayToHexString(md.digest(resultString .getBytes())); } else { resultString = byteArrayToHexString(md.digest(resultString .getBytes(charsetname))); } } catch (Exception localException) {} return resultString; } private static final String[] hexDigits = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
然后创建连接去请求微信接口
BufferedReader reader = null; try { URL url = new URL(urlStr); URLConnection conn = url.openConnection(); conn.setDoOutput(true); conn.setConnectTimeout(CONNECT_TIMEOUT); conn.setReadTimeout(CONNECT_TIMEOUT); if(contentType!=null && !"".equals(contentType)) conn.setRequestProperty("content-type", contentType); OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING); if(data == null) data = ""; writer.write(data); writer.flush(); writer.close(); reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING)); StringBuilder sb = new StringBuilder(); String line = null; while ((line = reader.readLine()) != null) { sb.append(line); sb.append("\r\n"); } return sb.toString(); } catch (IOException e) { logger.error("Error connecting to " + urlStr + ": " + e.getMessage()); } finally { try { if (reader != null) reader.close(); } catch (IOException e) { } } return null;
调用之后会拿到一个xml 里面是返回值。解析xml
Map resMap = XMLParser.getMapFromXML(resStr); if(resMap == null){ logger.info("统一下单接口调用失败"); }else{ String returnCode = (String) resMap.get("return_code"); if(returnCode.equals("SUCCESS")){ logger.info("统一下单接口调用成功"); String codeUrl = (String) resMap.get("code_url"); //把二维码连接返回给页面 result.put("msg", codeUrl); //更新订单的支付方式,金额等 order.setPayAmount(Float.parseFloat(sumprice)); order.setPayType(payType); order.setWxPayNonceStr(random); order.setBody(body.toString()); if(orderService.updateWhenPay(order) > 0){ result.put("code", "ok"); }else{ result.put("code", "err"); } }解析xml把生成的二维码给页面 通过jQuery的qrcode.js把地址生成二维码。
<span style="white-space:pre"> </span>$.post("/xxx/xxxxx.jspx",{"orderId":orderId ,"payType":payType,"sumPrice":price}, function(data){ if(data&&data.code=="ok"){ if(payType==1){ //微信支付 $("#model_content").css("display","block"); jQuery('#qrcodeCanvas').qrcode({ render : "canvas",//设置渲染方式 width : 210, //设置宽度 height : 210, //设置高度 typeNumber : -1, //计算模式 correctLevel : QRErrorCorrectLevel.H,//纠错等级 background : "#ffffff",//背景颜色 foreground : "#000000", //前景颜色 text : data.msg }); intel =setInterval(function(){ //请求查询订单状态 queryOrderStatus(orderId,intel); },2000); }else if(payType==2){ //支付宝 $("#ali_pay").html(data.msg); } }else{ window.wxc.xcConfirm("支付失败!", window.wxc.xcConfirm.typeEnum.error); } });
用qrcode.js的生成二维码的时候render 建议用canvas方式 虽然它还提供了table方式,但是我用的时候table方式无法生成二维码的但是canvas是可以的 ,没弄懂为什么。如果顺利的话 二维码就生成了,扫码就可以支付了。解释下这段js代码,请求成功后用一个遮罩层,把二维码放遮罩层上,然后用一个函数去轮询这个订单的状态,因为调用时异步的吗,如果查询到订单已经支付了的话,清除掉轮询并就跳转到支付完成页面。
下面贴微信回调的代码
try { //获取微信回调的流 BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream)request.getInputStream())); <span style="white-space:pre"> </span> String line = null; <span style="white-space:pre"> </span>StringBuilder sb = new StringBuilder(); <span style="white-space:pre"> </span> while((line = br.readLine())!=null){ sb.append(line); } //把微信返回的xml转换成map Map result = XMLParser.getMapFromXML(sb.toString()); //返回给微信的xml StringBuilder reXML = new StringBuilder("<xml>"); //return_code 字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断 if(result.get("return_code").toString().equals("SUCCESS")){ //回调成功 logger.info("微信支付成功,返回的XML:\n"+sb.toString()); //获取微信回调的参数 String orderNum = (String) result.get("out_trade_no"); //业务结果 String resultCode = (String) result.get("result_code"); String transactionId = result.get("transaction_id").toString(); String sign = result.get("sign").toString(); if(result.get("sign")!=null){ //去除Sign result.remove("sign"); } if(sign.equals(TenUtils.getSign(result))){ //签名通过,判断交易状态 if(resultCode.equals("SUCCESS")){ if(orderNum !=null && !"".equals(orderNum)){ Order order = orderService.findByOrderNum(orderNum); if(order != null){ //支付成功 if(order.getStatus() == 0){ //订单如果是待支付状态,更改订单状态为确认中状态(1),更新支付完成时间 orderService.updateStatus(order.getId(), 1); orderService.updatePayCompleteTime(order.getId()); reXML.append("<return_code><![CDATA[SUCCESS]]></return_code>"); reXML.append("<return_msg><![CDATA[OK]]></return_msg>"); //插入支付日志 Map<String,String> map = new HashMap<String,String>(); map.put("pro_type", "销售"); map.put("pay_type", "微信"); map.put("pro_info", order.getBody()); map.put("user_name", order.getBuyMember().getUsername()); map.put("trade_no", orderNum); map.put("pay_amount", order.getPayAmount()+""); map.put("status", "支付完成"); map.put("buyer_email", transactionId); orderService.insertPayLog(map); }else{ reXML.append("<return_code><![CDATA[FAIL]]></return_code>"); reXML.append("<return_msg><![CDATA[订单状态异常]]></return_msg>"); } }else{ reXML.append("<return_code><![CDATA[FAIL]]></return_code>"); reXML.append("<return_msg><![CDATA[参数[out_trade_no]错误]]></return_msg>"); } }else{ reXML.append("<return_code><![CDATA[FAIL]]></return_code>"); reXML.append("<return_msg><![CDATA[参数[out_trade_no]为空]]></return_msg>"); } }else{ <span style="white-space:pre"> </span>logger.info("交易状态异常:"+resultCode); } }else{ reXML.append("<return_code><![CDATA[FAIL]]></return_code>"); reXML.append("<return_msg><![CDATA[签名失败]]></return_msg>"); } }else{ logger.info("微信支付回调失败,失败原因:"+result.get("return_msg").toString()); } <span style="white-space:pre"> </span>reXML.append("</xml>"); <span style="white-space:pre"> </span>logger.info("返回给微信的XML:\n"+reXML); <span style="white-space:pre"> </span>response.getWriter().write(new String(reXML.toString().getBytes("UTF-8"),"ISO-8859-1")); } catch (Exception e) { <span style="white-space:pre"> </span>logger.info(e.getMessage(), e); }
支付成功后微信会调用这个方法,这个方法主要用于修改订单状态。当订单状态改变了,js的queryOrderStatus方法检测到订单支付完成了,就会跳转到支付完成页面。就完成了。把js轮训方法也贴上
//查询订单状态 function queryOrderStatus(orderId,intel){ $.post("/xxx/xxxx.jspx",{"orderId":orderId}, function(data){ if(data && data.orderId == orderId){ //查询成功 if(data.code=="ok"){ //订单查询成功 if(data.status==1){ //支付成功,清除轮训并刷新页面 clearInterval(intel); window.location.href = "/xxxx/xxxx.jspx?orderId="+orderId+"&guid="+guid(); } } } }); }酱紫微信支付就完成了。
下面说说支付宝支付,因为支付官方提供的有demo 拿过来改改就能直接用了,这个比微信方便多了。直接贴代码
//支付宝支付 //把请求参数打包成数组 Map<String, String> params = new HashMap<String, String>(); params.put("service", AlipayConfig.service); params.put("partner", AlipayConfig.partner); params.put("seller_id", AlipayConfig.seller_id); params.put("_input_charset", AlipayConfig.input_charset); //params.put("qr_pay_mode", AlipayConfig.qr_pay_mode); //异步通知支付结果连接 //params.put("notify_url", AlipayConfig.notify_url); //支付宝处理完请求后,自动跳转到指定页面 params.put("return_url", AlipayConfig.return_url); //傻逼支付宝“anti_phishing_key”同步回调不返回这个参数,请求的时候不传递 //params.put("anti_phishing_key", AlipaySubmit.query_timestamp()); //支付用户的ip params.put("exter_invoke_ip", AlipayConfig.exter_invoke_ip); params.put("out_trade_no", order.getOrderNumber()); params.put("subject", body.toString()); params.put("payment_type", AlipayConfig.payment_type); params.put("it_b_pay", AlipayConfig.it_b_pay); params.put("total_fee", sumprice); params.put("body", body.toString()); //请求支付接口 html = AlipaySubmit.buildRequest(params, "POST","确认"); logger.info("支付宝请求HTML:\n"+html); //更新订单的支付方式,金额等 order.setPayAmount(Float.parseFloat(sumprice)); order.setPayType(payType); if(orderService.updatePay(order) > 0){ result.put("code", "ok"); }else{ result.put("code", "err"); } result.put("msg", html);
我这里用的是同步回调方式,当然也可以用异步和微信是一样的,支付宝是拼接一个自动提交的form表单,然后把这歌表单写到页面就能跳转到支付宝收银台,代码在上面微信生成二维码那里,拿下来
//支付宝 $("#ali_pay").html(data.msg);
这样子表单会自动提交,页面会跳转到支付宝收银台,完成支付后会从支付宝那里跳转 需要一个处理的方法,贴代码
try { //获取支付宝POST过来反馈信息 Map<String,String> params = new HashMap<String,String>(); Map requestParams = request.getParameterMap(); for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) { String name = (String) iter.next(); String[] values = (String[]) requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } //乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化 //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "UTF-8"); params.put(name, valueStr); } //商户订单号 String out_trade_no = request.getParameter("out_trade_no"); //支付宝交易号 String trade_no = request.getParameter("trade_no"); //交易状态 String trade_status = request.getParameter("trade_status"); //支付价格 String total_fee = request.getParameter("total_fee"); String seller_id = request.getParameter("seller_id"); //买家支付宝账号 String buyer_email = request.getParameter("buyer_email"); //产品描述 String body = request.getParameter("body"); Float total_pay = Float.parseFloat(total_fee); boolean verify_result = AlipayNotify.verify(params); if(verify_result){//验证成功 Order o = orderService.findByOrderNum(out_trade_no); if(o != null){ //请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的 if(o.getPayAmount() == total_pay && AlipayConfig.seller_id.equals(seller_id)){ //请求参数和回调参数一致 if(trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")){ //注意:付款完成后,支付宝系统发送该交易状态通知 //判断该笔订单是否是待付款状态 if(o.getStatus() == 0){ //如果没有做过处理,更改订单状态为确认中状态(1),更新支付完成时间 orderService.updateStatus(o.getId(), 1); orderService.updatePayCompleteTime(o.getId()); //插入支付日志 Map<String,String> map = new HashMap<String,String>(); map.put("pro_type", "销售"); map.put("pay_type", "支付宝"); map.put("pro_info", body); map.put("user_name", o.getBuyMember().getUsername()); map.put("trade_no", trade_no); map.put("pay_amount", o.getPayAmount()+""); map.put("status", "支付完成"); map.put("buyer_email", buyer_email); orderService.insertPayLog(map); //跳转到支付成功页面 return "redirect:/xxx/xxxx.jspx?orderId="+o.getId(); } } } } }else{//验证失败 response.setContentType("text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); response.getWriter().write("Sign Failure , 验签失败!"); } } catch (Exception e) { logger.info(e.getMessage(), e); }这个地方要注意 支付宝给的demo 是这样写的
<span style="white-space:pre"> </span>乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化 <span style="white-space:pre"> </span>valueStr = new String(valueStr.getBytes("ISO-8859-1"), "UTF-8");
这里千万不要一上来就用“UTF-8” 不然会乱码 然后就会出现签名错误的问题。至此微信和支付宝支付都做成功了。
相关文章推荐
- 利用支付宝的团队编写的 HttpProtocolHandler 、StringRequestEntity 调用微信 支付引发的 body不是UTF8编码的
- WordPress WooCommerce支付宝插件破解微信屏蔽,在微信中可用支付宝支付
- android微信和支付宝支付
- 微信/支付宝支付js
- 微信、支付宝支付
- [置顶] 实现APP支付QQ,微信,支付宝三方的后端服务------php实现微信APP支付
- laravel5.4框架微信,支付宝等支付和退款使用第三方插件Omnipay
- 支付狗提供支付宝、微信个人网站收款接口
- 实战:微信小程序支付开发具体流程
- iOS支付[支付宝、银联、微信]
- Android 微信 支付宝支付,2行代码实现支付
- 支付宝、微信sdk支付流程模拟
- Android WebView 微信和支付宝H5调用本地app支付
- 浅谈购物车之支付宝(微信)支付功能
- 微信,支付宝,百度钱包三种APP支付成功关闭浏览器
- 微信浏览器中进行支付宝支付
- 支付宝支付手机网页支付(适用于微信开发)DEMO(沙箱环境)
- 关于支付(支付宝和微信)
- [置顶] 实现APP支付QQ,微信,支付宝三方的后端服务------php实现支付宝APP支付
- [置顶] 实现APP支付QQ,微信,支付宝三方的后端服务------php实现QQAPP支付