目录

  • 一、准备工作
  • 二、代码编写
  • 1.将大部分常用的数据封装在WechatConstant中
  • 2.controller
  • 3.service



一、准备工作

要实现退款,需要准备很多东西。
小程序ID(APPID)
商户号(mch_id)
随机字符串(nonce_str)
微信订单号(transaction_id)
商户退款单号(out_refund_no)
订单金额(total_fee)
退款金额(refund_fee)
签名(sign)通过商户证书密钥生成
商户证书:是从微信商户平台-》账户设置-》API安全 中下载的

Tip:其中的微信的订单号、订单金额都在订单里,有提到支付成功后如何实现回调,如何存支付订单。


二、代码编写

1.将大部分常用的数据封装在WechatConstant中

Tip:这个WechatConstant在下边的代码中会用到,而且以后修改信息可直接在这里修改,别处的代码就不用动了

public class WechatConstant {
    public final static String MCH_ID = "****************"; //商户号
    public final static String MCH_KEY = "***************"; //商户密钥
    public final static String APPID = "*****************"; //appid
    public final static String SECRET = "****************"; //小程序密钥
    public final static String KEY = "*******************"; //商户证书密钥
}

2.controller

Tip:这个controller主要是构建参数的,将那些APPID、商户号等东西放进map里,在后边的service中要转成xml格式

@ApiOperation("退款")
    @RequestMapping(value = "/refund", method = {RequestMethod.GET})
    public Res refund(HttpServletRequest request,
                       @RequestParam("openid") String openid,
                       @RequestParam("activityid")int activityid,
                      @RequestParam("memberid") int memberid) throws Exception {
        //先将订单信息查出来  这里的订单就是上边说的支付订单,我的是通过openid和一个activityid查出来的,自己可根据业务逻辑修改。
        QueryWrapper<Wxpaynotifyvo> wxPayNotifyVOQueryWrapper = new QueryWrapper<>();
        wxPayNotifyVOQueryWrapper.eq("openid", openid);
        wxPayNotifyVOQueryWrapper.eq("activityid",activityid);
        Wxpaynotifyvo wxPayNotifyVO = this.wxPayService.getBaseMapper().selectOne(wxPayNotifyVOQueryWrapper);
        //构建参数
        Map<String, String> dataMap = new HashMap<>();
        //小程序ID
        dataMap.put("appid", WechatConstant.APPID);
        //商户号
        dataMap.put("mch_id", WechatConstant.MCH_ID);
        //随机字符串
        dataMap.put("nonce_str", UUID.randomUUID().toString().replaceAll("-", ""));
        //微信订单号
        dataMap.put("transaction_id", wxPayNotifyVO.getTransactionid());
        //商户退款单号
        Date now = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");//可以方便地修改日期格式
        String outRefundNo = "NO" + dateFormat.format(now);
        dataMap.put("out_refund_no", outRefundNo);
        //订单金额
        dataMap.put("total_fee", wxPayNotifyVO.getTotalfee());

        //退款金额
        dataMap.put("refund_fee", wxPayNotifyVO.getTotalfee());
        //商户证书密钥,生成签名
        String sign = WXPayUtil.generateSignature(dataMap, WechatConstant.KEY);
        dataMap.put("sign", sign);
        //这个是将报名表里的数据删除
        int i = applymanService.deleteApplyman(openid, activityid, memberid);
        //这个不是
        this.wxPayService.refound(dataMap);
        Res res = new Res();
        res.setCode(0);
        res.setMsg("退款成功!");
        return res;
    }

3.service

Tip:在这里真正实现退款,里边要用到一个商户证书,上边提到过,需要在微信商户平台下载的,大概就长这个样子:

java商城订单过期用什么技术 java订单退款怎么实现_java

/**
     * 退款
     *
     * @param dataMap
     */
    @Override
    public void refound(Map<String, String> dataMap) {
        try {
            //将传来的map转xml
            StringBuffer sb = new StringBuffer();
            sb.append("<xml>");
            for (String key : dataMap.keySet()) {
                String value = "<![CDATA[" + dataMap.get(key) + "]]>";
                sb.append("<" + key + ">" + value + "</" + key + ">");
                System.out.println();
            }
            sb.append("</xml>");
            String xmlString = sb.toString();
            doRefoud(dataMap.get("mch_id"), "https://api.mch.weixin.qq.com/secapi/pay/refund", xmlString);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //退款之后将wxpaynotifyvo中的数据删除,防止下次报名活动时与上次数据重复
        QueryWrapper<Wxpaynotifyvo> wxpaynotifyvoQueryWrapper = new QueryWrapper<>();
        wxpaynotifyvoQueryWrapper.eq("transactionid",dataMap.get("transaction_id"));
        int delete = this.baseMapper.delete(wxpaynotifyvoQueryWrapper);

    }

    private static String doRefoud(String mch_id, String url, String xmlString) throws Exception {

        //证书 是从微信商户平台-》账户设置-》 API安全 中下载的
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        //我想的是把证书传到服务器上,没写出来
        //FileInputStream inputStream = new FileInputStream(new File("src\\main\\resources\\apiclient_cert.p12"));
        //据说这样写打包后也可以找到,就放在resources里了
        ClassPathResource classPathResource = new ClassPathResource("apiclient_cert.p12");
        InputStream inputStream = classPathResource.getInputStream();
        try {
            //这里写密码,默认是MCHID
            keyStore.load(inputStream, WechatConstant.MCH_ID.toCharArray());
        } finally {
            inputStream.close();
        }                                                           //这里写密码
        SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, WechatConstant.MCH_ID.toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

        try {
            //请求路径
            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(new StringEntity(xmlString,"UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            try {
                HttpEntity entity = response.getEntity();
                //接受到返回信息
                String jsonStr = EntityUtils.toString(response.getEntity(),"UTF-8");
                EntityUtils.consume(entity);
            } finally {
                response.close();
            }
        } finally {
            httpClient.close();
        }
        return null;
    }

Tip:退款之前先要实现支付,因为支付订单里边的参数是必须的,本来支付退款应该写在一起,但是东西太多所以分开了。这篇小程序退款是和前边写的微信支付是一个项目里的,可以结合着一起看比较完整。
欢迎批评纠正 ~ _ ~