微信支付退款功能开发

第一次做微信的退款处理,特此标记一下

官方API地址:https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_4&index=4

准备

  1. appId 微信分配的公众账号ID(企业号corpid即为此appId)
  2. mch_id 微信支付分配的商户号
  3. pay_secret 商户平台设置的秘钥:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
  4. 官方文档明确指出,退款还需使用到证书,至于证书怎么用后面说明

证书分为PHP版的和其他语言版的,这边以Java为例


下载证书放置服务器的文件目录,windows可直接导入


开发

  1. 请求实体对象

package com.macro.mall.portal.domain;

import lombok.Data;

@Data
public class WechatReturnRequestParam {

    private String appId;//公众账号ID
    private String mchId;//商户号
    private String nonceStr;//随机字符串
    private String sign;//签名
    private String signType = "MD5";//签名类型
    private String transactionId;//微信支付返回的订单号
    private String outTradeNo;//商户订单号
    private String outRefundNo;//退款单号
    private Integer totalFee;//订单金额
    private Integer refundFee;//退款金额
    private String refundFeeType = "CNY";//货币类型
    private String refundDesc;//退款原因
    private String notifyUrl;//通知地址
    private String mchSecret;

}


private static SortedMap<Object, Object> buildParamter(WechatReturnRequestParam requstParam) {
        SortedMap<Object, Object> parameters = new TreeMap();
        parameters.put("appid", requstParam.getAppId());
        parameters.put("mch_id", requstParam.getMchId());
        parameters.put("nonce_str", getNonceStr());
        parameters.put("transaction_id", requstParam.getTransactionId());
        parameters.put("out_refund_no", requstParam.getOutRefundNo());
        parameters.put("total_fee", requstParam.getTotalFee() + "");
        parameters.put("refund_fee", requstParam.getRefundFee() + "");
        if (!StringUtils.isEmpty(requstParam.getRefundDesc())){
            parameters.put("refund_desc", requstParam.getRefundDesc());
        }
        String sign = createSign("UTF-8", parameters, requstParam.getMchSecret());
        parameters.put("sign", sign);
        return parameters;
    }
  1. 拼接参数并返回XML字符串
	//1
	String requestXML = WechatPayUtil.buildReturnXML(requstParam);
	//2
	public static String buildRequestXML(WechatPayRequestParam param) {
        SortedMap<Object, Object> parameters = buildParamter(param);
        String requestXML = getRequestXml(parameters);
        return requestXML;
	}
	//3
	public static SortedMap<Object, Object> buildParamter(WechatPayRequestParam param) {
        if (param.getTotalFee() == null) {
            param.setTotalFee(1);
        }
        SortedMap<Object, Object> parameters = new TreeMap();
        parameters.put("appid", param.getAppId());
        parameters.put("mch_id", param.getMchId());
        parameters.put("nonce_str", getNonceStr());
        parameters.put("body", param.getBody());
        parameters.put("out_trade_no", param.getOutTradeNo());
        parameters.put("total_fee", param.getTotalFee() + "");
        parameters.put("spbill_create_ip", param.getSpbillCreateIp());
        parameters.put("notify_url", param.getNotifyUrl());
        parameters.put("trade_type", param.getTradeType());
        parameters.put("scene_info", param.getSceneInfo());
        String sign = createSign("UTF-8", parameters, param.getMchSecret());
        parameters.put("sign", sign);
        return parameters;
    	}
	//4.
	public static String createSign(String characterEncoding, SortedMap<Object, Object> parameters, String mchSecret) {
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();
        Iterator it = es.iterator();

        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) 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=" + mchSecret);
        String sign = md5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    	}
	//5
	public static String getRequestXml(SortedMap<Object, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set es = parameters.entrySet();
        Iterator it = es.iterator();

        while (true) {
            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 + ">" + v + "</" + k + ">");
                } else {
                    sb.append("<" + k + "><![CDATA[" + v + "]]></" + k + ">");
                }
            }

            sb.append("</xml>");
            return sb.toString();
        }
    }
  1. 创建请求
String responseString = WechatPayUtil.preReturnRequest(requestXML, config);
public static String preReturnRequest(String requestXML, WechatConfig config) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
		//config.getCertUrl() 是你存放证书的路径
        FileInputStream instream = new FileInputStream(new File(config.getCertUrl()));
        try {
		//config.getMchId() 商户mch_id
            keyStore.load(instream, config.getMchId().toCharArray());
        } finally {
            instream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, config.getMchId().toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        String responseString = "";
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        CloseableHttpResponse httpResponse;
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
        httpPost.setEntity(new StringEntity(requestXML, "text/xml", "UTF-8"));
        httpResponse = httpClient.execute(httpPost);
        HttpEntity httpEntity = httpResponse.getEntity();
        if (httpEntity != null) {
            InputStream is = httpEntity.getContent();
            BufferedReader br = new BufferedReader(new InputStreamReader(is, Consts.UTF_8));
            for (String line; (line = br.readLine()) != null; responseString = responseString + line) ;
            br.close();
            is.close();
        }
        return responseString;
    }
  1. 获取请求结果
WechatReturnRequestParam requstParam = getWechatReturnRequestParam(order, config);
        String requestXML = WechatPayUtil.buildReturnXML(requstParam);
        try {
            log.info("发起微信退款的请求参数:{}", requestXML);
            String responseString = WechatPayUtil.preReturnRequest(requestXML, config);
            log.info("微信退款返回的参数:{}", responseString);
            JSONObject result = WechatPayUtil.doXMLParse(responseString);
            if (result == null) {
                throw new RuntimeException("微信退款返回xml解析失败");
            }
            if (!"SUCCESS".equals(result.getString("return_code"))) {
                throw new RuntimeException(result.getString("return_msg"));
            }
            if (!"SUCCESS".equals(result.getString("result_code"))) {
                throw new RuntimeException(result.getString("err_code_des"));
            }
            log.info("订单{}已从微信退款,", order.getOrderSn());
        } catch (Exception e) {
            log.error("发起微信退款失败:错误异常:{}", e);
        }

后记:我在请求参数的时候并没有传入notify_url通知地址这个参数,需要的童鞋请自行添加。

转载于:https://my.oschina.net/WessonChen/blog/3022507

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值