业务场景:基本上做业务的话,也是逃不开对接各种支付接口的,比如数字人民币支付、农行免密支付、支付宝支付、微信支付等等。在着手开发时候,也是遇到不少阻力,微信官方提供的接口文档很散乱,如果之前没接触过,一上来就来搞微信支付,即使参考很多文档、材料,也是很令人抓狂的。在这篇文章中,主要记录的就是对接微信支付的流程,以及开发中遇到的问题。

一、了解支付流程

即使这样,还是推荐去阅览 微信app支付开发文档

商户自行申请入驻微信支付,无服务商协助。(商户平台申请)成为直连商户

申请APPID

申请mchid

绑定APPID及mchid

配置API key

(这些是需要准备的参数)

二、开发流程

2.1 添加依赖

    <dependency><groupId>com.github.liyiorg</groupId><artifactId>weixin-popular</artifactId><version>2.8.5</version></dependency>

2.2 建配置类:WxPayConfig


public class WxPayConfig {/**   APPID */public static final String APPID = "";/** 商户号 */public static final String MCH_ID = "";/** 密钥 */public static final String PRIVATE_KEY = "";/**AppSecret 微信开放平台 对应app的开发密码,app支付不需要 */public static final String APPSECRET = "";/** 用户订单支付结果异步通知url */public static final String NOTIFY_URL = "";}

2.3 建工具类:WxPayConfig

import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;import javax.servlet.http.HttpServletRequest;
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 java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.util.*;public class WXPayUtil {/*** 生成微信支付sign* @return*/public static String createSign(SortedMap<String, String> params, String key){StringBuilder sb = new StringBuilder();Set<Map.Entry<String, String>> es =  params.entrySet();Iterator<Map.Entry<String, String>> it =  es.iterator();//生成 stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";while (it.hasNext()){Map.Entry<String, String> entry = (Map.Entry<String, String>)it.next();String k = (String)entry.getKey();String v = (String)entry.getValue();if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)){sb.append(k+"="+v+"&");}}sb.append("key=").append(key);String sign = MD5(sb.toString()).toUpperCase();return sign;}/*** XML格式字符串转换为Map* @param strXML XML字符串* @return XML数据转换后的Map* @throws Exception*/public static Map<String, String> xmlToMap(String strXML) throws Exception {try {Map<String, String> data = new HashMap<String, String>();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) {}return data;} catch (Exception ex) {throw ex;}}/*** 将Map转换为XML格式的字符串* @param data Map类型数据* @return XML格式的字符串* @throws Exception*/public static String mapToXml(Map<String, String> 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;}/*** 生成uuid,即用来标识一笔单,也用做 微信支付的nonce_str* @return*/public static String generateUUID(){String uuid = UUID.randomUUID().toString().replaceAll("-","").substring(0,32);return uuid;}/*** MD* @param data* @return*/public static String MD5(String data){try {MessageDigest md5 = MessageDigest.getInstance("MD5");byte [] array = md5.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();}catch (Exception e){e.printStackTrace();}return null;}/*** 获取用户请求ip* @param request* @return*/public static String getIpAddr(HttpServletRequest request) {String ipAddress = request.getHeader("x-forwarded-for");if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("Proxy-Client-IP");}if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("WL-Proxy-Client-IP");}if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getRemoteAddr();if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {//根据网卡取本机配置的IPInetAddress inet = null;try {inet = InetAddress.getLocalHost();} catch (UnknownHostException e) {e.printStackTrace();}ipAddress = inet.getHostAddress();}}//对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15if (ipAddress.indexOf(",") > 0) {ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));}}return ipAddress;}/*** 封装post,返回请求的结果* @return*/public static String doPost(String url, String data, int timeout){CloseableHttpClient httpClient =  HttpClients.createDefault();//超时设置RequestConfig requestConfig =  RequestConfig.custom().setConnectTimeout(timeout) //连接超时.setConnectionRequestTimeout(timeout)//请求超时.setSocketTimeout(timeout).setRedirectsEnabled(true)  //允许自动重定向.build();HttpPost httpPost  = new HttpPost(url);httpPost.setConfig(requestConfig);httpPost.addHeader("Content-Type","application/json;charset=utf-8");if(data != null && data instanceof String){ //使用字符串传参StringEntity stringEntity = new StringEntity(data,"UTF-8");httpPost.setEntity(stringEntity);}try{CloseableHttpResponse httpResponse = httpClient.execute(httpPost);HttpEntity httpEntity = httpResponse.getEntity();if(httpResponse.getStatusLine().getStatusCode() == 200){String result = EntityUtils.toString(httpEntity, "UTF-8");return result;}}catch (Exception e){e.printStackTrace();}finally {try{httpClient.close();}catch (Exception e){e.printStackTrace();}}return null;}}

2.4 建控制层:WxController

    /*** 微信app支付* @param* @return* @throws Exception*/@PostMapping("/WXPayment")ResultMsg<Object> WXPayment(@RequestBody OrderVO orderVO) throws Exception {ResultMsg<Object> resultMsg = new ResultMsg<>();try {resultMsg = wxService.WXPayment(orderVO);} catch (Exception e) {e.printStackTrace();resultMsg.setSuccess(false);resultMsg.setResultMsg("系统异常,支付失败!");return resultMsg;}return resultMsg;}/*** 微信支付回调* @param request* @param response* @return* @throws Exception*/@PostMapping(value = "/wechatPay/callback")public String wechatPayCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {return wxService.wechatPayCallback(request, response);}

2.4 service实现类:WxServiceImpl

/**** 微信预支付接口* @param orderVO* @return*/@Overridepublic ResultMsg<Object> WXPayment(OrderVO orderVO) {ResultMsg<Object> resultMsg = new ResultMsg<Object>();if (StringUtils.isBlank(orderVO.getUserId()) ||StringUtils.isBlank(orderVO.getOrderId())||StringUtils.isBlank(orderVO.getType())||StringUtils.isBlank(orderVO.getTotalPrice())) {resultMsg.setSuccess(false);resultMsg.setResultMsg("信息不完整!");return resultMsg;}Unifiedorder unifiedorder = new Unifiedorder();unifiedorder.setAppid(WxPayConfig.APPID);unifiedorder.setMch_id(WxPayConfig.MCH_ID);unifiedorder.setNonce_str(UUID.randomUUID().toString().replaceAll("-", ""));unifiedorder.setBody(orderVO.getOrderId());//订单id 拉起后,在微信页面上方显示的数据unifiedorder.setOut_trade_no(orderVO.getOrderId());//订单idString receivableAmount = orderVO.getTotalPrice();//自己业务的支付总价,但是微信支付接口以分为单位String money = new BigDecimal(receivableAmount).multiply(new BigDecimal("100")).stripTrailingZeros().toPlainString();String fee = money;unifiedorder.setTotal_fee(fee); // 订单总金额,单位为分;unifiedorder.setSpbill_create_ip("127.0.0.1");unifiedorder.setNotify_url(WxPayConfig.NOTIFY_URL);//回调接口地址,用来接收微信通知,以及处理我们自己的业务逻辑unifiedorder.setTrade_type("APP");unifiedorder.setAttach(orderVO.getType());//附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用,实际情况下只有支付完成状态才会返回该字段。示例值:自定义数据 说白了就是前端调微信拉起预支付接口的某些参数,需要我们在回调接口处理业务逻辑使用log.info("微信APP支付--(签名前):" + XMLConverUtil.convertToXML(unifiedorder));/** 获取签名 */UnifiedorderResult unifiedorderResult = PayMchAPI.payUnifiedorder(unifiedorder, WxPayConfig.PRIVATE_KEY);log.info("微信APP支付--支付统一下单接口请求状态(return_code):" + unifiedorderResult.getReturn_code());log.info("微信APP支付--支付统一下单接口请求状态(return_msg):" + unifiedorderResult.getReturn_msg());log.info("微信APP支付--支付统一下单接口请求状态(result_code):" + unifiedorderResult.getResult_code());log.info("微信APP支付--支付请求参数封装(签名后):" + XMLConverUtil.convertToXML(unifiedorder));log.info("微信APP支付--支付统一下单接口返回数据:" + JsonUtil.toJson(unifiedorderResult));// 下单结果验签;if (unifiedorderResult.getSign_status() != null && unifiedorderResult.getSign_status()) {log.info("微信APP支付验签成功");MchPayApp generateMchAppData = PayUtil.generateMchAppData(unifiedorderResult.getPrepay_id(), WxPayConfig.APPID, WxPayConfig.MCH_ID,WxPayConfig.PRIVATE_KEY);Map<String, Object> map = new HashMap<>();map.put("payOrderId", orderVO.getOrderId());//订单idmap.put("mchPayApp", generateMchAppData);log.info(" WXPayment  return map" + map);resultMsg.setData(map);resultMsg.setSuccess(true);return resultMsg;}return resultMsg;}/**** 微信回调接口* @return*/@Overridepublic String wechatPayCallback(HttpServletRequest request, HttpServletResponse response) {ResultMsg<Object> resultMsg = new ResultMsg<>();// 解析微信支付异步通知请求参数;String xml = null;try {xml = StreamUtils.copyToString(request.getInputStream(), Charset.forName("utf-8"));} catch (IOException e) {e.printStackTrace();}Map<String, String> params = XMLConverUtil.convertToMap(xml);MchPayNotify payNotify = XMLConverUtil.convertToObject(MchPayNotify.class, xml);/** 打印日志信息 */log.info("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$---进入微信支付异步通知请求接口---$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");log.info("微信支付用户订单支付结果异步通知请求参数(xml):" + params);log.info("微信支付用户订单支付结果异步通知请求参数(map):" + params);log.info("return_code:" + payNotify.getReturn_code());log.info("return_msg:" + params.get("return_msg"));String out_trade_no = payNotify.getOut_trade_no();  // 用户订单号;String money = String.valueOf(payNotify.getTotal_fee());//支付金额String total_amount = new BigDecimal(money).multiply(new BigDecimal("0.01")).stripTrailingZeros().toPlainString();//单位换算成元String trade_no = payNotify.getTransaction_id();//微信交易号//CampOrder campOrder = campDao.selectOrderDetail(orderId);/** 校验支付成功还是失败 */if ("SUCCESS".equals(payNotify.getReturn_code())) {/** 获取微信后台返回来的异步通知参数 */String tradeNo = payNotify.getTransaction_id();   // 微信交易号;String tradeStatus = payNotify.getResult_code();  // 微信支付状态;Integer totalFee = payNotify.getTotal_fee();      // 支付金额 (单位:分)String  subject = payNotify.getAttach(); // 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用,实际情况下只有支付完成状态才会返回该字段。示例值:自定义数据boolean flag = SignatureUtil.validateSign(params, WxPayConfig.PRIVATE_KEY);//返回结果return_code 和result_code都为SUCCESS的时候才算成功if (flag && "SUCCESS".equals(tradeStatus)) {/** 更新订单支付信息: */log.info("********************** 支付成功(微信异步通知) **********************");log.info("* 订单号: " + out_trade_no);log.info("* 微信交易号: " + trade_no);log.info("* 实付金额: " + total_amount);log.info("***************************************************************");/************         愣着干嘛,处理业务啊            ************/log.info("微信支付成功...");/** 封装通知信息 */MchBaseResult baseResult = new MchBaseResult();baseResult.setReturn_code("SUCCESS");baseResult.setReturn_msg("OK");xml = XMLConverUtil.convertToXML(baseResult);} else {MchBaseResult baseResult = new MchBaseResult();baseResult.setReturn_code("FAIL");baseResult.setReturn_msg("FAIL");xml = XMLConverUtil.convertToXML(baseResult);//TODO 支付失败;逻辑}} else {MchBaseResult baseResult = new MchBaseResult();baseResult.setReturn_code("FAIL");baseResult.setReturn_msg("FAIL");xml = XMLConverUtil.convertToXML(baseResult);}log.info("微信支付--APP支付方式支付用户订单异步通知返回数据:" + xml);return xml;}

【微信支付】(亲测可用)对接app微信支付V2版本 后端代码示例相关推荐

  1. 微信6月5号12点半发布可以更改微信号,为什么我的微信号不能修改?解决办法【亲测可用】,微信号修改,改微信号方法

    2020年6月5号12点半,微信官方称微信最新版本可以更改微信号了,只限安卓版,苹果的可能要等一段时间才行,安卓的能改也是一年改一次 不过对于年少轻狂年代使用的微信号,现在回头看看,那感觉-哎!言语无 ...

  2. 前端控制微信分享到群,朋友圈的方法,js禁止微信分享 亲测可用

    直接上代码,下面代码都是开发过程中记录的怕丢了,就在这备个份,使用过程中有问题可以私信或者联系微信:wxid_7yiygpz72wk622 另也承接小程序,网站开发,H5开发,程序二次开发,APP开发 ...

  3. Ubuntu16.04安装qq和微信(亲测 可用)附安装包下载链接

    最近把笔记本系统换成了ubuntu16.04,结果在用的时候需要用到微信和qq,但根据网上的好多资料都不能成功安装,弄的一晚上,终于装好了. qq下载链接:http://mirrors.aliyun. ...

  4. 微信支付rootca.pem通用根证书文件,亲测可用

    微信支付rootca.pem通用根证书文件,2018年5月后微信支付默认不提供. 我当初找了好久又花钱又费时才找的可以用的证书,亲测可用. 链接:https://download.csdn.net/d ...

  5. zkeys阿帕云对接易支付插件,支持zkeys阿帕云最新版(亲测可用)

    阿帕云对接易支付的文件,可以让你的阿帕云具有对接易支付的接口. zkeys已经改名为阿帕云 以下版本都可以使用 ZKEYS云管平台(大陆版)更新至v5.3.3版本 ZKEYS分销平台更新至v5.2.3 ...

  6. 【webview】微信和PC监听浏览器关闭和刷新(亲测可用)

    监听浏览器关闭和刷新 前言 PC端 微信端 前言 最近做的项目里有一个新的需求,在不同的浏览器内打开的网页,监听用户的操作,比如关闭浏览器,刷新浏览器等等. 这就涉及到了几个平台:PC端浏览器,移动端 ...

  7. java+uniapp对接app微信支付

    java+uniapp对接app微信支付 1.准备工作 注册app APP接入微信支付,需要先将商户APP在微信开放平台进行注册,登记APP开发参数以生成APPID.具体操作步骤如下: 一.登录微信开 ...

  8. h5微信本地调试 vue_UniApp本地调试H5(谷歌chrome浏览器)跨域问题的解决方法,亲测可用...

    UniApp本地调试H5跨域(谷歌chrome浏览器,UniApp内置浏览器不存在此问题)的推荐方案:欺骗浏览器,让浏览器觉得你没有跨域(其实还是跨域了,用的是代理) 客户器端解决方案(亲测可用): ...

  9. 微信小程序码的生成(JAVA完整版) 亲测可用

    JAVA生成小程序码(太阳码) 首先准备工具类,这里我使用的是QrUtil;废话不多说,上工具类; 工具类是获取token使用; appid = 小程序appID secret = 小程序秘钥 /** ...

最新文章

  1. android重启软件用不了,应用程序重启而不是重启
  2. 开发教程(四) MIP组件平台使用说明
  3. android手势识别
  4. linux c之遍历字符串数组
  5. hdu-1728(贪心bfs的灵活运用吧)
  6. CWnd类与Windows窗口的关系-3、CWnd类如何封装Windows窗口
  7. 基于相对熵优化VMD的非局部均值去噪方法
  8. java 调用热敏打印机_java调用芯烨热敏打印机 TSPL2指令 打印没有反应
  9. Python学习记录 使用百度aip模块(API Python-SDK)实现人脸识别
  10. 金蝶迷你版凭证导入工具_金蝶kis迷你版如何插入凭证?
  11. 导航算法A*的简单实现
  12. 手机扫码传文件免安装免流工具教程
  13. 采用deepdive的上市公司关系抽取
  14. Nginx之父被抓!员工“接私活儿”到底合不合法?
  15. 克莱姆V(克莱姆相关系数、克莱姆关联系数、独立系数)的MATLAB计算
  16. 2017lol服务器维修时间,lol年3月16日要维护多长时间2017_lol3.16维护时间公告_好特教程...
  17. Flutter基础之部分控件学习
  18. 模糊神经网络学习1【基础案例】
  19. 医学领域图像相关自述
  20. Runtime Error(ACCESS_VIOLATION)常见解决方法

热门文章

  1. 理解和正确使用Java中的断言(assert)
  2. 计算机网络——知识点超详细总结
  3. 文件删除如何恢复?简单的方法
  4. Google Glass众叛亲离?
  5. 电子书下载Developing for Google Glass
  6. 京东双十一店铺装修,免费海报设计软件
  7. 简单做好游戏中“攻击动作”的3个窍门
  8. 上海海事大学计算机专硕招得不满么,注意!近期大批院校初试科目调整汇总,这些院校专业停招全日制研究生...
  9. 在K-Means算法中使用肘部法寻找最佳聚类数
  10. swagger2入门程序启动报错Exception encountered during context initialization - cancelling refresh attempt: o