JAVA实现微信支付功能
②准备好11个参数JSAPI支付方式
1 配置微信平台
①配置微信公众平台
登录微信公众平台=》公众号设置=》功能设置=》网页授权域名
②配置微信商家平台
①先去官方下载SDK,并导进项目中
2 后台代码的实现
JSAPI官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
①先去官方下载SDK,并导进项目中
地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
②准备好11个参数
appid:商家平台ID。在微信的平台上有 body:商品描述。 mch_id:商户ID。在微信的平台上有 nonce_str:随机字符串,UUID就好了。 openid:用户标识。因为这边是用户已经登录成功了。所以在session中就能拿到。 out_trade_no:商户订单号 spbill_create_ip:终端IP。这个可以从请求头中拿到 total_fee:支付金额。单位是分。 trade_type:交易类型。这里我填JSAPI notify_url:通知地址。就是用户支付成功之后,微信访问你的哪个接口,跟你传递支付成功的相关信息。
sign:签名。这个签名它是由上面的10个参数计算得出的。
③源码
sendPay类:
import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.http.auth.AUTH; import org.hamcrest.core.Is; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.jc.util.wxPay.WXPayUtil; import com.sun.xml.internal.fastinfoset.Encoder; import controller.AuthUtil; import net.sf.json.JSONObject; @Controller @RequestMapping("/pay") public class sendPay { /** * @Description 微信浏览器内微信支付/公众号支付(JSAPI) * @param request * @param code * @return Map */ @RequestMapping(value = "orders") public @ResponseBody Map<String, String> orders(HttpServletRequest request, HttpServletResponse response) { try { String openId = "用户的openid"; // 拼接统一下单地址参数 Map<String, String> paraMap = new HashMap<String, String>(); // 获取请求ip地址 String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } if (ip.indexOf(",") != -1) { String[] ips = ip.split(","); ip = ips[0].trim(); } paraMap.put("appid", AuthUtil.APPID); // 商家平台ID paraMap.put("body", "纯情小店铺-薯条"); // 商家名称-销售商品类目、String(128) paraMap.put("mch_id", AuthUtil.MCHID); // 商户ID paraMap.put("nonce_str", WXPayUtil.generateNonceStr()); // UUID paraMap.put("openid", openId); paraMap.put("out_trade_no", UUID.randomUUID().toString().replaceAll("-", ""));// 订单号,每次都不同 paraMap.put("spbill_create_ip", ip); paraMap.put("total_fee", "1"); // 支付金额,单位分 paraMap.put("trade_type", "JSAPI"); // 支付类型 paraMap.put("notify_url", "用户支付完成后,你想微信调你的哪个接口");// 此路径是微信服务器调用支付结果通知路径随意写 String sign = WXPayUtil.generateSignature(paraMap, AuthUtil.PATERNERKEY); paraMap.put("sign", sign); String xml = WXPayUtil.mapToXml(paraMap);// 将所有参数(map)转xml格式 // 统一下单 https://api.mch.weixin.qq.com/pay/unifiedorder String unifiedorder_url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; System.out.println("xml为:" + xml); // String xmlStr = HttpRequest.sendPost(unifiedorder_url, // xml);//发送post请求"统一下单接口"返回预支付id:prepay_id String xmlStr = HttpRequest.httpsRequest(unifiedorder_url, "POST", xml); System.out.println("xmlStr为:" + xmlStr); // 以下内容是返回前端页面的json数据 String prepay_id = "";// 预支付id if (xmlStr.indexOf("SUCCESS") != -1) { Map<String, String> map = WXPayUtil.xmlToMap(xmlStr); prepay_id = (String) map.get("prepay_id"); } Map<String, String> payMap = new HashMap<String, String>(); payMap.put("appId", AuthUtil.APPID); payMap.put("timeStamp", WXPayUtil.getCurrentTimestamp() + ""); payMap.put("nonceStr", WXPayUtil.generateNonceStr()); payMap.put("signType", "MD5"); payMap.put("package", "prepay_id=" + prepay_id); String paySign = WXPayUtil.generateSignature(payMap, AuthUtil.PATERNERKEY); payMap.put("paySign", paySign); //将这个6个参数传给前端 return payMap; } catch (Exception e) { e.printStackTrace(); } return null; } /** * @Title: callBack * @Description: 支付完成的回调函数 * @param: * @return: */ @RequestMapping("/notify") public String callBack(HttpServletRequest request, HttpServletResponse response) { // System.out.println("微信支付成功,微信发送的callback信息,请注意修改订单信息"); InputStream is = null; try { is = request.getInputStream();// 获取请求的流信息(这里是微信发的xml格式所有只能使用流来读) String xml = WXPayUtil.InputStream2String(is); Map<String, String> notifyMap = WXPayUtil.xmlToMap(xml);// 将微信发的xml转map System.out.println("微信返回给回调函数的信息为:"+xml); if (notifyMap.get("result_code").equals("SUCCESS")) { String ordersSn = notifyMap.get("out_trade_no");// 商户订单号 String amountpaid = notifyMap.get("total_fee");// 实际支付的订单金额:单位 分 BigDecimal amountPay = (new BigDecimal(amountpaid).divide(new BigDecimal("100"))).setScale(2);// 将分转换成元-实际支付金额:元 /* * 以下是自己的业务处理------仅做参考 更新order对应字段/已支付金额/状态码 */ System.out.println("===notify===回调方法已经被调!!!"); } // 告诉微信服务器收到信息了,不要在调用回调action了========这里很重要回复微信服务器信息用流发送一个xml即可 response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>"); } catch (Exception e) { e.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } }
HttpRequest类:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.URL; import java.net.URLConnection; import java.util.List; import java.util.Map; import javax.net.ssl.HttpsURLConnection; public class HttpRequest { /** * 向指定URL发送GET方法的请求 * * @param url * 发送请求的URL * @param param * 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @return URL 所代表远程资源的响应结果 */ public static String sendGet(String url, String param) { String result = ""; BufferedReader in = null; try { String urlNameString = url + "?" + param; System.out.println(urlNameString); URL realUrl = new URL(urlNameString); // 打开和URL之间的连接 URLConnection connection = realUrl.openConnection(); // 设置通用的请求属性 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 建立实际的连接 connection.connect(); // 获取所有响应头字段 Map<String, List<String>> map = connection.getHeaderFields(); // 遍历所有的响应头字段 for (String key : map.keySet()) { System.out.println(key + "--->" + map.get(key)); } // 定义 BufferedReader输入流来读取URL的响应 in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送GET请求出现异常!" + e); e.printStackTrace(); } // 使用finally块来关闭输入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return result; } /** * 向指定 URL 发送POST方法的请求 * * @param url * 发送请求的 URL * @param param * 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @return 所代表远程资源的响应结果 */ public static String sendPost(String url, String param) { PrintWriter out = null; BufferedReader in = null; String result = ""; try { URL realUrl = new URL(url); // 打开和URL之间的连接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流 out = new PrintWriter(conn.getOutputStream()); // 发送请求参数 out.print(param); // flush输出流的缓冲 out.flush(); // 定义BufferedReader输入流来读取URL的响应 in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送 POST 请求出现异常!" + e); e.printStackTrace(); } // 使用finally块来关闭输出流、输入流 finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException ex) { ex.printStackTrace(); } } return result; } /** * post请求并得到返回结果 * * @param requestUrl * @param requestMethod * @param output * @return */ public static String httpsRequest(String requestUrl, String requestMethod, String output) { try { URL url = new URL(requestUrl); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); connection.setRequestMethod(requestMethod); if (null != output) { OutputStream outputStream = connection.getOutputStream(); outputStream.write(output.getBytes("UTF-8")); outputStream.close(); } // 从输入流读取返回内容 InputStream inputStream = connection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; connection.disconnect(); return buffer.toString(); } catch (Exception ex) { ex.printStackTrace(); } return ""; } }
AuthUtil类
import java.io.IOException; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import net.sf.json.JSONObject; public class AuthUtil { public static final String APPID = "平台ID"; public static final String APPSECRET = "平台密钥"; public static final String MCHID = "商家ID"; public static final String PATERNERKEY = "商家密钥"; public static JSONObject doGetJson(String url) throws ClientProtocolException, IOException { JSONObject jsonObject = null; // 首先初始化HttpClient对象 DefaultHttpClient client = new DefaultHttpClient(); // 通过get方式进行提交 HttpGet httpGet = new HttpGet(url); // 通过HTTPclient的execute方法进行发送请求 HttpResponse response = client.execute(httpGet); // 从response里面拿自己想要的结果 HttpEntity entity = response.getEntity(); if (entity != null) { String result = EntityUtils.toString(entity, "UTF-8"); jsonObject = jsonObject.fromObject(result); } // 把链接释放掉 httpGet.releaseConnection(); return jsonObject; } }
2.3 前端的实现
这是只用一个jsp页面来做测试
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="./js/jquery-1.11.0.min.js"></script> <title>微信支付JSP</title> </head> <body> <input type="button" value="进行微信支付" id="payId"> <script type="text/javascript"> $(function(){ var appId,timeStamp,nonceStr,package,signType,paySign; $("#payId").click(function(){ pay(); }); //去后台拿六个参数 function pay(){ var url = "http://localhost:8082/WeChat/pay/orders"; $.get(url,function(result) { appId = result.appId; timeStamp = result.timeStamp; nonceStr = result.nonceStr; package = result.package; signType = result.signType; paySign = result.paySign; if (typeof WeixinJSBridge == "undefined") { if (document.addEventListener) { document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); } else if (document.attachEvent) { document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } alert("请在微信上进行支付操作!"); onBridgeReady(); } else { onBridgeReady(); } }); } //去微信那边发起支付请求 function onBridgeReady(){ alert("appId:"+appId+" "+"timeStamp:"+timeStamp+" "+"nonceStr:"+nonceStr+" "+"package:"+package+" "+"signType:"+signType+" "+"paySign:"+paySign+" "); WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId":appId, //公众号名称,由商户传入 "timeStamp":timeStamp, //时间戳,自1970年以来的秒数 "nonceStr":nonceStr, //随机串 "package":package, "signType":signType, //微信签名方式: "paySign":paySign //微信签名 }, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) { //alert('支付成功'); console.log("支付成功"); //支付成功后跳转的页面 }else if(res.err_msg == "get_brand_wcpay_request:cancel"){ alert('支付取消'); }else if(res.err_msg == "get_brand_wcpay_request:fail"){ alert('支付失败'); alert(JSON.stringify(res)); WeixinJSBridge.call('closeWindow'); } //使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。 }); } }) </script> </body> </html>
三,总结
虽然第一次看官方文档很乱,信息量很多,但仔细总结一下,其实就下面这点流程而已。
再记录点,以防有用:
url urlencode
public static String inputStream2String(InputStream inStream, String encoding){ String result = null; ByteArrayOutputStream outStream = null; try { if(inStream != null){ outStream = new ByteArrayOutputStream(); byte[] tempBytes = new byte[1024]; int count = 0; while((count = inStream.read(tempBytes)) != -1){ outStream.write(tempBytes, 0, count); } tempBytes = null; outStream.flush(); result = new String(outStream.toByteArray(), encoding); outStream.close(); } } catch (Exception e) { result = null; } return result; }
转载:
https://blog.csdn.net/daotiao0199/article/details/85284038
https://blog.csdn.net/javaYouCome/article/details/79473743