E企盈营销工具技术服务商 热线:4006-838-530

小程序微信退款(退款信息回调解密)

E企盈直播平台营销卖货系统

一、微信支付(简单贴一下)    /** 发起微信支付  */    public Map wechatPay(String openid, HttpServletRequest request, String outTradeNo, Integer totalFee) {        //生成的随机32位字符串        String nonce_str = RandomString.generateRandomString(32, RandomString.POSSIBLE_CHARS);        //商品金额 单位分 将原来的金额*100 元->分        Integer total_fee = (totalFee);        //商品名称        String packageName = “杭家订单:” + outTradeNo;        //获取客户端的ip地址        String spbill_create_ip = getIpAddress(request);        //生成商户订单号        String out_trade_no = outTradeNo;        //组装参数,用户生成统一下单接口的签名        Map<String, String> packageParams = new HashMap<String, String>();        packageParams.put(“appid”, weChatConfig.appId);        packageParams.put(“mch_id”, weChatConfig.mchId);        packageParams.put(“nonce_str”, nonce_str);        packageParams.put(“body”, packageName);        packageParams.put(“out_trade_no”, out_trade_no);//商户订单号        packageParams.put(“total_fee”, String.valueOf(total_fee));//支付金额,这边需要转成字符串类型,否则后面的签名会失败        packageParams.put(“spbill_create_ip”, spbill_create_ip);        packageParams.put(“notify_url”, weChatConfig.notifyUrl);//支付成功后的回调地址        packageParams.put(“trade_type”, weChatConfig.tradetype);//支付方式        packageParams.put(“openid”, openid);        String prestr = PayUtil.createLinkString(packageParams); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串        //MD5运算生成签名,这里是第一次签名,用于调用统一下单接口        String mysign = PayUtil.sign(prestr, weChatConfig.key, “utf-8”).toUpperCase();        //拼接统一下单接口使用的xml数据,要将上一步生成的签名一起拼接进去        String xml = “<xml>” + “<appid>” + weChatConfig.appId + “</appid>”                + “<body><![CDATA[” + packageName + “]]></body>”                + “<mch_id>” + weChatConfig.mchId + “</mch_id>”                + “<nonce_str>” + nonce_str + “</nonce_str>”                + “<notify_url>” + weChatConfig.notifyUrl + “</notify_url>”                + “<openid>” + openid + “</openid>”                + “<out_trade_no>” + out_trade_no + “</out_trade_no>”                + “<spbill_create_ip>” + spbill_create_ip + “</spbill_create_ip>”                + “<total_fee>” + total_fee + “</total_fee>”                + “<trade_type>” + weChatConfig.tradetype + “</trade_type>”                + “<sign>” + mysign + “</sign>”                + “</xml>”;        //调用统一下单接口,并接受返回的结果        String result = PayUtil.httpRequest(weChatConfig.payUrl, “POST”, xml);        System.out.println(“调试模式 返回XML数据:” + result);        // 将解析结果存储在HashMap中        Map map = PayUtil.doXMLParse(result);        String return_code = (String) map.get(“return_code”);//返回状态码        String return_msg = (String) map.get(“return_msg”);//返回状态信息        //返回给移动端需要的参数        Map<String, Object> response = new HashMap<>();        if (“SUCCESS”.equals(return_code)) {            // 业务结果            String prepay_id = (String) map.get(“prepay_id”);//返回的预付单信息            String sign = (String) map.get(“sign”);//微信返回的签名值            response.put(“sign”, sign);            response.put(“signType”, weChatConfig.signtype);            response.put(“nonceStr”, nonce_str);            response.put(“package”, “prepay_id=” + prepay_id);            Long timeStamp = System.currentTimeMillis() / 1000;            response.put(“timeStamp”, timeStamp + “”);//这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误            String stringSignTemp = “appId=” + weChatConfig.appId + “&nonceStr=” + nonce_str + “&package=prepay_id=” + prepay_id + “&signType=” + weChatConfig.signtype + “&timeStamp=” + timeStamp;            //再次签名,这个签名用于小程序端调用wx.requesetPayment方法            String paySign = PayUtil.sign(stringSignTemp, weChatConfig.key, “utf-8”).toUpperCase();            log.info(“=======================第二次签名:” + paySign + “=====================”);            response.put(“paySign”, paySign);            response.put(“totalFee”,total_fee);            /**业务代码*/        } else {            throw new BaseException(ResultEnum.WECHAT_PAY_ERROR, return_msg);        }        response.put(“appid”, weChatConfig.appId);        return response;    }    /** 支付回调 */    @Override    @Transactional    public String wechatPayCallBack(HttpServletRequest request, HttpServletResponse response) {        try {            String line = null;            BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream()));            StringBuilder sb = new StringBuilder();            while ((line = br.readLine()) != null) {                sb.append(line);            }            br.close();            //sb为微信返回的xml            String notityXml = sb.toString();            String resXml = “”;            log.info(“接收到的报文:” + notityXml);            Map map = PayUtil.doXMLParse(notityXml);            String returnCode = (String) map.get(“return_code”);            if (“SUCCESS”.equals(returnCode)) {                //验证签名是否正确                Map<String, String> validParams = PayUtil.paraFilter(map);  //回调验签时需要去除sign和空值参数                String validStr = PayUtil.createLinkString(validParams);//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串                String sign = PayUtil.sign(validStr, weChatConfig.key, “utf-8”).toUpperCase();//拼装生成服务器端验证的签名                //根据微信官网的介绍,此处不仅对回调的参数进行验签,还需要对返回的金额与系统订单的金额进行比对等                if (sign.equals(map.get(“sign”))) {                    log.info(“sign == sigin 签名通过”);                    /**此处添加自己的业务逻辑代码start*/                    String outTradeNo = (String) map.get(“out_trade_no”);                    String transactionId = (String) map.get(“transaction_id”);                    String ip = getIpAddress(request);                    // 支付回调处理                    Boolean check = orderService.wechatPayCallBack(outTradeNo,transactionId,ip);                    if(check){                        // 通知微信服务器已经支付成功                        resXml = “<xml>” + “<return_code><![CDATA[SUCCESS]]></return_code>”                                + “<return_msg><![CDATA[OK]]></return_msg>” + “</xml> “;                    } else {                        // 通知微信服务器已经支付失败                        resXml = “<xml>” + “<return_code><![CDATA[FAIL]]></return_code>”                                + “<return_msg><![CDATA[FAIL]]></return_msg>” + “</xml> “;                    }                    /**此处添加自己的业务逻辑代码end**/                }            } else {                resXml = “<xml>” + “<return_code><![CDATA[FAIL]]></return_code>”                        + “<return_msg><![CDATA[报文为空]]></return_msg>” + “</xml> “;            }            log.info(resXml);            log.info(“微信支付回调数据结束”);            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());            out.write(resXml.getBytes());            out.flush();            out.close();            return resXml;        } catch (Exception e) {            return null;        }    }二、微信退款1.主要代码:    /** 微信退款 */    @Override    public Map<String, String> wechatRefund(int refundMoney, int totalMoney, String out_trade_no) {        //生成的随机32位字符串        String nonce_str = RandomString.generateRandomString(32, RandomString.POSSIBLE_CHARS);        String out_refund_no = SnowflakeIdWorker.getId();//商户退款单号        //签名算法        SortedMap<Object, Object> paramMap = new TreeMap<Object, Object>();        paramMap.put(“appid”, weChatConfig.appId);        paramMap.put(“mch_id”, weChatConfig.mchId);        paramMap.put(“nonce_str”, nonce_str);        paramMap.put(“out_trade_no”, out_trade_no);        paramMap.put(“out_refund_no”, out_refund_no);        paramMap.put(“total_fee”, String.valueOf(totalMoney));        paramMap.put(“refund_fee”, String.valueOf(refundMoney));        paramMap.put(“notify_url”, weChatConfig.refundNotifyUrl);        String sign = PayUtil.createSign(weChatConfig.key,paramMap);        //获取最终待发送的数据        String requestXml = “<xml>” +                “<appid>” + weChatConfig.appId + “</appid>” +                “<mch_id>” + weChatConfig.mchId + “</mch_id>” +                “<nonce_str>” + nonce_str + “</nonce_str>” +                “<out_trade_no>” + out_trade_no + “</out_trade_no>” +                “<out_refund_no>” + out_refund_no + “</out_refund_no>” +                “<total_fee>” + String.valueOf(totalMoney) + “</total_fee>” +                “<refund_fee>” + String.valueOf(refundMoney) + “</refund_fee>” +                “<notify_url>” + weChatConfig.refundNotifyUrl + “</notify_url>” +                “<sign>” + sign + “</sign>” +                “</xml>”;        //定义本函数返回值:        Map<String,String> returnMap = new HashMap<>();        try {            //建立连接并发送数据            String result = PayUtil.WeixinSendPostToRefund(weChatConfig.refundUrl,requestXml, weChatConfig.mchId);            //解析返回的xml            Map<String,String> resultMap = PayUtil.doXMLParse(result);            //退款返回标志码            String return_code = resultMap.get(“return_code”).toString();            String result_code = resultMap.get(“result_code”).toString();            if(“SUCCESS”.equals(return_code) && “SUCCESS”.equals(result_code)){                //code…                returnMap.put(“status”,”success”);                returnMap.put(“msg”,”发起微信退款成功”);                returnMap.put(“transactionId”,resultMap.get(“transaction_id”));                returnMap.put(“outTradeNo”,resultMap.get(“out_trade_no”));                returnMap.put(“outRefundNo”,resultMap.get(“out_refund_no”));                return returnMap;            }else if(return_code.equals(“SUCCESS”) && result_code.equals(“FAIL”)){                returnMap.put(“status”,”fail”);                returnMap.put(“msg”,”微信原路返款失败!”);                return returnMap;            }else{                returnMap.put(“status”,”fail”);                returnMap.put(“msg”,”微信原路返款失败!”);                return returnMap;            }        }catch (Exception e){            e.printStackTrace();            returnMap.put(“status”,”fail”);            returnMap.put(“msg”,e.getMessage());            return returnMap;        }    }2.证书使用微信退款是需要证书的,怎么获取证书,微信支付文档比任何人说得都详细/**  微信退款–向微信端发送post请求 */    public static String WeixinSendPostToRefund(String url,String xmlObj,String mch_id) throws Exception{        ClassPathResource classPathResource = new ClassPathResource(“apiclient_cert.p12”);//证书路径(此处我用的相对路径,你也可以考虑安全性,放在项目外)        InputStream instream = classPathResource.getInputStream();        KeyStore keyStore = KeyStore.getInstance(“PKCS12”);        try {            keyStore.load(instream, mch_id.toCharArray());        } catch (Exception e) {            e.printStackTrace();        } finally {            instream.close();        }        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mch_id.toCharArray()).build();        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { “TLSv1” }, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();        String result = “”;        try {            HttpPost httpPost = new HttpPost(url);            HttpEntity xmlData = new StringEntity((String) xmlObj, “text/xml”, “iso-8859-1”);            httpPost.setEntity(xmlData);            CloseableHttpResponse response = httpclient.execute(httpPost);            try {                HttpEntity entity = response.getEntity();                result = EntityUtils.toString(entity, “UTF-8″);                System.out.println(response.getStatusLine());                EntityUtils.consume(entity);            } finally {                response.close();            }        } finally {            httpclient.close();        }        //去除空格        return result.replaceAll(” “, “”);    }3.代码中引用的方法生成随机字符串:你可以用微信提供的javaSDK,其中有这个,而且map转xml和xml转map等都有商户订单号:我用的SnowflakeIdWorker,这个随你,只要唯一就行了sign签名: /**     * 微信退款–sign签名     */    public static String createSign(String secretKey, SortedMap<Object, Object> parameters) {        StringBuffer sb = new StringBuffer();        Set<Map.Entry<Object, Object>> es = parameters.entrySet();        Iterator<Map.Entry<Object, Object>> it = es.iterator();        while (it.hasNext()) {            Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) it.next();            String k = (String) entry.getKey();            Object v = entry.getValue();            if (null != v && !””.equals(v) && !”sign”.equals(k) && !”key”.equals(k)) {                sb.append(k + “=” + v + “&”);            }        }        sb.append(“key=” + secretKey);        String sign = MD5Utils.MD5(sb.toString()).toUpperCase();        parameters.put(“sign”, sign);        return sign;    }三、微信退款回调首先不用多说,在外网地址上开放一个接口1.主要代码/** 微信退款回调 */    @Override    @Transactional(rollbackFor = Exception.class)    public String wechatRefundCallback(HttpServletRequest request, HttpServletResponse response) {        log.info(“微信回调开始啦!!!!”);        try {            BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream()));            String line = null;            StringBuilder sb = new StringBuilder();            while ((line = br.readLine()) != null) {                sb.append(line);            }            br.close();            //sb为微信返回的xml            Map resultMap = PayUtil.doXMLParse(sb.toString());            if (“SUCCESS”.equals(resultMap.get(“return_code”))){//通信成功                String req_info = String.valueOf(resultMap.get(“req_info”));                String resultXml = AESUtil.decryptData(req_info,weChatConfig.key);                Map<String,Object> reqInfoMap = PayUtil.doXMLParse(resultXml);//code….                String resultStr = “<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>”;                return resultStr;            }        }catch (Exception e){            e.printStackTrace();        }        throw new RuntimeException(“微信回调失败”);    }2.解密方法AESUtilimport javax.crypto.Cipher;import javax.crypto.spec.SecretKeySpec;public class AESUtil {    /**  密钥算法 */    private static final String ALGORITHM = “AES”;    /**  加解密算法/工作模式/填充方式  */    private static final String ALGORITHM_MODE_PADDING = “AES/ECB/PKCS5Padding”;    /** AES加密  */    public static String encryptData(String data,String password) throws Exception {        // 创建密码器        Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);        SecretKeySpec key = new SecretKeySpec(MD5Utils.MD5(password).toLowerCase().getBytes(), ALGORITHM);        // 初始化        cipher.init(Cipher.ENCRYPT_MODE, key);        return Base64Util.encode(cipher.doFinal(data.getBytes()));    }    /** AES解密 */    public static String decryptData(String base64Data,String password) throws Exception {        Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);        SecretKeySpec key = new SecretKeySpec(MD5Utils.MD5(password).toLowerCase().getBytes(), ALGORITHM);        cipher.init(Cipher.DECRYPT_MODE, key);        byte[] decode = Base64Util.decode(base64Data);        byte[] doFinal = cipher.doFinal(decode);        return new String(doFinal,”utf-8″);    }}Base64Utilimport java.util.Base64;public class Base64Util {    /** 解码  */    public static byte[] decode(String encodedText){        final Base64.Decoder decoder = Base64.getDecoder();        return decoder.decode(encodedText);    }    /** 编码 */    public static String encode(byte[] data){        final Base64.Encoder encoder = Base64.getEncoder();        return encoder.encodeToString(data);    }}MD5Utils /** 普通MD5 */    public static String MD5(String input) {        MessageDigest md5 = null;        try {            md5 = MessageDigest.getInstance(“MD5”);        } catch (NoSuchAlgorithmException e) {            return “check jdk”;        } catch (Exception e) {            e.printStackTrace();            return “”;        }        char[] charArray = input.toCharArray();        byte[] byteArray = new byte[charArray.length];        for (int i = 0; i < charArray.length; i++) {            byteArray[i] = (byte) charArray[i];        }        byte[] md5Bytes = md5.digest(byteArray);        StringBuffer hexValue = new StringBuffer();        for (int i = 0; i < md5Bytes.length; i++) {            int val = ((int) md5Bytes[i]) & 0xff;            if (val < 16) {                hexValue.append(“0”);            }            hexValue.append(Integer.toHexString(val));        }        return hexValue.toString();    }ps:很多教程说要替换jre的两个jar包,我这里没照这一步,在AES解密的时候SecretKeySpec key = new SecretKeySpec(MD5Utils.MD5(password).toLowerCase().getBytes(), ALGORITHM);这步将商户key加密串转成小写了,这样就解决了”JAVA运行环境默认不允许256位密钥的AES加解密””的问题有啥不懂得,或者我贴的代码有啥缺漏的,可以留言参考:https://blog.csdn.net/zhangxing52077/article/details/80269999

赞(0) 打赏
未经允许不得转载:E企盈小程序开发-热线:4006-838-530 » 小程序微信退款(退款信息回调解密)
分享到: 更多 (0)
E企盈小程序直播营销卖货系统
E企盈直播平台营销卖货系统

评论 抢沙发

E企盈小程序开发

联系我们联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏