首页 > 编程 > Java > 正文

Java微信退款开发

2019-11-26 09:38:10
字体:
来源:转载
供稿:网友

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

微信支付接口中,涉及资金回滚的接口会使用到商户证书,包括退款、撤销接口。商家在申请微信支付成功后,可以按照以下路径下载:微信商户平台(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<String, String> 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<Object, Object> parameters = new TreeMap<Object, Object>();  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<String,String> xml2map(HttpServletRequest request) throws IOException, DocumentException {  Map<String,String> map = new HashMap<String, String>();  SAXReader reader = new SAXReader();  InputStream inputStream = request.getInputStream();  Document document = reader.read(inputStream);  Element root = document.getRootElement();  List<Element> 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<String, String> parseXml(String xml) throws Exception {  Map<String, String> map = new HashMap<String, String>();  Document document = DocumentHelper.parseText(xml);  Element root = document.getRootElement();  List<Element> 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("<![CDATA[");      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; }}

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

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表