一、下载证书并导入到系统
微信支付接口中,涉及资金回滚的接口会使用到商户证书,包括退款、撤销接口。商家在申请微信支付成功后,可以按照以下路径下载:微信商户平台(pay.weixin.qq.com)-->账户设置-->api安全-->证书下载。
下载的时候需要手机验证及登录密码。下载后找到apiclient_cert.p12这个证书,双击导入,导入的时候提示输入密码,这个密码就是商户id,且必须是在自己的商户平台下载的证书。否则会出现密码错误的提示:
导入正确的提示:
二、编写代码
首先初始化退款接口中的请求参数,如微信订单号transaction_id(和商户订单号只需要知道一个)、订单金额total_fee等;其次调用mobimessage中的refundresdata2xml方法解析成需要的类型;最后调用refundrequest类的httpsrequest方法触发请求。
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
41
42
43
44
45
|
/**
* 处理退款请求
* @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方法。
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
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数据之间的转换。
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
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方法加载证书到系统中,其中证书地址如下:
1
|
public static string certlocalpath = "/web-inf/cert/apiclient_cert.p12" ;
|
refundrequest类中httpsrequest方法调用微信接口,触发请求。
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
/**
* 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;
}
}
|
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/fenghuibian/article/details/52459699