一、下載證書并導入到系統
微信支付接口中,涉及資金回滾的接口會使用到商戶證書,包括退款、撤銷接口。商家在申請微信支付成功后,可以按照以下路徑下載:微信商戶平臺(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