[置顶] java微信退款unexpected end of file from server
2016-06-07 11:15
926 查看
首先抛出一个问题 unexpected end of file from server
相信很多人都会遇到这个问题,因为只要你做过微信支付然后再来做退款的时候就会习惯性地把支付的源码直接拿过来用,然后开始退款的时候就报错了~
问题的原因在于:支付的时候,你是把钱给微信,那么怎么简单怎么弄,现在你要拿出来,那得做点什么了~
支付不需要证书,退款需要双向证书!
ok,开始去商户平台下载证书java用这个apiclient_cert.p12就好了,他需要几个jar包~
httpclient-4.3.4.jar,httpcore-4.3.2.jar
https://yunpan.cn/cRfFTTUKIWx3x 访问密码 774e (根据自己的需要开始下载)
然后就是关键代码了,普通的可能就是直接打开Http地址就好了,带上证书,你得这么操作;
说明:1、修改你证书的地址(这里微信说需要安装一下,其实装不装无所谓,因为Linux上你可能就不知道怎么安装,直接放到一个具有管理员权限的位置,并且不能呗下载到,避免被恶意操作)
2、修改两处密码,填你们公司的mch_id
调用的代码:
说明:之前做支付的时候是这么调用的:String weixinPost = HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo).toString();
现在换成String weixinPost = ClientCustomSSL.doRefund(wxUrl, xmlInfo).toString();
到这里差不多就能正常退款了!
然后新问题来了:一个订单1元一个,同时购买7个,退款的时候退5元,我擦
又报错了:同一个out_refund_no退款金额要一致
那是因为out_refund_no这个参数我用的是商品的id,因为如果你不做处理,微信认为你还可能下一次再来退款,那么已经用当前的id退款,再来的话,微信可能也不好处理,这个时候追加一个随机数就好了~相比支付的时候,不需要回调这里就简单多了~
上几张图片吧:
源码:
核心类:WeixinPay
============================无私的世界,啥都共享==================================================
老司机开车完毕~~
说明:其它的方法,签名什么的我之前已经提供过了,这里就不重复了~
good luck
来了:
到这里差不多就能正常退款了!
相信很多人都会遇到这个问题,因为只要你做过微信支付然后再来做退款的时候就会习惯性地把支付的源码直接拿过来用,然后开始退款的时候就报错了~
问题的原因在于:支付的时候,你是把钱给微信,那么怎么简单怎么弄,现在你要拿出来,那得做点什么了~
支付不需要证书,退款需要双向证书!
ok,开始去商户平台下载证书java用这个apiclient_cert.p12就好了,他需要几个jar包~
httpclient-4.3.4.jar,httpcore-4.3.2.jar
https://yunpan.cn/cRfFTTUKIWx3x 访问密码 774e (根据自己的需要开始下载)
然后就是关键代码了,普通的可能就是直接打开Http地址就好了,带上证书,你得这么操作;
/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package com.base; import java.io.File; import java.io.FileInputStream; import java.security.KeyStore; import javax.net.ssl.SSLContext; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; 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 codeGenerate.util.ConfProperties; /** * This example demonstrates how to create secure connections with a custom SSL * context. */ public class ClientCustomSSL { public static String doRefund(String url,String data) throws Exception { /** * 注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的 */ KeyStore keyStore = KeyStore.getInstance("PKCS12"); FileInputStream instream = new FileInputStream(new File(ConfProperties.getSSLPath()));//P12文件目录 try { /** * 此处要改 * */ keyStore.load(instream, "你的MCHID".toCharArray());//这里写密码..默认是你的MCHID } finally { instream.close(); } // Trust own CA and all self-signed certs /** * 此处要改 * */ SSLContext sslcontext = SSLContexts.custom() .loadKeyMaterial(keyStore, "你的MCHID".toCharArray())//这里也是写密码的 .build(); // Allow TLSv1 protocol only SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); CloseableHttpClient httpclient = HttpClients.custom() .setSSLSocketFactory(sslsf) .build(); try { HttpPost httpost = new HttpPost(url); // 设置响应头信息 httpost.addHeader("Connection", "keep-alive"); httpost.addHeader("Accept", "*/*"); httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); httpost.addHeader("Host", "api.mch.weixin.qq.com"); httpost.addHeader("X-Requested-With", "XMLHttpRequest"); httpost.addHeader("Cache-Control", "max-age=0"); httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "); httpost.setEntity(new StringEntity(data, "UTF-8")); CloseableHttpResponse response = httpclient.execute(httpost); try { HttpEntity entity = response.getEntity(); String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8"); EntityUtils.consume(entity); return jsonStr; } finally { response.close(); } } finally { httpclient.close(); } } }
说明:1、修改你证书的地址(这里微信说需要安装一下,其实装不装无所谓,因为Linux上你可能就不知道怎么安装,直接放到一个具有管理员权限的位置,并且不能呗下载到,避免被恶意操作)
2、修改两处密码,填你们公司的mch_id
调用的代码:
//构造xml参数 String xmlInfo = WeixinPay.xmlInfo(refund); String wxUrl = WeixinPay.getRefundURL; String weixinPost = ClientCustomSSL.doRefund(wxUrl, xmlInfo).toString(); System.out.println(weixinPost); WXPayRefundResult refundResult = WeixinPay.getUnifiedorderResult(weixinPost); if("SUCCESS".equalsIgnoreCase(refundResult.getResult_code())){ result = "200"; System.out.println("==========处理退款成功=========="); }else{ result = refundResult.getReturn_msg(); }
说明:之前做支付的时候是这么调用的:String weixinPost = HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo).toString();
现在换成String weixinPost = ClientCustomSSL.doRefund(wxUrl, xmlInfo).toString();
到这里差不多就能正常退款了!
然后新问题来了:一个订单1元一个,同时购买7个,退款的时候退5元,我擦
又报错了:同一个out_refund_no退款金额要一致
那是因为out_refund_no这个参数我用的是商品的id,因为如果你不做处理,微信认为你还可能下一次再来退款,那么已经用当前的id退款,再来的话,微信可能也不好处理,这个时候追加一个随机数就好了~相比支付的时候,不需要回调这里就简单多了~
String out_refund_no = applyReturn.getItemId()+CommonUtils.getRandByNum(6);
/** * 产生num位的随机数 * @return */ public static String getRandByNum(int num){ String length = "1"; for(int i=0;i<num;i++){ length += "0"; } Random rad=new Random(); String result = rad.nextInt(Integer.parseInt(length)) +""; if(result.length()!=num){ return getRandByNum(num); } return result; }
上几张图片吧:
源码:
//微信
if(trans.getListSplitTrans()!=null&&trans.getListSplitTrans().size()>0){
Item item = this.itemService.findItemById(applyReturn.getItemId());
if(item!=null){
//这里是下单后并未直接支付,而是后面拆单后一个一个地支付
for (SplitTrans split : trans.getListSplitTrans()) {
if(split.getSellerId().equals(item.getUserId())){
if(split.getPm()==applyReturn.getPm()){
outCode = split.getOutCode();
finalPay = split.getFinalpay().intValue();
}else{
result = "提现中的支付方式跟交易流水中的支付方式不一致!!";
}
break;
}
}
}else{
result = "退款中的商品不存在~";
}
}else if(!StringUtils.isEmpty(trans.getOutCode())){
if(trans.getPm()==applyReturn.getPm()){
outCode = trans.getOutCode();
finalPay = trans.getFinalpay().intValue();
}else{
result = "提现中的支付方式跟交易流水中的支付方式不一致!!";
}
}
if(StringUtils.isEmpty(result)){
String appid = WeixinPay.appid;
String mch_id = WeixinPay.mch_id;
String nonce_str = WeixinPay.getRandomString(32);
String transaction_id = outCode;
String out_refund_no = applyReturn.getItemId()+CommonUtils.getRandByNum(6);
int total_fee = finalPay*100;//单位为分
int refund_fee = (int)applyReturn.getMoney()*100;
String op_user_id = WeixinPay.mch_id;//操作员帐号, 默认为商户号
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_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 = WeixinPay.createSign("UTF-8", parameters);
System.out.println("签名是:"+sign);
WXPayRefund refund = new WXPayRefund();
refund.setAppid(appid);
refund.setMch_id(mch_id);
refund.setNonce_str(nonce_str);
refund.setOp_user_id(op_user_id);
refund.setOut_refund_no(out_refund_no);
refund.setRefund_fee(refund_fee);
refund.setSign(sign);
refund.setTotal_fee(total_fee);
refund.setTransaction_id(transaction_id);
//构造xml参数 String xmlInfo = WeixinPay.xmlInfo(refund); String wxUrl = WeixinPay.getRefundURL; String weixinPost = ClientCustomSSL.doRefund(wxUrl, xmlInfo).toString(); System.out.println(weixinPost); WXPayRefundResult refundResult = WeixinPay.getUnifiedorderResult(weixinPost); if("SUCCESS".equalsIgnoreCase(refundResult.getResult_code())){ result = "200"; System.out.println("==========处理退款成功=========="); }else{ result = refundResult.getReturn_msg(); }
}
核心类:WeixinPay
package com.base; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.StringReader; import java.net.URL; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.SortedMap; import javax.net.ssl.HttpsURLConnection; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.xml.sax.InputSource; import codeGenerate.util.MD5Util; import com.base.entity.WXPayRefund; import com.base.entity.WXPayRefundResult; /** * 商户微信支付配置 * @author iYjrg_xiebin * @date 2016年5月24日下午2:52:25 */ public class WeixinPay { //http://mch.weixin.qq.com/wiki/doc/api/index.php?chapter=4_3 //商户Key:改成公司申请的即可 //32位密码设置地址:http://www.sexauth.com/ 12341hvufnm1sdcb0e81t36k0d0f15nc private static String Key = "32位私钥";//改成你们的私钥(密码) /** * 配置参数 */ public static String appid = "你们的appId"; public static String mch_id = "你们的mch_id"; public static String getRefundURL = "https://api.mch.weixin.qq.com/secapi/pay/refund"; /** * 随即字符串 * @param length * @return */ public static String getRandomString(int length) { //length表示生成字符串的长度 String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; Random random = new Random(); StringBuffer sb = new StringBuffer(); int number = 0; for (int i = 0; i < length; i++) { number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); } /** * 微信支付签名算法sign * @param characterEncoding * @param parameters * @return */ @SuppressWarnings("rawtypes") public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){ StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序) 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=" + Key); System.out.println("字符串拼接后是:"+sb.toString()); String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; } /** * 开始退款 * 构造xml参数 * @param xml * @return */ public static String xmlInfo(WXPayRefund refund){ //构造xml参数的时候,至少又是个必传参数 /* * <xml> <appid>wx2421b1c4370ec43b</appid> <mch_id>10000100</mch_id> <nonce_str>6cefdb308e1e2e8aabd48cf79e546a02</nonce_str> <op_user_id>10000100</op_user_id> <out_refund_no>1415701182</out_refund_no> <out_trade_no>1415757673</out_trade_no> <refund_fee>1</refund_fee> <total_fee>1</total_fee> <transaction_id></transaction_id> <sign>FE56DD4AA85C0EECA82C35595A69E153</sign> </xml> */ if(refund!=null){ StringBuffer bf = new StringBuffer(); bf.append("<xml>"); bf.append("<appid><![CDATA["); bf.append(refund.getAppid()); bf.append("]]></appid>"); bf.append("<mch_id><![CDATA["); bf.append(refund.getMch_id()); bf.append("]]></mch_id>"); bf.append("<nonce_str><![CDATA["); bf.append(refund.getNonce_str()); bf.append("]]></nonce_str>"); bf.append("<sign><![CDATA["); bf.append(refund.getSign()); bf.append("]]></sign>"); bf.append("<op_user_id><![CDATA["); bf.append(refund.getOp_user_id()); bf.append("]]></op_user_id>"); bf.append("<transaction_id><![CDATA["); bf.append(refund.getTransaction_id()); bf.append("]]></transaction_id>"); bf.append("<out_refund_no><![CDATA["); bf.append(refund.getOut_refund_no()); bf.append("]]></out_refund_no>"); bf.append("<total_fee><![CDATA["); bf.append(refund.getTotal_fee()); bf.append("]]></total_fee>"); bf.append("<refund_fee><![CDATA["); bf.append(refund.getRefund_fee()); bf.append("]]></refund_fee>"); bf.append("</xml>"); return bf.toString(); } return ""; } /** * post请求并得到返回结果 * @param requestUrl * @param requestMethod * @param output * @return */ public static String httpsRequest(String requestUrl, String requestMethod, String output) { try{ URL url = new URL(requestUrl); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); connection.setRequestMethod(requestMethod); if (null != output) { OutputStream outputStream = connection.getOutputStream(); outputStream.write(output.getBytes("UTF-8")); outputStream.close(); } // 从输入流读取返回内容 InputStream inputStream = connection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; connection.disconnect(); return buffer.toString(); }catch(Exception ex){ ex.printStackTrace(); } return ""; } /** * 解析退款申请 * 解析的时候自动去掉CDMA * @param xml */ @SuppressWarnings("unchecked") public static WXPayRefundResult getUnifiedorderResult(String xml){ WXPayRefundResult unifieorderResult = new WXPayRefundResult(); try { StringReader read = new StringReader(xml); // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入 InputSource source = new InputSource(read); // 创建一个新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通过输入源构造一个Document Document doc; doc = (Document) sb.build(source); Element root = doc.getRootElement();// 指向根节点 List<Element> list = root.getChildren(); if(list!=null&&list.size()>0){ for (Element element : list) { System.out.println("key是:"+element.getName()+",值是:"+element.getText()); if("return_code".equals(element.getName())){ unifieorderResult.setReturn_code(element.getText()); } if("return_msg".equals(element.getName())){ unifieorderResult.setReturn_msg(element.getText()); } if("result_code".equals(element.getName())){ unifieorderResult.setResult_code(element.getText()); } } } } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }catch (Exception e) { e.printStackTrace(); } return unifieorderResult; } }
============================无私的世界,啥都共享==================================================
老司机开车完毕~~
说明:其它的方法,签名什么的我之前已经提供过了,这里就不重复了~
good luck
来了:
到这里差不多就能正常退款了!
相关文章推荐
- 微信公众号开发获取用户信息
- 通过微信网页授权获取用户OpenId(redirect_uri 参数错误)
- 迁移项目之后出现微信提现退款curl出错代码58
- 订餐系统之微信点餐
- 微信开发:网页授权获取用户的基本信息
- 一个想得太多,一个做得太少,微博、微信都面临哪些挑战?
- 微信分享不成功原因与解决方法
- 微信分享出去的图片和简介
- 微信扫码支付 php代码
- 网页微信配合谷歌浏览器实现自动发送信息
- Android小程序——乐学成语实现(三)
- Android小程序——乐学成语实现(二)
- (4.2.29) Android开发学习之基于ZBar实现微信扫一扫
- Android小程序——乐学成语实现(一)
- 微信支付将向境外商户全面开放 支持九种外币
- 微信支付时出现[交易已提交,请查询确认是否已扣款,避免重复操作]
- [置顶] java微信开发---jsapi接口授权和解绑设备
- 支付顺序-->微信支付到公司账户-->待出票
- 微信开发之ngrok内网穿透工具
- 微信开放平台微信登录授权的说明