Java 微信支付之APP支付服务端 (一)

xiaoxiao2021-02-28  8

Java 微信支付之APP支付服务端 (一)
如图所示,这是服务端要集成的所有微信接口。至于在开放平台申请就不做赘述了。主要流程,1.下单,2.异步通知,3.查询。
一、微信统一下单请求交易 /** * 微信统一下单请求交易 * * @param userId * @param amount * @return */ @RequestMapping(value=Route.Payment.WEIXIN_PAY,method=RequestMethod.POST) @ResponseBody public Response pay(@PathVariable("userId") int userId,@PathVariable("amount") String amount) { log.info("InfoMsg:--- 微信统一下单请求交易开始"); Response resp = this.getReponse(); String message = ""; try { ThirdPayBean bean = thirdPayService.findByPayId(19); String mch_id = bean.getMer_no(); //商户号 String notify_url = bean.getReturn_url(); //通知地址 PlatformMutualityManagent pmm = platformMutualityManagentService.findOne(5); String appid = pmm.getClient_id(); //应用ID String App_Secret = pmm.getClient_secret(); String nonce_str = WXPayUtil.generateNonceStr(); //随机字符串 String sign = ""; //签名 String sign_type = "MD5"; //签名类型 String body = "循心"; //商品描述 ps:腾讯充值中心-QQ会员充值 String out_trade_no = OrderGeneratedUtils.getOrderNo(); //商户订单号 int total_fee = WXRandomNumberUtil.wx_format_PayAmount(amount); //交易金额 String spbill_create_ip = InetAddress.getLocalHost().getHostAddress(); //终端IP String trade_type = "APP"; //交易类型 String attach = userId + "|" + bean.getBank_id(); //附加数据 ps:用户|支付方式 StringBuffer sb = new StringBuffer(); sb.append("appid=").append(appid).append("&"); sb.append("attach=").append(attach).append("&"); sb.append("body=").append(body).append("&"); sb.append("mch_id=").append(mch_id).append("&"); sb.append("notify_url=").append(notify_url).append("&"); sb.append("nonce_str=").append(nonce_str).append("&"); sb.append("out_trade_no=").append(out_trade_no).append("&"); sb.append("sign_type=").append(sign_type).append("&"); sb.append("spbill_create_ip=").append(spbill_create_ip).append("&"); sb.append("total_fee=").append(total_fee).append("&"); sb.append("trade_type=").append(trade_type).append("&"); String params = sb.toString(); //需要签名的数据 String stringSignTemp = params + "&key=" + App_Secret; //MD5签名方式 sign = WXPayUtil.MD5(stringSignTemp).toUpperCase(); Map map = new HashMap<>(); map.put("appid", appid); map.put("attach", attach); map.put("body", body); map.put("mch_id", mch_id); map.put("notify_url", notify_url); map.put("nonce_str", nonce_str); map.put("out_trade_no", out_trade_no); map.put("sign", sign); map.put("sign_type", sign_type); map.put("spbill_create_ip", spbill_create_ip); map.put("total_fee", total_fee); map.put("trade_type", trade_type); List theaderList = new ArrayList<>(); theaderList.add(new UHeader("Content-Type", "application/x-www-form-urlencoded")); //Httpclient发送请求 String postResponse = MaryunHttpUtils.getPostResponse(weixin_pay_Url, map, theaderList); //解析返回的XML数据 Map response = WXPayUtil.xmlToMap(postResponse); if(!response.isEmpty() && response.get("return_code").equals("SUCCESS")){ if(response.get("result_code").equals("SUCCESS")) { boolean result = rechargeRecordService.generatedBills(response,attach); if(result) { log.info("InfoMsg:--- 微信统一下单请求交易成功"); } }else { message = response.get("err_code_des"); log.error("errorMsg:--- 微信统一下单请求交易解析失败" + message); } }else { log.error("errorMsg:--- 微信统一下单请求交易解析失败" + message); } log.info("InfoMsg:--- 微信统一下单请求交易结束"); return resp.success(); } catch (Exception e) { log.error("errorMsg:--- 微信统一下单请求交易失败" + e.getMessage()); return resp.failure(e.getMessage()); } }
二、微信异步通知 /** * 微信支付通知地址 * * @param request * @return */ @RequestMapping(value=Route.Payment.WEIXIN_PAY_NOTIFY,method=RequestMethod.POST) @ResponseBody public Response weixin_pay_notify(HttpServletRequest request) { log.info("infoMsg:--- 微信异步通知开始"); Response resp = this.getReponse(); BufferedReader reader = null; String wx_map = ""; try { PlatformMutualityManagent pmm = platformMutualityManagentService.findOne(5); Assert.notNull(pmm); String app_key = pmm.getClient_id(); reader = request.getReader(); String line = ""; String xmlString = null; StringBuffer inputString = new StringBuffer(); while ((line = reader.readLine()) != null) { inputString.append(line); } xmlString = inputString.toString(); request.getReader().close(); if(!StringUtils.isBlank(xmlString)) { Map return_map = WXPayUtil.xmlToMap(xmlString); //验签 if(WXPayUtil.isSignatureValid(xmlString, app_key)) { if(return_map.get("return_map").equals("SUCCESS")) { //TODO 账变,修改状态,到账提醒 Double amount = Double.parseDouble(return_map.get("total_fee")); String passbackParams = return_map.get("total_fee"); String order_no = return_map.get("out_trade_no"); boolean result = rechargeRecordService.updateBill(amount,passbackParams,order_no); if(result) { Map map = new HashMap<>(); map.put("return_map", "SUCCESS"); map.put("return_msg", "OK"); wx_map = WXPayUtil.mapToXml(map); } } } } log.info("infoMsg:--- 微信异步通知失败"); return resp.success(wx_map); } catch (Exception e) { log.error("errorMsg:--- 微信通知失败" + e.getMessage()); return resp.failure(e.getMessage()); } }
三、微信支付订单查询 /** * 微信支付订单查询 * * @param transaction_id 微信订单号 * @param out_trade_no 平台订单号 * @return */ @RequestMapping(value=Route.Payment.WEIXIN_PAY_QUERY,method=RequestMethod.POST) @ResponseBody public Response weixin_pay_query(@PathVariable("transaction_id") String transaction_id,@PathVariable("out_trade_no") String out_trade_no) { log.info("infoMsg:--- 微信支付订单查询开始"); Response resp = this.getReponse(); String sign = ""; String message = ""; Map return_map = null; try { ThirdPayBean bean = thirdPayService.findByPayId(19); Assert.notNull(bean); String mch_id = bean.getMer_no(); //商户号 PlatformMutualityManagent pmm = platformMutualityManagentService.findOne(5); Assert.notNull(pmm); String appid = pmm.getClient_id(); //应用ID String App_Secret = pmm.getClient_secret(); String nonce_str = WXPayUtil.generateNonceStr(); //随机字符串 StringBuffer sb = new StringBuffer(); sb.append("appid=").append(appid).append("&"); sb.append("nonce_str=").append(nonce_str).append("&"); sb.append("out_trade_no=").append(out_trade_no).append("&"); String params = sb.toString(); //需要签名的数据 String stringSignTemp = params + "&key=" + App_Secret; //MD5签名方式 sign = WXPayUtil.MD5(stringSignTemp).toUpperCase(); Map req_map = new HashMap<>(); req_map.put("appid", appid); req_map.put("mch_id", mch_id); req_map.put("transaction_id", transaction_id); req_map.put("out_trade_no", out_trade_no); req_map.put("nonce_str", nonce_str); req_map.put("sign", sign); List headerList = new ArrayList<>(); headerList.add(new UHeader("Content-Type", "application/x-www-form-urlencoded")); String postResponse = MaryunHttpUtils.getPostResponse(weixin_query_Url, req_map, headerList); if(StringUtils.trim(postResponse).equals("")) { return_map = WXPayUtil.xmlToMap(postResponse); if(!return_map.isEmpty()) { String return_code = return_map.get("return_code"); if(return_code.equals("SUCCESS")) { String result_code = return_map.get("return_code"); if(result_code.equals("SUCCESS")) { message = (String) req_map.get("trade_state_desc"); } }else { message = (String) req_map.get("err_code_des"); } } } log.info("infoMsg:--- 微信支付订单查询结束"); return resp.success(return_map); } catch (Exception e) { log.error("erroroMsg:--- 微信支付订单查询失败" + e.getMessage() + message); return resp.failure(e.getMessage() + message); } }
三、微信支付中设计的工具类 1.httpclient package com.xunxin.util; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.RequestEntity; import org.apache.commons.httpclient.methods.StringRequestEntity; import org.apache.commons.httpclient.params.HttpMethodParams; import com.alibaba.fastjson.JSON; /** * Copyright © 2017 noseparte(Libra) © Like the wind, like rain * @Author Noseparte * @Compile 2017年10月24日 -- 下午6:03:20 * @Version 1.0 * @Description Apache HttpClient Util */ public class MaryunHttpUtils { public static class UHeader{ /** * the title of header. */ private String headerTitle; private String headerValue; public UHeader(String headerTitle, String headerValue) { super(); this.headerTitle = headerTitle; this.headerValue = headerValue; } public String getHeaderTitle() { return headerTitle; } public void setHeaderTitle(String headerKey) { this.headerTitle = headerKey; } public String getHeaderValue() { return headerValue; } public void setHeaderValue(String headerValue) { this.headerValue = headerValue; } } public static String getResponse(String url,HashMap args,List headerList){ String info = ""; try { HttpClient client = new HttpClient(); // client = setProxy(client); // client.getHostConfiguration().setProxy("10.170.187.42", 3128); client.setConnectionTimeout(60000); client.setTimeout(60000); GetMethod method = new GetMethod(url); client.getParams().setContentCharset("UTF-8"); if(headerList.size()>0){ for(int i = 0;i args,List headerList){ String info = ""; try { HttpClient client = new HttpClient(); //client = setProxy(client); // client.getHostConfiguration().setProxy("10.170.187.42", 3128); Iterator it = args.entrySet().iterator(); String sargs = ""; while(it.hasNext()){ Entry entry = (Entry)it.next(); sargs += "&"+entry.getKey().toString()+"="+entry.getValue(); } if(!"".equals(sargs)){ sargs = "?"+sargs.substring(1, sargs.length()); } System.out.println(url+sargs); GetMethod method = new GetMethod(url+sargs); // client.getParams().setContentCharset("UTF-8"); if(headerList!=null&&headerList.size()>0){ for(int i = 0;i map,List headerList){ String info = ""; try { HttpClient client = new HttpClient(); //client = setProxy(client); // client.getHostConfiguration().setProxy("10.170.187.42", 3128); PostMethod method = new PostMethod(url); client.getParams().setContentCharset("UTF-8"); if(headerList.size()>0){ for(int i = 0;i args,List headerList,String headerName){ String info = ""; try { HttpClient client = new HttpClient(); PostMethod method = new PostMethod(url); client.getParams().setContentCharset("UTF-8"); if(headerList.size()>0){ for(int i = 0;i headerList){ String info = ""; try { HttpClient client = new HttpClient(); PostMethod method = new PostMethod(url); client.getParams().setContentCharset("UTF-8"); if(headerList.size()>0){ for(int i = 0;i headerList,String headerName){ String info = ""; try { HttpClient client = new HttpClient(); PostMethod method = new PostMethod(url); client.getParams().setContentCharset("UTF-8"); if(headerList.size()>0){ for(int i = 0;i = maxPerMSECSize) { orderNumCount = 0L; } //组装订单号 String countStr=maxPerMSECSize +orderNumCount+""; finOrderNum=nowLong+countStr.substring(1); orderNumCount++; System.out.println(finOrderNum); // Thread.sleep(1000); } return finOrderNum; } catch (Exception e) { e.printStackTrace(); } return null; } 2.商户订单号工具类 OrderGeneratedUtils import java.text.SimpleDateFormat; import java.util.Date; /** * * Copyright © 2017 noseparte(Libra) © Like the wind, like rain * @Author Noseparte * @Compile 2017年11月2日 -- 下午5:18:35 * @Version 1.0 * @Description 订单生成工具类 */ public class OrderGeneratedUtils { /** * 锁对象,可以为任意对象 */ private static Object lockObj = "lockerOrder"; /** * 订单号生成计数器 */ private static long orderNumCount = 0L; /** * 每毫秒生成订单号数量最大值 */ private static int maxPerMSECSize=1000; /** * 生成订单编号 * @return */ public static synchronized String getOrderNo() { try { // 最终生成的订单号 String finOrderNum = ""; synchronized (lockObj) { // 取系统当前时间作为订单号变量前半部分,精确到毫秒 long nowLong = Long.parseLong(new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date())); // 计数器到最大值归零,可扩展更大,目前1毫秒处理峰值1000个,1秒100万 if (orderNumCount >= maxPerMSECSize) { orderNumCount = 0L; } //组装订单号 String countStr=maxPerMSECSize +orderNumCount+""; finOrderNum=nowLong+countStr.substring(1); orderNumCount++; System.out.println(finOrderNum); // Thread.sleep(1000); } return finOrderNum; } catch (Exception e) { e.printStackTrace(); } return null; }   3.微信支付随机算法生成 WXRandomNumberUtil import java.util.Random; /** * * Copyright © 2017 noseparte(Libra) © Like the wind, like rain * @Author Noseparte * @Compile 2017年11月6日 -- 上午11:20:41 * @Version 1.0 * @Description 微信支付生成随机数算法 */ public class WXRandomNumberUtil { /** * 生成随机数算法 * * @param length * @return */ public static String getRandomStringByLength(int length) { String base = "abcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < length; i++) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); } /** * 微信支付交易金额格式化 * * @param amount * @return */ public static int wx_format_PayAmount(String amount) { int pay_amount = 0; pay_amount = Integer.parseInt((amount.split(".")[0])) * 100; return pay_amount; } 4..微信支付工具类(官方) WXPayUtil import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.StringWriter; import java.security.MessageDigest; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.UUID; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.xunxin.util.constants.WXPayConstants; import com.xunxin.util.constants.WXPayConstants.SignType; /** * * Copyright © 2017 noseparte(Libra) © Like the wind, like rain * @Author Noseparte * @Compile 2017年11月6日 -- 下午6:10:22 * @Version 1.0 * @Description 微信支付通用工具类 */ public class WXPayUtil { /** * XML格式字符串转换为Map * * @param strXML XML字符串 * @return XML数据转换后的Map * @throws Exception */ public static Map xmlToMap(String strXML) throws Exception { try { Map data = new HashMap (); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8")); org.w3c.dom.Document doc = documentBuilder.parse(stream); doc.getDocumentElement().normalize(); NodeList nodeList = doc.getDocumentElement().getChildNodes(); for (int idx = 0; idx < nodeList.getLength(); ++idx) { Node node = nodeList.item(idx); if (node.getNodeType() == Node.ELEMENT_NODE) { org.w3c.dom.Element element = (org.w3c.dom.Element) node; data.put(element.getNodeName(), element.getTextContent()); } } try { stream.close(); } catch (Exception ex) { // do nothing } return data; } catch (Exception ex) { WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML); throw ex; } } /** * 将Map转换为XML格式的字符串 * * @param data Map类型数据 * @return XML格式的字符串 * @throws Exception */ public static String mapToXml(Map data) throws Exception { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder(); org.w3c.dom.Document document = documentBuilder.newDocument(); org.w3c.dom.Element root = document.createElement("xml"); document.appendChild(root); for (String key: data.keySet()) { String value = data.get(key); if (value == null) { value = ""; } value = value.trim(); org.w3c.dom.Element filed = document.createElement(key); filed.appendChild(document.createTextNode(value)); root.appendChild(filed); } TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); DOMSource source = new DOMSource(document); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformer.transform(source, result); String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", ""); try { writer.close(); } catch (Exception ex) { } return output; } /** * 生成带有 sign 的 XML 格式字符串 * * @param data Map类型数据 * @param key API密钥 * @return 含有sign字段的XML */ public static String generateSignedXml(final Map data, String key) throws Exception { return generateSignedXml(data, key, SignType.MD5); } /** * 生成带有 sign 的 XML 格式字符串 * * @param data Map类型数据 * @param key API密钥 * @param signType 签名类型 * @return 含有sign字段的XML */ public static String generateSignedXml(final Map data, String key, SignType signType) throws Exception { String sign = generateSignature(data, key, signType); data.put(WXPayConstants.FIELD_SIGN, sign); return mapToXml(data); } /** * 判断签名是否正确 * * @param xmlStr XML格式数据 * @param key API密钥 * @return 签名是否正确 * @throws Exception */ public static boolean isSignatureValid(String xmlStr, String key) throws Exception { Map data = xmlToMap(xmlStr); if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) { return false; } String sign = data.get(WXPayConstants.FIELD_SIGN); return generateSignature(data, key).equals(sign); } /** * 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。 * * @param data Map类型数据 * @param key API密钥 * @return 签名是否正确 * @throws Exception */ public static boolean isSignatureValid(Map data, String key) throws Exception { return isSignatureValid(data, key, SignType.MD5); } /** * 判断签名是否正确,必须包含sign字段,否则返回false。 * * @param data Map类型数据 * @param key API密钥 * @param signType 签名方式 * @return 签名是否正确 * @throws Exception */ public static boolean isSignatureValid(Map data, String key, SignType signType) throws Exception { if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) { return false; } String sign = data.get(WXPayConstants.FIELD_SIGN); return generateSignature(data, key, signType).equals(sign); } /** * 生成签名 * * @param data 待签名数据 * @param key API密钥 * @return 签名 */ public static String generateSignature(final Map data, String key) throws Exception { return generateSignature(data, key, SignType.MD5); } /** * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。 * * @param data 待签名数据 * @param key API密钥 * @param signType 签名方式 * @return 签名 */ public static String generateSignature(final Map data, String key, SignType signType) throws Exception { Set keySet = data.keySet(); String[] keyArray = keySet.toArray(new String[keySet.size()]); Arrays.sort(keyArray); StringBuilder sb = new StringBuilder(); for (String k : keyArray) { if (k.equals(WXPayConstants.FIELD_SIGN)) { continue; } if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名 sb.append(k).append("=").append(data.get(k).trim()).append("&"); } sb.append("key=").append(key); if (SignType.MD5.equals(signType)) { return MD5(sb.toString()).toUpperCase(); } else if (SignType.HMACSHA256.equals(signType)) { return HMACSHA256(sb.toString(), key); } else { throw new Exception(String.format("Invalid sign_type: %s", signType)); } } /** * 获取随机字符串 Nonce Str * * @return String 随机字符串 */ public static String generateNonceStr() { return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32); } /** * 生成 MD5 * * @param data 待处理数据 * @return MD5结果 */ public static String MD5(String data) throws Exception { java.security.MessageDigest md = MessageDigest.getInstance("MD5"); byte[] array = md.digest(data.getBytes("UTF-8")); StringBuilder sb = new StringBuilder(); for (byte item : array) { sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); } return sb.toString().toUpperCase(); } /** * 生成 HMACSHA256 * @param data 待处理数据 * @param key 密钥 * @return 加密结果 * @throws Exception */ public static String HMACSHA256(String data, String key) throws Exception { Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256"); sha256_HMAC.init(secret_key); byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8")); StringBuilder sb = new StringBuilder(); for (byte item : array) { sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); } return sb.toString().toUpperCase(); } /** * 日志 * @return */ public static Logger getLogger() { Logger logger = LoggerFactory.getLogger("wxpay java sdk"); return logger; } /** * 获取当前时间戳,单位秒 * @return */ public static long getCurrentTimestamp() { return System.currentTimeMillis()/1000; } /** * 获取当前时间戳,单位毫秒 * @return */ public static long getCurrentTimestampMs() { return System.currentTimeMillis(); } /** * 生成 uuid, 即用来标识一笔单,也用做 nonce_str * @return */ public static String generateUUID() { return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32); } }
About Me: Github地址:https://github.com/noseparte Email:  noseparte@aliyun.com     有java与hadoop相关的技术问题,可以发私信与我交流。NPM地址:  https://www.npmjs.com/~noseparteWebSite: http://www.noseparte.com/   Copyright © 2017 noseparte
转载请注明原文地址: https://www.6miu.com/read-1250355.html

最新回复(0)