一、下载证书并导入到系统
微信支付接口中,涉及资金回滚的接口会使用到商户证书,包括退款、撤销接口。商家在申请微信支付成功后,可以按照以下路径下载:微信商户平台(pay.weixin.qq.com)–>账户设置–>API安全–>证书下载。
下载的时候需要手机验证及登录密码。下载后找到apiclient_cert.p12这个证书,双击导入,导入的时候提示输入密码,这个密码就是商户ID,且必须是在自己的商户平台下载的证书。否则会出现密码错误的提示:
导入正确的提示:
二、编写代码
首先初始化退款接口中的请求参数,如微信订单号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
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("");
} 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;
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助
为了学习工作与休闲娱乐互不冲突,现新建圈【码农茶水铺】用于程序员生活,爱好,交友,求职招聘,吐槽等话题交流,希望各位大神工作之余到茶水铺来喝茶聊天。群号:603619042
还有大量的面试题,视频资源共享,