700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Java集成微信小程序支付和退款

Java集成微信小程序支付和退款

时间:2022-07-05 15:13:46

相关推荐

Java集成微信小程序支付和退款

微信支付文档:https://pay./wiki/doc/apiv3/open/pay/chapter1_1_1.shtml

微信退款文档:https://pay./wiki/doc/apiv3/apis/chapter3_1_9.shtml

1、微信小程序支付

前提

在进行对接微信支付之前,我们首先需要将以下几点准备好:

申请APPID申请商户号小程序开通微信支付,绑定已经申请好的商户号。登录小程序后台(mp.)。点击左侧导航栏的微信支付,在页面中进行开通。(开通申请要求小程序已发布上线)

注意事项

appid必须为最后拉起收银台的小程序appid;mch_id为和appid成对绑定的支付商户号,收款资金会进入该商户号;trade_type请填写JSAPI;openid为appid对应的用户标识,即使用wx.login接口获得的openid。

本文主要记录后端步骤,前端步骤无非就是获取后端数据然后调用提供的API进行支付,大家可自行查看官方文档。

1. 支付业务流程图

2. 导入依赖

implementation group: 'com.github.wechatpay-apiv3', name: 'wechatpay-apache-httpclient', version: '0.4.7'implementation group: 'com.github.wxpay',name: 'wxpay-sdk',version: '0.0.3'

3. 编写相关工具类

解析Xml工具类

/*** @author zzw* @description TODO 解析xml工具类* @date -04-28 14:02*/public class XMLUtil {/** * 瑙f瀽xml,杩斿洖绗竴绾у厓绱犻敭鍊煎銆傚鏋滅锟�?绾у厓绱犳湁瀛愯妭鐐癸紝鍒欐鑺傜偣鐨勶拷?锟芥槸瀛愯妭鐐圭殑xml鏁版嵁锟�? * @param strxml * @author Lyp* @return * @throws JDOMException * @throws IOException */ public static Map doXMLParse(String strxml) throws JDOMException, IOException {strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); if(null == strxml || "".equals(strxml)) {return null; } Map m = new HashMap(); InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8")); SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(in); Element root = doc.getRootElement(); List list = root.getChildren(); Iterator it = list.iterator(); while(it.hasNext()) {Element e = (Element) it.next(); String k = e.getName(); String v = ""; List children = e.getChildren(); if(children.isEmpty()) {v = e.getTextNormalize(); } else {v = XMLUtil.getChildrenText(children); } m.put(k, v); } //鍏抽棴锟�? in.close(); return m; } /** * 鑾峰彇瀛愮粨鐐圭殑xml * @param children * @return String */ public static String getChildrenText(List children) {StringBuffer sb = new StringBuffer(); if(!children.isEmpty()) {Iterator it = children.iterator(); while(it.hasNext()) {Element e = (Element) it.next(); String name = e.getName(); String value = e.getTextNormalize(); List list = e.getChildren(); sb.append("<" + name + ">"); if(!list.isEmpty()) {sb.append(XMLUtil.getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); }}

微信支付工具类

/*** @author zzw* @description TODO 微信支付工具类* @date -04-28 14:02*/public class WXPayUtil {/*** 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.getTagName());}}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;}}/*** ??ap?????ML?????????* @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;}/*** ?????? sign ?? XML ????????* @param data Map??????* @param key API???* @return ???sign?????ML*/public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {return generateSignedXml(data, key, SignType.MD5);}/*** ?????? sign ?? XML ????????* @param data Map??????* @param key API???* @param signType ??????* @return ???sign?????ML*/public static String generateSignedXml(final Map<String, String> 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<String, String> data = xmlToMap(xmlStr);if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {return false;}String sign = data.get(WXPayConstants.FIELD_SIGN);return generateSignature(data, key).equals(sign);}/*** ????????????????????ign???????????alse?????D5?????** @param data Map??????* @param key API???* @return ?????????* @throws Exception*/public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {return isSignatureValid(data, key, SignType.MD5);}/*** ????????????????????ign???????????alse??** @param data Map??????* @param key API???* @param signType ??????* @return ?????????* @throws Exception*/public static boolean isSignatureValid(Map<String, String> 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<String, String> 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<String, String> data, String key, SignType signType) throws Exception {Set<String> 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 {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);}/*** ??????ip???* @return*/public static String localip(){String ip=null;Enumeration allNetInterfaces;try {allNetInterfaces=NetworkInterface.getNetworkInterfaces();while(allNetInterfaces.hasMoreElements()){NetworkInterface netInterface=(NetworkInterface)allNetInterfaces.nextElement();List<InterfaceAddress> InterfaceAddress=netInterface.getInterfaceAddresses();for(InterfaceAddress add:InterfaceAddress){InetAddress Ip=add.getAddress();if(Ip!=null&&Ip instanceof Inet4Address){ip=Ip.getHostAddress();}}}} catch (SocketException e) {System.out.println("??????ip???????????");e.printStackTrace();}return ip;}}

支付相关工具类

public class PayForUtil {private static Logger lg=Logger.getLogger(PayForUtil.class); /** * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 * @return boolean */ public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {StringBuffer sb = new StringBuffer(); Set es = packageParams.entrySet(); Iterator it = es.iterator(); while(it.hasNext()) {Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); String v = (String)entry.getValue(); if(!"sign".equals(k) && null != v && !"".equals(v)) {sb.append(k + "=" + v + "&"); } } sb.append("key=" + API_KEY); //算出摘要 String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase(); String tenpaySign = ((String)packageParams.get("sign")).toLowerCase(); return tenpaySign.equals(mysign); } /** * @Description:sign签名 * @param characterEncoding * 编码格式 * @param parameters * 请求参数 * @return */ public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {StringBuffer sb = new StringBuffer(); Set es = packageParams.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) {Map.Entry entry = (Map.Entry) 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=" + API_KEY); String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; } /** * @Description:将请求参数转换为xml格式的string * @param parameters * 请求参数 * @return */ public static String getRequestXml(SortedMap<Object,Object> parameters) {StringBuffer sb = new StringBuffer(); sb.append("<xml>"); Set es = parameters.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) {Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">"); } else {sb.append("<" + k + ">" + v + "</" + k + ">"); } } sb.append("</xml>"); return sb.toString(); } /** * 取出一个指定长度大小的随机正整数. * * @param length * int 设定所取出随机数的长度。length小于11 * @return int 返回生成的随机数。 */ public static int buildRandom(int length) {int num = 1; double random = Math.random(); if (random < 0.1) {random = random + 0.1; } for (int i = 0; i < length; i++) {num = num * 10; } return (int) ((random * num)); } /** * 获取当前时间 yyyyMMddHHmmss * @return String */ public static String getCurrTime() {Date now = new Date(); SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss"); String s = outFormat.format(now); return s; } }

金额换算工具类

public class Utils {public static String getFee(String fee) {System.out.println(Float.valueOf(fee).floatValue());Float a =Float.valueOf(fee).floatValue()*100;System.out.println(a);fee = String.valueOf(a);//浮点变量a转换为字符串str//先把小数点后的0截取掉int idx = fee.lastIndexOf(".");//查找小数点的位置System.out.println(idx);String strNum = fee.substring(0,idx);//截取从字符串开始到小数点位置的字符串,就是整数部分System.out.println(strNum);//将截取后的金额转换为整数int num = Integer.valueOf(strNum);//把整数部分通过Integer.valueof方法转换为数字//工行金额以分为单位,将金额* 100// num = num * 100;System.out.println(num);return num+"";}public static String getFee2(String fee) {System.out.println(Float.valueOf(fee).floatValue());// System.out.println( String.valueOf(+ Float.parseFloat(fee)) );Float a =Float.valueOf(fee).floatValue()/100;System.out.println(a);fee = String.valueOf(a);//浮点变量a转换为字符串strreturn fee+"";}public static boolean isNumeric(String str){for (int i = str.length();--i>=0;){if (!Character.isDigit(str.charAt(i))){return false;}}return true;}public static void main(String[] args) {System.out.println(getFee("2.50"));}}

4. 生成预支付交易单

商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付。

入参:

/*** @author zzw* @date /4/1 15:51* @description TODO 微信WxPayConfig配置*/@Datapublic class PayVo {private String description; // 商品描述private String out_trade_no; // 商户订单号private String time_expire; // 订单失效时间private String attach; // 附加数据private String notify_url; // 回调private String openid; // 用户标识private String total; // 总金额}

预支付订单业务逻辑层

/*** 电子处方订单支付接口* @param payVo* @return*/@Overridepublic String payPrescription(PayVo payVo) {try {String errorString = null;String errorCode = null;String resXml = null;String nonce_str = WXPayUtil.generateNonceStr();JSONArray json = null;Map map = null;SortedMap<Object, Object> orderMap = new TreeMap<Object, Object>();/*----- 1.生成预支付订单需要的的package数据-----*/SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();// mchid直连商户号、wxappid应用ID、paysuccesscallback通知地址 都需要从程序内部获取,从而保证支付的安全性// paysuccesscallback 通知地址需要跟程序内部的地址一致packageParams.put("appid",wxappid); packageParams.put("mch_id", mchid);packageParams.put("nonce_str", nonce_str);packageParams.put("notify_url", paysuccesscallback);packageParams.put("attach", payVo.getAttach());packageParams.put("openid", payVo.getOpenid());packageParams.put("out_trade_no", prescriptionPayVo.getOut_trade_no());packageParams.put("spbill_create_ip", WXPayUtil.localip());packageParams.put("total_fee",.ih.java.main.utils.Utils.getFee(payVo.getTotal()));packageParams.put("trade_type", "JSAPI");packageParams.put("fee_type", "CNY");packageParams.put("body", prescriptionPayVo.getDescription());packageParams.put("sign_type", "MD5");/*----2.根据package生成签名sign---- */String sign = PayForUtil.createSign("UTF-8", packageParams, selfkey);packageParams.put("sign", sign);logger.info("###signWX" + sign);String requestXML = PayForUtil.getRequestXml(packageParams);logger.info("###requestXML" + requestXML);resXml = WxHttpUtil.postData("https://api.mch./pay/unifiedorder",requestXML);logger.info("###resXml" + resXml);if (null == resXml || "".equals(resXml)) {errorString = "接口异常!返回数据为空,请检查接口是否可用;接口地址:https://api.mch./pay/unifiedorder" ;resXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> <xml> <return_code>" + "fail"+ "</return_code><return_msg>" + errorString + "</return_msg></xml> ";logger.info("###" + errorString);orderMap.put("ResultCode", "-1");orderMap.put("ErrorMsg", "执行失败。");json = JSONArray.fromObject(orderMap);} else {try {map = XMLUtil.doXMLParse(resXml);String return_code = (String) map.get("return_code");System.out.println(return_code);String timestamp = String.valueOf(System.currentTimeMillis() / 1000);String nonceStr = String.valueOf(System.currentTimeMillis());orderMap.put("appId", sysConfig.getWxappid());// 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符orderMap.put("timeStamp", timestamp);orderMap.put("nonceStr", nonceStr);logger.info("###nonceStr" + nonceStr);orderMap.put("package", "prepay_id=" + map.get("prepay_id"));orderMap.put("signType", "MD5");String sign1 = PayForUtil.createSign("UTF-8", orderMap, selfkey);orderMap.put("paySign", sign1);orderMap.put("resCode", return_code);logger.info("###sign" + map.get("sign"));json = JSONArray.fromObject(orderMap);} catch (JDOMException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}logger.info("###调用JSAPI预支付下单接口需要返回的参数" + json.toString());return json.toString();}catch(Exception e) {e.printStackTrace();return super.errorResult(e.getMessage());}

5. 支付成功回调

@ApiOperation(value = "支付成功回调")@RequestMapping(value="/paySucessCallMethod")public void paySuccessCallMethod(HttpServletRequest req, HttpServletResponse response){logger.info("========== 开始处理订单支付回调通知 ==========");String resultStr = null;String str = null;InputStream inputStream;StringBuffer sb = new StringBuffer();try {inputStream = req.getInputStream();String s;BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));while ((s = in.readLine()) != null) {sb.append(s);}in.close();inputStream.close();// 解析xml成mapMap<String, String> m = new HashMap<String, String>();m = XMLUtil.doXMLParse(sb.toString());// 过滤空设置TreeMap,扫码SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();Iterator<String> it = m.keySet().iterator();while (it.hasNext()) {String parameter = it.next();String parameterValue = m.get(parameter);String v = "";if (null != parameterValue) {v = parameterValue.trim();}packageParams.put(parameter, v);}// 微信支付的api密钥String key = privatekey;logger.info("微信支付返回回来的参数:" + packageParams);String return_code = (String) packageParams.get("return_code");String result_code = (String) packageParams.get("result_code");DrugOrder drugOrder = null;// 判断签名是否正确if (PayForUtil.isTenpaySign("UTF-8", packageParams, key)) {// -------------------------------// 处理业务开始// --------------------------String resXml = "";if (StrKit.notBlank(return_code) && StrKit.notBlank(result_code)&& return_code.equalsIgnoreCase("SUCCESS") && result_code.equalsIgnoreCase("SUCCESS")) {// 支付成功// 执行自己的业务逻辑// 声明日志插入结果对象String outTradeNo = ((String) packageParams.get("out_trade_no"));logger.info("#########################开始校验订单号:"+outTradeNo+"是否存在" );Order order = orderPayRepository.findOrderPayByOutTradeNo(outTradeNo);if(!StringUtils.isEmpty(order)) {Order order = order;// 非第一次回调,已经成功生成订单,直接返回结果就可以了/// 执行自己业务逻辑结束logger.info("给微信回调返回成功");// 通知微信异步成功不然会一直通知后台八次之后交易失败resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";}else {//第一次回调// **************************1.流水表插入*******************************//logger.info("###流水表插入开始");logger.info("###流水表插入开始packageParams:",packageParams);/* 执行相关业务逻辑,支付成功,生成支付订单 */logger.info("###流水表插入结束");// 执行自己业务逻辑结束logger.info("给微信回调返回成功");// 通知微信异步成功不然会一直通知后台八次之后交易失败resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";}} else {logger.info("支付失败,错误信息:" + packageParams.get("err_code"));resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";}// ------------------------------// 处理业务完毕// ------------------------------BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());out.write(resXml.getBytes());out.flush();out.close();} else {logger.info("通知签名验证失败");}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}logger.info("==========结束处理处方支付回调通知==========");}

2、微信小程序退款

当交易发生之后一年内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付金额退还给买家,微信支付将在收到退款请求并且验证成功之后,将支付款按原路退还至买家账号上。

前提

(1)微信退款所需要的配置! 退款只需要证书即可。微信退款需要证书:资金发生变化需要证书。支付接口不需要。点击证书使用。

按照步骤:下载证书。

(2)使用API证书

◆ apiclient_cert.p12是商户证书文件,除PHP外的开发均使用此证书文件。

◆ 商户如果使用.NET环境开发,请确认Framework版本大于2.0,必须在操作系统上双击安装证书apiclient_cert.p12后才能被正常调用。

◆ API证书调用或安装需要使用到密码,该密码的值为微信商户号(mch_id)

(3)API证书安全

1.证书文件不能放在web服务器虚拟目录,应放在有访问权限控制的目录中,防止被他人下载;

2.建议将证书文件名改为复杂且不容易猜测的文件名;

3.商户服务器要做好病毒和木马防护工作,不被非法侵入者窃取证书文件。

证书相关API:/liyanlei5858/article/details/120021692

注意:

1、交易时间超过一年的订单无法提交退款

2、微信支付退款支持单笔交易分多次退款(不超50次),多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。 一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号

3、错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款申请请求不超过6次

4、每个支付订单的部分退款次数不能超过50次

5、如果同一个用户有多笔退款,建议分不同批次进行退款,避免并发退款导致退款失败

6、申请退款接口的返回仅代表业务的受理情况,具体退款是否成功,需要通过退款查询接口获取结果

7、一个月之前的订单申请退款频率限制为:5000/min

8、同一笔订单多次退款的请求需相隔1分钟

1. 编写退款相关工具类

public class PayConfig implements WXPayConfig {private byte[] certData;/*** 微信退款所需要的配置! 退款只需要证书即可。* @throws Exception*/public PayConfig() throws Exception {File file = new File(WxPayConstant.getPrivateppath());InputStream certStream = new FileInputStream(file);this.certData = new byte[(int) file.length()];certStream.read(this.certData);certStream.close();}@Overridepublic String getAppID() {return WxPayConstant.getWxappid(); //appid}public String getAPPSECRET(){return WxPayConstant.getAppsecret(); //appSecret}@Overridepublic String getMchID() {return WxPayConstant.getMchid(); //商户号id}@Overridepublic String getKey() {return WxPayConstant.getPrivatekey(); //支付API密钥}@Overridepublic InputStream getCertStream() {ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);return certBis;}@Overridepublic int getHttpConnectTimeoutMs() {return 8000;}@Overridepublic int getHttpReadTimeoutMs() {return 10000;}}

2. 退款代码

@Overridepublic String refund(String transaction_id) {try {logger.info("#############################################执行微信支付退款接口refund方法开始");/* 执行微信小程序退款相关业务逻辑 */logger.info("==========================微信退款开始!!========================");Order order = orderRepository.findOrderByTransactionId(transaction_id);Map<String,String> data = new HashMap<String,String>();Integer pay = order.getTotalFee();OrderRefund orderRefund = orderRefundRepository.findOrderRefundByTransactionId(transaction_id);if (!ObjectUtils.isEmpty(drugOrderRefund)){logger.info("==========================此订单号对应的退款表已存在,重新执行退款操作!!========================"+orderRefund.toString());data.put("out_refund_no" , orderRefund.getOutRefundNo());}else {String out_refund_no = UUIDHexGenerator.createTradeNo();data.put("out_refund_no" , out_refund_no);}// data.put("notify_url", null); // 根据自己的需求决定需要回调地址data.put("transaction_id" , transaction_id);data.put("total_fee" , String.valueOf(pay));data.put("refund_fee" , String.valueOf(pay));PayConfig config = new PayConfig();WXPay wxpay = new WXPay(config);data.put("appid" , appid);data.put("mch_id" , mch_id);data.put("nonce_str" , WXPayUtil.generateNonceStr());data.put("sign" , MD5Util.getSign(data));Map<String,String> resp = wxpay.refund(data);//获取微信退款返回的结果logger.info("微信返回信息:\n" + resp);String return_code = resp.get("return_code"); // 返回状态码String return_msg = resp.get("return_msg");// 返回信息// 不管有没有退款成功,都要保存到退款表,如果退款成功,则删除支付表,如果退款失败,则退款表状态为失败logger.info("####################################################################流水表插入开始");OrderRefund orderRefund=new OrderRefund();List<OrderRefund> orderRefundList = orderRefundRepository.findOrderRefundByOutRefundNo(order.getOutTradeNo());if (CollectionUtils.isEmpty(orderRefundList)) {//保存退款信息logger.info("#############################################################流水表对象赋值操作结束");// 调用service,插入日志表logger.info("#######################################################调用service,插入流水表开始");if("SUCCESS".equals(return_code)){String result_code = resp.get("result_code"); //业务结果String err_code_des = resp.get("err_code_des");//错误代码描述if("SUCCESS".equals(result_code)){// 执行退款相关业务逻辑logger.info("####################################################################流水表插入结束");// 执行自己业务逻辑结束logger.info("#####################################################################退款成功");}else{// 退款失败}}else{// 退款失败}OrderRefund saveAndFlush = orderRefundRepository.saveAndFlush(orderRefund);logger.info("#####################################################################执行退款回调结束,退款表type: "+saveAndFlush.getType());}resMap.put("timestamp", TimeHelper.getCurrentTime());String results = JsonHelper.parserMap(resMap);return results;} catch (Exception e) {e.printStackTrace();return super.errorResult(e.getMessage());}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。