这篇文章是java微信退款的教程,退款之前用户需要先进行支付,支付之后才可以使用退款。做到退款的同学应该已经是完成了支付了,我写的退款和支付的流程很相似只是所需的参数有所不同。
1
2
3
4
5
6
7
8
9
10
11
|
string outtradeno = request.getparameter( "outtradeno" ); // 获取商户订单号
integer totalfee = integer.parseint(request.getparameter( "totalfee" )); // 获取支付金额
map<string, string> getmap = new hashmap<string, string>();
// 获得当前目录
string path = request.getsession().getservletcontext().getrealpath( "/" );
date now = new date();
simpledateformat dateformat = new simpledateformat( "yyyymmddhhmmss" ); // 可以方便地修改日期格式
string outrefundno = "no" + dateformat.format(now);
|
提供的参数有订单号这个是支付成功之后生成的唯一号码,然后是获取到用户支付的金额这两个参数都是由支付之后的订单上面获得的。下面那个path则是保存微信安全证书文件的位置,这里提一下要实现微信退款和微信企业转账功能是需要到微信商户平台去下载安全证书的,然后把证书放在项目的web-inf/目录下即可。
1
2
3
4
5
6
7
8
9
10
11
12
|
refundreqdata refundreqdata = new refundreqdata();
refundreqdata.setappid(configure.getappid());
refundreqdata.setmch_id(configure.getmch_id());
refundreqdata.setnonce_str(randomstringgenerator.getrandomstringbylength( 32 ));
refundreqdata.setout_trade_no(outtradeno);
refundreqdata.setout_refund_no(outrefundno);
refundreqdata.settotal_fee(totalfee);
refundreqdata.setrefund_fee(refundfee);
refundreqdata.setop_user_id(configure.getmch_id());
refundreqdata.setnotify_url( "https://weixin.qq.com/notify/" );
string sign = signature.getsign(refundreqdata); // 生成签名
refundreqdata.setsign(sign);
|
获取到需要的参数之后呢,我在这里使用了一个退款的实体类把这些参数保存到了我的实体类里面方便后面的签名加密。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
arraylist<string> list = new arraylist<string>();
@suppresswarnings ( "rawtypes" )
class cls = o.getclass();
field[] fields = cls.getdeclaredfields();
for (field f : fields) {
f.setaccessible( true );
if (f.get(o) != null && f.get(o) != "" ) {
string name = f.getname();
xstreamalias anno = f.getannotation(xstreamalias. class );
if (anno != null )
name = anno.value();
list.add(name + "=" + f.get(o) + "&" );
}
}
int size = list.size();
string [] arraytosort = list.toarray( new string[size]);
arrays.sort(arraytosort, string.case_insensitive_order);
stringbuilder sb = new stringbuilder();
for ( int i = 0 ; i < size; i ++) {
sb.append(arraytosort[i]);
}
string result = sb.tostring();
result += "key=" + configure.getkey();
system.out.println( "签名数据:" +result);
result = md5util.md5encode(result, "utf-8" ).touppercase();
return result;
|
这个是我签名加密的方法,把数据加密之后会成为一个很长的字符串,但是官方提供的退款接口是没办法解析你这个超长字符串的数据的,所以我们要把这个字符串变成官方接口认识的数据格式也就是xml格式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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);
}
}
};
}
});
|
这一段代码是我把字符串格式的数据转换成xml格式的方法。再把xml格式的数据保存在一个字符串里面,这个时候我们开始向官方接口发送数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
public string httpsrequest(string url, string xmlobj, string path) throws exception {
// 加载证书
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) {
e.printstacktrace();
} catch (connecttimeoutexception e) {
e.printstacktrace();
} catch (sockettimeoutexception e) {
e.printstacktrace();
} catch (exception e) {
e.printstacktrace();
} finally {
httppost.abort();
}
return result;
}
|
通过https往api post xml数据。
1
2
3
4
5
6
7
8
|
refundrequest refundrequest = new refundrequest();
string result = refundrequest.httpsrequest( "https://api.mch.weixin.qq.com/secapi/pay/refund" , info, path);
getmap = mobimessage.parsexml( new string(result.tostring().getbytes(), "utf-8" ));
system.out.println(getmap + "............getmap" );
json.put( "return_msg" , getmap.get( "return_msg" ));
json.put( "return_code" , getmap.get( "return_code" ));
json.put( "outtradeno" , outtradeno);
|
这一段就是给接口发送数据的代码(官方api接口,xml数据,证书的位置),然后我们接受接口返回的信息通过返回的return_msg和return_code来判断是否退款成功。
好了,微信退款就是这样完全可以照着代码把流程读出来很清晰明了也很简单,代码能力稍强的都看得懂,我主要是给大家提供一个思路。如果有同学没看懂也没关系下面是该项目的源码地址大家可以去下载退款的源代码都在里面:wechat.rar
ps:总结一下我在做微信退款的时候遇到的问题:
1.遇到了一个"keystore password was incorrect"这个问题,原因这个退款所需要的证书不正确,这个证书是需要从微信平台去下载这个证书;
2.一定要注意在支付时的订单号码和退款时的订单号码是一致的,我碰到的这个问题是在支付时,把订单号码和微信返回的交易号码存数据库时弄反了,导致微信找不到这笔订单;
3.另外碰到的问题是退款在获取证书的时候,证书的路径不对,导致没有获取到证书,所以退款失败,所以还要检查证书是否存在,证书的路径是否正确,还要留意服务器上能否获取到证书。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/qq_37756682/article/details/82625108