java微信退款接口demo_Java微信退款开发

本文介绍了如何使用Java实现微信退款接口,包括下载并导入商户证书,以及编写退款接口代码。详细讲解了退款请求的参数初始化,如订单号、金额等,并展示了如何通过RefundRequest类调用微信接口进行退款操作。
摘要由CSDN通过智能技术生成

一、下载证书并导入到系统

微信支付接口中,涉及资金回滚的接口会使用到商户证书,包括退款、撤销接口。商家在申请微信支付成功后,可以按照以下路径下载:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->证书下载。

aefbf9fd72b84bbc71c4760fc939507a.png

541b8d19ecb107e5b950799877d1414c.png

下载的时候需要手机验证及登录密码。下载后找到apiclient_cert.p12这个证书,双击导入,导入的时候提示输入密码,这个密码就是商户ID,且必须是在自己的商户平台下载的证书。否则会出现密码错误的提示:

adf0847f38b32ec229947c5689249f1b.png

导入正确的提示:

927082f1328892b6f15a5e60ada43c87.png

二、编写代码

首先初始化退款接口中的请求参数,如微信订单号transaction_id(和商户订单号只需要知道一个)、订单金额total_fee等;其次调用MobiMessage中的RefundResData2xml方法解析成需要的类型;最后调用RefundRequest类的httpsRequest方法触发请求。

/**

* 处理退款请求

* @param request

* @return

* @throws Exception

*/

@RequestMapping("/refund")

@ResponseBody

public JsonApi refund(HttpServletRequest request) throws Exception {

//获得当前目录

String path = request.getSession().getServletContext().getRealPath("/");

LogUtils.trace(path);

Date now = new Date();

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");//可以方便地修改日期格式

String outRefundNo = "NO" + dateFormat.format( now );

//获得退款的传入参数

String transactionID = "4008202001201609012791655620";

String outTradeNo = "20160901141024";

Integer totalFee = 1;

Integer refundFee = totalFee;

RefundReqData refundReqData = new RefundReqData(transactionID,outTradeNo,outRefundNo,totalFee,refundFee);

String info = MobiMessage.RefundReqData2xml(refundReqData).replaceAll("__", "_");

LogUtils.trace(info);

try {

RefundRequest refundRequest = new RefundRequest();

String result = refundRequest.httpsRequest(WxConfigure.REFUND_API, info, path);

LogUtils.trace(result);

Map getMap = MobiMessage.parseXml(new String(result.toString().getBytes(), "utf-8"));

if("SUCCESS".equals(getMap.get("return_code")) && "SUCCESS".equals(getMap.get("return_msg"))){

return new JsonApi();

}else{

//返回错误描述

return new JsonApi(getMap.get("err_code_des"));

}

}catch(Exception e){

e.printStackTrace();

return new JsonApi();

}

}

初始化退款接口需要的数据,隐藏了get和set方法。

public class RefundReqData {

//每个字段具体的意思请查看API文档

private String appid = "";

private String mch_id = "";

private String nonce_str = "";

private String sign = "";

private String transaction_id = "";

private String out_trade_no = "";

private String out_refund_no = "";

private int total_fee = 0;

private int refund_fee = 0;

private String op_user_id = "";

/**

* 请求退款服务

* @param transactionID 是微信系统为每一笔支付交易分配的订单号,通过这个订单号可以标识这笔交易,它由支付订单API支付成功时返回的数据里面获取到。建议优先使用

* @param outTradeNo 商户系统内部的订单号,transaction_id 、out_trade_no 二选一,如果同时存在优先级:transaction_id>out_trade_no

* @param outRefundNo 商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔

* @param totalFee 订单总金额,单位为分

* @param refundFee 退款总金额,单位为分

*/

public RefundReqData(String transactionID,String outTradeNo,String outRefundNo,int totalFee,int refundFee){

//微信分配的公众号ID(开通公众号之后可以获取到)

setAppid(WxConfigure.AppId);

//微信支付分配的商户号ID(开通公众号的微信支付功能之后可以获取到)

setMch_id(WxConfigure.Mch_id);

//transaction_id是微信系统为每一笔支付交易分配的订单号,通过这个订单号可以标识这笔交易,它由支付订单API支付成功时返回的数据里面获取到。

setTransaction_id(transactionID);

//商户系统自己生成的唯一的订单号

setOut_trade_no(outTradeNo);

setOut_refund_no(outRefundNo);

setTotal_fee(totalFee);

setRefund_fee(refundFee);

setOp_user_id(WxConfigure.Mch_id);

//随机字符串,不长于32 位

setNonce_str(StringUtil.generateRandomString(16));

//根据API给的签名规则进行签名

SortedMap parameters = new TreeMap();

parameters.put("appid", appid);

parameters.put("mch_id", mch_id);

parameters.put("nonce_str", nonce_str);

parameters.put("transaction_id", transaction_id);

parameters.put("out_trade_no", out_trade_no);

parameters.put("out_refund_no", out_refund_no);

parameters.put("total_fee", total_fee);

parameters.put("refund_fee", refund_fee);

parameters.put("op_user_id", op_user_id);

String sign = DictionarySort.createSign(parameters);

setSign(sign); //把签名数据设置到Sign这个属性中

}

MobiMessage实现json数据类型和xml数据之间的转换。

public class MobiMessage {

public static Map xml2map(HttpServletRequest request) throws IOException, DocumentException {

Map map = new HashMap();

SAXReader reader = new SAXReader();

InputStream inputStream = request.getInputStream();

Document document = reader.read(inputStream);

Element root = document.getRootElement();

List list = root.elements();

for(Element e:list){

map.put(e.getName(), e.getText());

}

inputStream.close();

return map;

}

//订单转换成xml

public static String JsApiReqData2xml(JsApiReqData jsApiReqData){

/*XStream xStream = new XStream();

xStream.alias("xml",productInfo.getClass());

return xStream.toXML(productInfo);*/

MobiMessage.xstream.alias("xml",jsApiReqData.getClass());

return MobiMessage.xstream.toXML(jsApiReqData);

}

public static String RefundReqData2xml(RefundReqData refundReqData){

/*XStream xStream = new XStream();

xStream.alias("xml",productInfo.getClass());

return xStream.toXML(productInfo);*/

MobiMessage.xstream.alias("xml",refundReqData.getClass());

return MobiMessage.xstream.toXML(refundReqData);

}

public static String class2xml(Object object){

return "";

}

public static Map parseXml(String xml) throws Exception {

Map map = new HashMap();

Document document = DocumentHelper.parseText(xml);

Element root = document.getRootElement();

List elementList = root.elements();

for (Element e : elementList)

map.put(e.getName(), e.getText());

return map;

}

//扩展xstream,使其支持CDATA块

private static XStream xstream = new XStream(new XppDriver() {

public HierarchicalStreamWriter createWriter(Writer out) {

return new PrettyPrintWriter(out) {

// 对所有xml节点的转换都增加CDATA标记

boolean cdata = true;

//@SuppressWarnings("unchecked")

public void startNode(String name, Class clazz) {

super.startNode(name, clazz);

}

protected void writeText(QuickWriter writer, String text) {

if (cdata) {

writer.write("

writer.write(text);

writer.write("]]>");

} else {

writer.write(text);

}

}

};

}

});

}

RefundRequest类中initCert方法加载证书到系统中,其中证书地址如下:

public static String certLocalPath = "/WEB-INF/cert/apiclient_cert.p12";

RefundRequest类中httpsRequest方法调用微信接口,触发请求。

/**

* User: rizenguo

* Date: 2014/10/29

* Time: 14:36

*/

public class RefundRequest {

//连接超时时间,默认10秒

private int socketTimeout = 10000;

//传输超时时间,默认30秒

private int connectTimeout = 30000;

//请求器的配置

private RequestConfig requestConfig;

//HTTP请求器

private CloseableHttpClient httpClient;

/**

* 加载证书

* @param path

* @throws IOException

* @throws KeyStoreException

* @throws UnrecoverableKeyException

* @throws NoSuchAlgorithmException

* @throws KeyManagementException

*/

private void initCert(String path) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {

//拼接证书的路径

path = path + WxConfigure.certLocalPath;

KeyStore keyStore = KeyStore.getInstance("PKCS12");

//加载本地的证书进行https加密传输

FileInputStream instream = new FileInputStream(new File(path));

try {

keyStore.load(instream, WxConfigure.Mch_id.toCharArray()); //加载证书密码,默认为商户ID

} catch (CertificateException e) {

e.printStackTrace();

} catch (NoSuchAlgorithmException e) {

e.printStackTrace();

} finally {

instream.close();

}

// Trust own CA and all self-signed certs

SSLContext sslcontext = SSLContexts.custom()

.loadKeyMaterial(keyStore, WxConfigure.Mch_id.toCharArray()) //加载证书密码,默认为商户ID

.build();

// Allow TLSv1 protocol only

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(

sslcontext,

new String[]{"TLSv1"},

null,

SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

httpClient = HttpClients.custom()

.setSSLSocketFactory(sslsf)

.build();

//根据默认超时限制初始化requestConfig

requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();

}

/**

* 通过Https往API post xml数据

* @param url API地址

* @param xmlObj 要提交的XML数据对象

* @param path 当前目录,用于加载证书

* @return

* @throws IOException

* @throws KeyStoreException

* @throws UnrecoverableKeyException

* @throws NoSuchAlgorithmException

* @throws KeyManagementException

*/

public String httpsRequest(String url, String xmlObj, String path) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {

//加载证书

initCert(path);

String result = null;

HttpPost httpPost = new HttpPost(url);

//得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别

StringEntity postEntity = new StringEntity(xmlObj, "UTF-8");

httpPost.addHeader("Content-Type", "text/xml");

httpPost.setEntity(postEntity);

//设置请求器的配置

httpPost.setConfig(requestConfig);

try {

HttpResponse response = httpClient.execute(httpPost);

HttpEntity entity = response.getEntity();

result = EntityUtils.toString(entity, "UTF-8");

} catch (ConnectionPoolTimeoutException e) {

LogUtils.trace("http get throw ConnectionPoolTimeoutException(wait time out)");

} catch (ConnectTimeoutException e) {

LogUtils.trace("http get throw ConnectTimeoutException");

} catch (SocketTimeoutException e) {

LogUtils.trace("http get throw SocketTimeoutException");

} catch (Exception e) {

LogUtils.trace("http get throw Exception");

} finally {

httpPost.abort();

}

return result;

}

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值