项目中用到微信退款功能,简述一下
打开微信支付开发文档!api列表申请退款,注意:退款需要双向证书
下载API证书,也可以按照以下路径下载:微信商户平台(pay.weixin.qq.com)-->账户中心-->账户设置-->API安全-->证书下载
有四个文件分被是:apiclient_cert.p12;apiclient_cert.pem;apiclient_key.pem;rootca.pem java开发的只用到p12证书!将该证书复制到linux系统下某个目录下(或者windowns系统某个目录下)然后就可以开发了
直接附上代码
/**
*
* @Description (微信退款)
* @param order
* @return
*/
public static Map<String,Object> refundOrder(Order order){
Map<String,Object> retMap = new HashMap<String,Object>();
try{
//将金额单位转换成分
int amount = 1;
if(StringUtils.isNotEmpty(String.valueOf(order.getPayMoney()))){
amount = (int) Math.round(Double.parseDouble(String.valueOf(order.getPayMoney()))*100);
}
//获得随机字符串
String nonce_str = StringUtil.createNonceStr();
//组装签名字符串
String signStr = "appid=" + WxPayConfig.APP_ID
+"&mch_id=" + WxPayConfig.APP_MCH_ID
+"&nonce_str=" + nonce_str
+"&out_refund_no=" + order.getOrderCode()
+"&out_trade_no=" + order.getOrderId()
+"&refund_fee=" + amount
+"&total_fee=" + amount
+"&key="+ WxPayConfig.APP_MCH_APPSECRET;
System.out.println(signStr);
//签名最后全部转为大写
String sign = Md5Util.MD5Encode(signStr).toUpperCase();
//拼接下单xml
StringBuffer xml = new StringBuffer();
xml.append("<xml>");
xml.append("<appid>").append(WxPayConfig.APP_ID).append("</appid>");
xml.append("<mch_id>").append(WxPayConfig.APP_MCH_ID).append("</mch_id>");
xml.append("<nonce_str>").append(nonce_str).append("</nonce_str>");
xml.append("<out_refund_no>").append(order.getOrderCode()).append("</out_refund_no>");
xml.append("<out_trade_no>").append(order.getOrderId()).append("</out_trade_no>");
xml.append("<refund_fee>").append(amount).append("</refund_fee>");
xml.append("<total_fee>").append(amount).append("</total_fee>");
xml.append("<sign>").append(sign).append("</sign>");
xml.append("</xml>");
//请求微信下单接口
String retXmlStr =new HttpsRequest().sendPost(WxPayConfig.REFUND_ORDER_ADDRESS, xml.toString());
//解析返回数据
if(StringUtils.isNotEmpty(retXmlStr)){
retMap = XmlUtil.xmlStrParser(retXmlStr);
}
}catch(Exception ex){
ex.printStackTrace();
}
return retMap;
}
其中涉及到的工具类方法
/**
*
* @Title: createNonceStr
* @Description: 创建
* @return
* @throws
*/
public static String createNonceStr(){
String letter[] = {"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","S","Y","Z"};
String nonce_str="";
for(int i=0;i<26;i++){
int num = new Random().nextInt(26);
nonce_str+= letter[num];
}
return nonce_str;
}
/**
* MD5编码
* @param origin 原始字符串
* @return 经过MD5加密之后的结果
*/
public static String MD5Encode(String origin) {
String resultString = null;
try {
resultString = origin;
MessageDigest md = MessageDigest.getInstance("MD5");
resultString = byteArrayToHexString(md.digest(resultString.getBytes("UTF-8")));
} catch (Exception e) {
e.printStackTrace();
}
return resultString;
}
/**
* 将xml结构的字符串转换成map
*
* @param xmlStr
* @return
*/
public static Map<String,Object> xmlStrParser(String xmlStr) {
Map<String,Object> map = new HashMap<String,Object>();
// 创建一个新的字符串
StringReader read = new StringReader(xmlStr);
// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
InputSource source = new InputSource(read);
// 创建一个新的SAXBuilder
SAXBuilder sb = new SAXBuilder();
try {
// 通过输入源构造一个Document
Document doc = sb.build(source);
// 取的根元素
Element root = doc.getRootElement();
System.out.println(root.getName());// 输出根元素的名称(测试)
// 得到根元素所有子元素的集合
List jiedian = root.getChildren();
// 获得XML中的命名空间(XML中未定义可不写)
Namespace ns = root.getNamespace();
Element et = null;
for (int i = 0; i < jiedian.size(); i++) {
et = (Element) jiedian.get(i);// 循环依次得到子元素
// System.out.println("key/value==>" + et.getName() +"/"+
// et.getText());
map.put(et.getName(), et.getText());
}
// et = (Element) jiedian.get(0);
// List zjiedian = et.getChildren();
// for(int j=0;j<zjiedian.size();j++){
// Element xet = (Element) zjiedian.get(j);
// System.out.println(xet.getName());
// }
} catch (Exception ex) {
ex.printStackTrace();
}
return map;
}
加载证书以及发送请求类
package com.bluemobi.framework.utils.wxPay;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
public class HttpsRequest {
private static Logger logger = Logger.getLogger(HttpsRequest.class);
//表示请求器是否已经做了初始化工作
private boolean hasInit = false;
//连接超时时间,默认10秒
private int socketTimeout = 10000;
//传输超时时间,默认30秒
private int connectTimeout = 30000;
//请求器的配置
private RequestConfig requestConfig;
//HTTP请求器
private CloseableHttpClient httpClient;
/**
* 构造方法
*/
public HttpsRequest() throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, NoSuchProviderException {
init();
}
/***
* 初始化https(加载证书)
*/
private void init() throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException , NoSuchProviderException{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream instream = new FileInputStream(new File(WxPayConfig.CERT_LOCALPATH));//加载本地的证书进行https加密传输
try {
keyStore.load(instream, WxPayConfig.APP_MCH_ID.toCharArray());//设置证书密码
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, WxPayConfig.APP_MCH_ID.toCharArray()).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"},null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
hasInit = true;
}
/**
* 通过Https往API post xml数据(post)
*
* @param url API地址
* @param xmlObj 要提交的XML数据对象
* @return API回包的实际数据
*/
public String sendPost(String url, String xmlObj) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException, NoSuchProviderException {
if (!hasInit) {
init();
}
String result = null;
HttpPost httpPost = new HttpPost(url);
logger.info("API,POST过去的数据是:" + xmlObj);
//得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
StringEntity postEntity = new StringEntity(xmlObj, "UTF-8");
httpPost.addHeader("Content-Type", "text/xml");
httpPost.setEntity(postEntity);
//设置请求器的配置
httpPost.setConfig(requestConfig);
logger.info("执行请求:" + httpPost.getRequestLine());
try {
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
result = EntityUtils.toString(entity, "UTF-8");
} catch (ConnectionPoolTimeoutException e) {
logger.error("http get throw ConnectionPoolTimeoutException(wait time out)");
} catch (ConnectTimeoutException e) {
logger.error("http get throw ConnectTimeoutException");
} catch (SocketTimeoutException e) {
logger.error("http get throw SocketTimeoutException");
} catch (Exception e) {
logger.error("http get throw Exception");
} finally {
httpPost.abort();
}
return result;
}
/**
* 设置连接超时时间
*
* @param socketTimeouts 连接时长,默认10秒
*/
public void setSocketTimeout(int socketTimeouts) {
socketTimeout = socketTimeouts;
resetRequestConfig();
}
/**
* 设置传输超时时间
*
* @param connectTimeouts 传输时长,默认30秒
*/
public void setConnectTimeout(int connectTimeouts) {
connectTimeout = connectTimeouts;
resetRequestConfig();
}
private void resetRequestConfig() {
requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
}
/**
* 允许商户自己做更高级更复杂的请求器配置
*
* @param requestConfigs 设置HttpsRequest的请求器配置
*/
public void setRequestConfig(RequestConfig requestConfigs) {
requestConfig = requestConfigs;
}
}
其中涉及到的包在 使用商户证书-点击(请参考微信支付提供的demo外链)下载就会有相应的jar包;主要是:httpclient-4.3.4.jar;httpcore-4.3.2.jar