微信开发中经常使用到微信支付,那么此篇文章将告诉您如何完成微信支付,以下内容以小程序支付为例,代码较多请左右滑动屏幕查看代码
导入依赖
org.apache.directory.studio org.apache.commons.codec 1.8 dom4j dom4j 1.6.1
User2MchSTO
/** * @author RickSun && iFillDream * @version 1.0 * 小程序统一下单 */public class User2MchSTO { //小程序ID private String appId; //商户号 private String mchId; //用户标识openId private String openId; //商户秘钥(加密用) private String signKey; //商户系统内部订单号,在同一个商户号下唯一 private String outTradeNo; //订单总金额,单位为分 private Integer totalFee; //回调地址 private String notifyUrl; //支付给商户-支付类型,JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支付 private String tradeType; //省略getter/setter方法}
FinalStr
public class FinalStr { public static final String WX_PAY_OK = ""; public static final String WX_PAY_SIGN_FAIL = ""; public static final String WX_PAY_DATE_NULL = "";}
XmlUtil
import org.apache.commons.lang3.StringUtils;import org.dom4j.Document;import org.dom4j.DocumentHelper;import org.dom4j.Element;import org.w3c.dom.Node;import org.w3c.dom.NodeList;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.parsers.ParserConfigurationException;import java.io.ByteArrayInputStream;import java.io.InputStream;import java.util.HashMap;import java.util.Map;import java.util.TreeMap;/** * @author RickSun && iFillDream * @version 1.0 * Xml工具类 */public class XmlUtil { /** * 生成Xml字符串 * @param sortedParams 有序map参数 * @return */ public static String map2XMLStr(Map sortedParams) { if (!(sortedParams instanceof TreeMap)) { Map temp = new TreeMap(); temp.putAll(sortedParams); sortedParams = temp; } Document document = DocumentHelper.createDocument(); Element root = document.addElement("xml"); for (Map.Entry entry : sortedParams.entrySet()) { String k = entry.getKey(); Object v = entry.getValue(); if (StringUtils.isBlank(k) || StringUtils.isBlank(v.toString())) { continue; } Element addElement = root.addElement(k); addElement.setText(v.toString()); } return document.getRootElement().asXML(); } /** * xml转map * @param xml xml字符串 * @return */ public static Map xmlToMap(String xml) { Map data = new HashMap(); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); String FEATURE = null; try { FEATURE = "http://apache.org/xml/features/disallow-doctype-decl"; documentBuilderFactory.setFeature(FEATURE, true); FEATURE = "http://xml.org/sax/features/external-general-entities"; documentBuilderFactory.setFeature(FEATURE, false); FEATURE = "http://xml.org/sax/features/external-parameter-entities"; documentBuilderFactory.setFeature(FEATURE, false); FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; documentBuilderFactory.setFeature(FEATURE, false); documentBuilderFactory.setXIncludeAware(false); documentBuilderFactory.setExpandEntityReferences(false); } catch (ParserConfigurationException e) { System.out.println("ParserConfigurationException was thrown. The feature '" + FEATURE + "' is probably not supported by your XML processor."); } try { DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); InputStream stream = new ByteArrayInputStream(xml.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) { //TODO } } catch (Exception ex) { //TODO } return data; }}
WxPayUtil
public class WxPayUtil { /** * 获取签名 * @param sortedParams 待签支付参数 * @param signKey 支付密钥即signKey * @return */ public static String getSign(Map sortedParams, String signKey) { StringBuilder builder = new StringBuilder(); if (!(sortedParams instanceof TreeMap)) { Map temp = new TreeMap<>(); temp.putAll(sortedParams); sortedParams = temp; } for (Map.Entry entry : sortedParams.entrySet()) { String k = entry.getKey(); Object v = entry.getValue(); if (StringUtils.isBlank(k) || StringUtils.isBlank(v.toString())) { continue; } if (builder.length() != 0) { builder.append("&"); } builder.append(k); builder.append("="); builder.append(v); } builder.append("&key="); builder.append(signKey); System.out.println(builder.toString()); return DigestUtils.md5Hex((builder.toString()).toUpperCase(); } /** * map转xml进行请求 * @param wxUrl 请求地址 * @param sortedParams 请求参数 * @return */ public static Map xmlPost(String wxUrl,Map sortedParams){ String parseXml = XmlUtil.map2XMLStr(sortedParams); Map resultInfo = null; try { //此处的UrlUtil就是HTTP请求工具,需要此工具的可后台回复UrlUtil关键字即可获取,此处不在展示出UrlUtil的代码 String s = UrlUtil.doPost(wxUrl, parseXml); resultInfo = XmlUtil.xmlToMap(s); if("SUCCESS".equals(resultInfo.get("return_code")) && "SUCCESS".equals(resultInfo.get("result_code")) ){ return R.successd( resultInfo ); }else{ throw new GlobalException(ErrorCode.PAY_ERR.setContent("失败"),JsonUtil.toJson(resultInfo),true); } } catch (IOException e) { e.printStackTrace(); throw new GlobalException(ErrorCode.PAY_ERR.setContent("失败"),e,true); } }}
获取支付参数
通过以下代码获取支付参数给前端,前端通过这些参数进行支付,随后在回调中验证是否支付成功。
private static final String unifiedorderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";/** * @author RickSun && iFillDream *小程序用户支付给商户 **/public static Map user2Mch(User2MchSTO user2MchSTO) { Map sortedParams = new TreeMap<>(); sortedParams.put("appid",user2MchSTO.getAppId()); sortedParams.put("mch_id",user2MchSTO.getMchId()); //随机32位以内的字符串 sortedParams.put("nonce_str",RandomUtil.randAlphaNumString(31).toUpperCase()); sortedParams.put("out_trade_no",user2MchSTO.getOutTradeNo()); sortedParams.put("total_fee",user2MchSTO.getTotalFee()); //支付时的IP地址 sortedParams.put("spbill_create_ip",getLocalIP()); sortedParams.put("notify_url",user2MchSTO.getNotifyUrl()); sortedParams.put("trade_type",user2MchSTO.getTradeType()); sortedParams.put("body","商品简单描述"); sortedParams.put("openid",user2MchSTO.getOpenId()); sortedParams.put("sign",getSign(sortedParams,user2MchSTO.getSignKey())); System.out.println("sign:" + sortedParams.get("sign")); Map fristResult = xmlPost(unifiedorderUrl,sortedParams); //再次签名 Map returnMap = new TreeMap<>(); //返回的预付单信息 returnMap.put("appId",user2MchSTO.getAppId()); returnMap.put("nonceStr", sortedParams.get("nonce_str")); String prepay_id = (String)fristResult.get("prepay_id"); returnMap.put("package", "prepay_id=" + prepay_id); returnMap.put("signType","MD5"); //时间戳 returnMap.put("timeStamp", Instant.now().getEpochSecond()+""); //加密 returnMap.put("paySign",getSign(returnMap,user2MchSTO.getSignKey())); fristResult.setData(returnMap); return fristResult;}
回调工具
/** * @author RickSun && iFillDream * @version 1.0 * @data 2020-08-27 15:09 * @description 回调工具 */public class NotifyUtil { public static String getRequestData(HttpServletRequest request) throws Exception { String charset = getCharset(request); StringBuffer buffer = new StringBuffer(); BufferedReader rd = null; try { rd = new BufferedReader(new InputStreamReader(request.getInputStream(), charset)); String tempLine = null; while ((tempLine = rd.readLine()) != null) { buffer.append(tempLine); } } finally { try { if (rd != null) { rd.close(); } } catch (IOException e) { e.printStackTrace(); } } return buffer.toString().trim(); } public static String getCharset(HttpServletRequest request) { if (request != null) { return request.getCharacterEncoding(); } return "UTF-8"; }}
支付回调
public String wxNotify(HttpServletRequest httpServletRequest, HttpServletResponse response) { System.out.println("-----------开始回调-------------"); try { String weixinReqXml = NotifyUtil.getRequestData(httpServletRequest); if (!StringUtils.isEmpty(weixinReqXml)) { Map srcMap =XmlUtil.xmlToMap(weixinReqXml); Map tempMap = srcMap; Map wxMap = tempMap; String return_code = (String)wxMap.get("return_code"); if ("SUCCESS".equals(return_code)) { String result_code = (String)wxMap.get("result_code"); String retruenSign = (String)wxMap.get("sign"); wxMap.remove("sign"); // 商户系统的订单号,与请求一致 String out_trade_no = (String)wxMap.get("out_trade_no"); if ("SUCCESS".equals(result_code)) { String sign = WxPayUtil.getSign(wxMap, KEY); // 签名校验 if ( sign.equals(retruenSign) ) { //TODO 业务处理 return FinalStr.WX_PAY_OK; } else { return FinalStr.WX_PAY_SIGN_FAIL; } } else { // 支付失败 String err_code = (String)wxMap.get("err_code"); String err_code_des = (String)wxMap.get("err_code_des"); if ("ORDERCLOSED".equals(err_code)) { return FinalStr.WX_PAY_DATE_NULL; } else { return FinalStr.WX_PAY_DATE_NULL; } } } else { String return_msg = (String)wxMap.get("return_msg"); throw new GlobalException(ErrorCode.WECHAT_PAY_NOTIFY_ERR,return_msg,true); } } } catch (Exception e) { throw new GlobalException(ErrorCode.WECHAT_PAY_NOTIFY_ERR,e,true); } return FinalStr.WX_PAY_OK;}
注:此处的GlobalException是自定义的异常,您可替换为您自己的或者直接抛出对应异常,亦可针对这些异常进行额外处理。
到此微信小程序的支付就完成了,由于技术有限代码肯定存在不足之处,尽请谅解。
如果您遇到不解的地方请关注微信公众号"笔记有云"后台留言即可
【更多精彩】
Java获取小程序手机号码
图片优雅的转换成Base64
Linux脚本启动jar包