微信支付流程:
1、在页面点击支付发生的动作
将订单参数传递后台->后台根据订单参数以及微信相关信息->获取支付所需要的参数->后台处理支付所需要的参数返回给前端->前端获取到支付参数->发起支付请求->支付成功的页面会显示跳回商家所设置的回调路径->后台根据回调路径处理相关业务逻辑;
2、程序案例;从官网中下载相关API
页面:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>every thing will be will</title> <script type="text/javascript" src="../static/js/jquery-3.3.1.min.js"></script> </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:8062/wxpay/order"; $.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>
后台业务逻辑
所需的参数:
public class WxParam { public final static String WX_APPID = "";//商家平台ID,微信平台 public final static String WX_BODY = ""; //商品描述 public final static String WX_MCH_ID = "";//商户ID ,微信平台 public final static String WX_NONCE_STR = "";//随机字符串,UUID public final static String WX_OPEN_ID = "";//用户OPENID public final static String WX_OUT_TRADE_NO = "";//商户订单号 public final static String WX_SPBILL_CREATE_IP = "";//终端IP,从请求头拿到 public final static String WX_TOTAL_FEE = "";//支付金额,单位是分 public final static String WX_TRADE_TYPE = "";//交易类型;JSAPI public final static String WX_NOTIFY_URL = "";//通知地址;用户支付成功后,返回应用程序的接口; public final static String WX_SIGN = "";//签名,根据上面10个参数计算获得 public final static String WX_PATERNERKEY = "";//商家密钥 }
前端所需参数的处理,以及回调路径
@Controller @RequestMapping("/wxpay") public class WxPayAction { /** * 订单采用微信支付所需的参数接口 * @param request * @return */ @RequestMapping("/order") @ResponseBody public Map<String,String> orders(HttpServletRequest request){ try { String openId = "用户的openid"; //存放微信支付所需的参数 final Map<String,String> paraMap = new HashMap<>(); // 获取请求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(); } String uuid = UUID.randomUUID().toString(); // paraMap.put("appid",WxParam.WX_APPID); paraMap.put("body","微信测试商品内容" ); paraMap.put("mch_id",WxParam.WX_MCH_ID ); paraMap.put("nonce_str",uuid ); paraMap.put("openid",openId ); paraMap.put("out_trade_no",UUID.randomUUID().toString().replaceAll("-", "") );//订单号 paraMap.put("spbill_create_ip",ip );//终端IP,从请求头拿到 paraMap.put("total_fee","1" );//支付金额,1分 paraMap.put("trade_type","JSAPI" );//支付类型 paraMap.put("notify_url","回调url" ); //这里的key是商家密钥;这一步将回调路径设置到sign参数里面了 String sign = WXPayUtil.generateSignedXml(paraMap,WxParam.WX_PATERNERKEY); paraMap.put("sign",sign ); //将map转换为xml String xml = WXPayUtil.mapToXml(paraMap); // 统一下单 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 = HttpRequestUtil.httpsRequest(unifiedorder_url, "POST", xml); System.out.println("预支付返回的信息:"+xmlStr); //上面的数据主要是为了获得预支付id;就相当于微信上提交订单(订单包含了回调函数),然后返回一个微信的订单id; //以下内容返回前端页面的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<>(); payMap.put("appId",WxParam.WX_APPID ); //后面为什么要加"" payMap.put("timeStamp",WXPayUtil.getCurrentTimestamp()+"" ); payMap.put("nonceStr",WXPayUtil.generateNonceStr() ); payMap.put("signType","MD5" ); payMap.put("package","prepay_id="+prepay_id); String paySin = WXPayUtil.generateSignature(payMap, WxParam.WX_PATERNERKEY); payMap.put("paySign",paySin ); return payMap; }catch (Exception e){ } return null; } /** * 回调函数,处理业务逻辑 * @param request * * @param response * * @return */ @RequestMapping("/notify") public String wxCallBack(HttpServletRequest request, HttpServletResponse response){ InputStream is = null; try{ is = request.getInputStream(); InputStreamReader isr=new InputStreamReader(is,"utf-8"); BufferedReader br=new BufferedReader(isr); StringBuffer buffer=new StringBuffer(); String line=null; while((line=br.readLine())!=null){ buffer.append(line); } //从流中获取 String xml = buffer.toString(); Map<String,String> notifyMap = WXPayUtil.xmlToMap(xml); 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===回调方法已经被调!!!"); } // 告诉微信服务器收到信息了,回复微信服务器信息用流发送一个xml即可 response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>"); }catch (Exception e){ }finally { if(is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } }
用到的工具类
public class HttpRequestUtil { /* * 处理https GET/POST请求 * 请求地址、请求方法、参数 * */ public static String httpsRequest(String requestUrl,String requestMethod,String outputStr){ StringBuffer buffer=null; try{ //创建SSLContext SSLContext sslContext=SSLContext.getInstance("SSL"); TrustManager[] tm={new MyX509TrustManager()}; //初始化 sslContext.init(null, tm, new java.security.SecureRandom());; //获取SSLSocketFactory对象 SSLSocketFactory ssf=sslContext.getSocketFactory(); URL url=new URL(requestUrl); HttpsURLConnection conn=(HttpsURLConnection)url.openConnection(); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); conn.setRequestMethod(requestMethod); //设置当前实例使用的SSLSoctetFactory conn.setSSLSocketFactory(ssf); conn.connect(); //往服务器端写内容 if(null!=outputStr){ OutputStream os=conn.getOutputStream(); os.write(outputStr.getBytes("utf-8")); os.close(); } //读取服务器端返回的内容 InputStream is=conn.getInputStream(); InputStreamReader isr=new InputStreamReader(is,"utf-8"); BufferedReader br=new BufferedReader(isr); buffer=new StringBuffer(); String line=null; while((line=br.readLine())!=null){ buffer.append(line); } }catch(Exception e){ e.printStackTrace(); } return buffer.toString(); } }
public class MyX509TrustManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { // TODO Auto-generated method stub } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { // TODO Auto-generated method stub } @Override public X509Certificate[] getAcceptedIssuers() { // TODO Auto-generated method stub return null; } }