1、在線支付概述
什么是在線支付呢?沒錯,就是在網上花錢!大家一定有過這樣的經歷。但是你可能不太了解在線支付的“內情”,下面我們來了解一下!
如果你現在開始經營一個電子商務網站,用戶買了東西一定要支付,你的網站一定要可以連接各大銀行了,然后在各大銀行支付完成后,再返回到你的網站上顯示“支付成功”!
這就是今天我們要做的事情,連接銀行的網銀系統完成支付。說專業一點,我們稱之為“開發在線支付的網關”
2、兩種在線支付的方式
在線支付一共有兩種方式:
*電商直接與銀行對接
*電商通過第三方支付平臺與銀行對接
電商直接與銀行對接,這也要銀行同意才行,但可惜的是,銀行很“牛”,不是誰想與它對接都可以的。如果你的電商每日的資金流量夠大,那么銀行會和你對接,因為客戶支付給電商的錢都存到了銀行的帳戶中!但是如果資金流量小,銀行不會理你的!
當小網站資金量不足時,不能與銀行對接,那么它們會選擇與第三方支付公司合作。大家也都明白這是些什么公司,例如:支付寶、易寶、財富通、快錢等公司是國內比較有名的。它們這些公司可以與銀行對接(因為資金夠多),然后小電商與它們對接!但是第三方是要求收費的!第三方一般會收取電商1%的費用,不過不會收客戶的錢。
通過上圖大家可以了解到,在銀行的頁面上會顯示出商城名稱、RMB訂單號、訂單時間。。。,這些東西銀行是怎么知道的,當然是電商傳遞給銀行的。當電商與銀行對接后,電商要給銀行的頁面傳遞銀行頁面需要的參數,所以銀行的頁面才能顯示這些數據!
但是,我們的商城不能只可以對接一家銀行吧!怎么也要對接BOC、CCB、ABC、ICBC四家吧!不同的銀行需要的對接參數是不相同的,這說明我們在開發時要為不同的銀行寫不同的對接代碼。這也是直接與銀行對接的缺點!當然與銀行直接對接也有好處,就是安全,沒有手續費!
為不同的銀行開發不同的代碼(缺點);
安全(優點);
沒有手續費(優點);
小電商銀行不讓對接(缺點)。
上圖中已經說明,客戶在電商的網站上點擊確認支付后,會定向到第三方的網站,然后再由第三方與銀行對接。這說明電商要傳遞給第三方參數!再由第三方把參數傳遞給銀行。這種方式的好處是:只需要針對第三方開發即可,而不用再為每家銀行提供參數。為每家銀行提供參數的工作是第三方的任務了。但是,第三方不老可靠的,如果第三方倒閉了,人跑了,那你的錢就沒了。因為客戶支付的錢沒有到你的銀行帳戶中,而是支付到了第三方的銀行帳戶中,而你是在第三方有一個帳戶。而且第三方還要收手續費,一般是1%,這可不是小數字啊(真黑)。
3、通過第三方在線支付規則
電商想在第三方注冊商戶,需要向第三方提供ICP認證。ICP經營許可證是根據國家《互聯網管理辦法規定》,經營性網站必須辦理的網站經營許可證,沒有就屬于非法經營。
我們不可能因為練習就去辦理ICP!所以我們無法在第三方注冊商戶。不過我們已經有現成的在易寶注冊的商戶,所以這一步就可以忽略了。
當你在易寶注冊成功后,易寶會給你如下幾樣東西:
在易寶的開戶賬號(即商戶編碼):10001126856
易寶接入規范:一個chm文件
對稱加密算法類:PaymentUtil.java
密鑰:69cl522AV6q613Ii4W6u8K6XuW8vM1N6bFgyv769220IuYe9u37N4y7rI4Pl
在易寶接入規范中,我們可以查找到易寶的支付網關,其實就是一個URL,用來與易寶對接的一個網址:https://www.yeepay.com/app-merchant-proxy/node
在易寶接入規范中,還可以查找到易寶要求的參數,在電商與易寶對接時需要給支付網關傳遞這些參數:
正式請求地址:https://www.yeepay.com/app-merchant-proxy/node
這些參數需要追加到URL后面。
但是要注意,這些參數的值需要加密。加密的密鑰和加密算法易寶都會提供!
其中p8_Url表示當支付成功后,返回到電商的哪個頁面。這說明我們需要寫一個顯示結果的頁面。第三方在支付成功后,會重定向到我們指定的返回頁面,而且還會帶給我們一些參數,我們的頁面需要獲取這些參數,顯示在頁面中。下面是第三方返回的參數:
4、開發第三方在線支付系統
步驟:
index.jsp頁面:一個表單,提交到BuyServlet,表單項有:訂單編號、付款金額、選擇銀行
BuyServlet:獲取表單數據,準備連接第三方網關。因為在index.jsp頁面中只給出3個參數,而第三方需要的參數有N多,頁面沒有給出的參數由BuyServlet補充。而且參數還需要加密,這也需要在BuyServlet中完成
BackServlet:當用戶支付成功后,第三方會重定向到我們指定的返回頁面,我們使用BackServlet作為返回頁面,它用來接收第三方傳遞的參數,顯示在頁面中
因為已經有了在易寶的注冊商號,所以我們就不用自己去注冊商號了。所以這里使用易寶做為第三方支付平臺來測試。因為我本人沒有電商(必須通過ICP認證的電商),所以也不能在第三方注冊商號。
我們現在使用的易寶商號是由傳智播客提供的,巴巴運動網在易寶注冊的商號。所以在測試時支付的錢都給了巴巴運動網在易寶注冊的商號了。
第一步:index.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
< form action = "" method = "post" > 訂單號:< input type = "text" name = "p2_Order" />< br /> 金 額:< input type = "text" name = "p3_Amt" />< br /> 選擇銀行:< br /> < input type = "radio" name = "pd_FrpId" value = "ICBC-NET-B2C" />工商銀行 < img src = "bank_img/icbc.bmp" align = "middle" /> < input type = "radio" name = "pd_FrpId" value = "BOC-NET-B2C" />中國銀行 < img src = "bank_img/bc.bmp" align = "middle" />< br />< br /> < input type = "radio" name = "pd_FrpId" value = "ABC-NET-B2C" />農業銀行 < img src = "bank_img/abc.bmp" align = "middle" /> < input type = "radio" name = "pd_FrpId" value = "CCB-NET-B2C" />建設銀行 < img src = "bank_img/ccb.bmp" align = "middle" />< br />< br /> < input type = "radio" name = "pd_FrpId" value = "BOCO-NET-B2C" />交通銀行 < img src = "bank_img/bcc.bmp" align = "middle" />< br /> < input type = "submit" value = "確認支付" /> </ form > |
每個銀行對應的值:
第二步:BuyServlet.java
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
|
public class BuyServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding( "utf-8" ); response.setContentType( "text/html;charset=utf-8" ); String p0_Cmd = "Buy" ; // 業務類型,固定值為buy,即“買” String p1_MerId = "10001126856" ; // 在易寶注冊的商號 String p2_Order = request.getParameter( "p2_Order" ); // 訂單編號 String p3_Amt = request.getParameter( "p3_Amt" ); // 支付的金額 String p4_Cur = "CNY" ; // 交易種幣,固定值為CNY,表示人民幣 String p5_Pid = "" ; // 商品名稱 String p6_Pcat = "" ; // 商品各類 String p7_Pdesc = "" ; // 商品描述 String p8_Url = "http://localhost:8080/buy/BackServlet" ;// 電商的返回頁面,當支付成功后,易寶會重定向到這個頁面 String p9_SAF = "" ; // 送貨地址 String pa_MP = "" ; // 商品擴展信息 String pd_FrpId = request.getParameter( "pd_FrpId" ); // 支付通道,即選擇銀行 String pr_NeedResponse = "1" ; // 應答機制,固定值為1 // 密鑰,由易寶提供,只有商戶和易寶知道這個密鑰。 String keyValue = "69cl522AV6q613Ii4W6u8K6XuW8vM1N6bFgyv769220IuYe9u37N4y7rI4Pl" ; // 通過上面的參數、密鑰、加密算法,生成hmac值 // 參數的順序是必須的,如果沒有值也不能給出null,而應該給出空字符串。 String hmac = PaymentUtil.buildHmac(p0_Cmd, p1_MerId, p2_Order, p3_Amt, p4_Cur, p5_Pid, p6_Pcat, p7_Pdesc, p8_Url, p9_SAF, pa_MP, pd_FrpId, pr_NeedResponse, keyValue); // 把所有參數連接到網關地址后面 String url = "https://www.yeepay.com/app-merchant-proxy/node" ; url += "?p0_Cmd=" + p0_Cmd + "&p1_MerId=" + p1_MerId + "&p2_Order=" + p2_Order + "&p3_Amt=" + p3_Amt + "&p4_Cur=" + p4_Cur + "&p5_Pid=" + p5_Pid + "&p6_Pcat=" + p6_Pcat + "&p7_Pdesc=" + p7_Pdesc + "&p8_Url=" + p8_Url + "&p9_SAF=" + p9_SAF + "&pa_MP=" + pa_MP + "&pd_FrpId=" + pd_FrpId + "&pr_NeedResponse=" + pr_NeedResponse + "&hmac=" + hmac; System.out.println(url); // 重定向到網關 response.sendRedirect(url); } } |
第三步:BackServlet
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
|
public class BackServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType( "text/html;charset=utf-8" ); /* * 易寶會提供一系列的結果參數,我們獲取其中需要的即可 * 獲取支付結果:r1_Code,1表示支付成功。 * 獲取支付金額:r3_Amt * 獲取電商的訂單號:r6_Order * 獲取結果返回類型:r9_BType,1表示重定向返回,2表示點對點返回, * 但點對點我們收不到,因為我們的ip都是局域網ip。 */ String r1_Code = request.getParameter( "r1_Code" ); String r3_Amt = request.getParameter( "r3_Amt" ); String r6_Order = request.getParameter( "r6_Order" ); String r9_BType = request.getParameter( "r9_BType" ); if (r1_Code.equals( "1" )) { if (r9_BType.equals( "1" )) { response.getWriter().print( "<h1>支付成功!</h1>" ); //其實支付不成功時根本易寶根本就不會返回到本Servlet response.getWriter().print( "支付金額為:" + r3_Amt + "<br/>" ); response.getWriter().print( "訂單號為:" + r6_Order + "<br/>" ); } } } } |
易寶支付提供的獲取hmac的工具類
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
public class PaymentUtil { private static String encodingCharset = "UTF-8" ; /** * 生成hmac方法 * * @param p0_Cmd 業務類型 * @param p1_MerId 商戶編號 * @param p2_Order 商戶訂單號 * @param p3_Amt 支付金額 * @param p4_Cur 交易幣種 * @param p5_Pid 商品名稱 * @param p6_Pcat 商品種類 * @param p7_Pdesc 商品描述 * @param p8_Url 商戶接收支付成功數據的地址 * @param p9_SAF 送貨地址 * @param pa_MP 商戶擴展信息 * @param pd_FrpId 銀行編碼 * @param pr_NeedResponse 應答機制 * @param keyValue 商戶密鑰 * @return */ public static String buildHmac(String p0_Cmd,String p1_MerId, String p2_Order, String p3_Amt, String p4_Cur,String p5_Pid, String p6_Pcat, String p7_Pdesc,String p8_Url, String p9_SAF,String pa_MP,String pd_FrpId, String pr_NeedResponse,String keyValue) { StringBuilder sValue = new StringBuilder(); // 業務類型 sValue.append(p0_Cmd); // 商戶編號 sValue.append(p1_MerId); // 商戶訂單號 sValue.append(p2_Order); // 支付金額 sValue.append(p3_Amt); // 交易幣種 sValue.append(p4_Cur); // 商品名稱 sValue.append(p5_Pid); // 商品種類 sValue.append(p6_Pcat); // 商品描述 sValue.append(p7_Pdesc); // 商戶接收支付成功數據的地址 sValue.append(p8_Url); // 送貨地址 sValue.append(p9_SAF); // 商戶擴展信息 sValue.append(pa_MP); // 銀行編碼 sValue.append(pd_FrpId); // 應答機制 sValue.append(pr_NeedResponse); return PaymentUtil.hmacSign(sValue.toString(), keyValue); } /** * 返回校驗hmac方法 * * @param hmac 支付網關發來的加密驗證碼 * @param p1_MerId 商戶編號 * @param r0_Cmd 業務類型 * @param r1_Code 支付結果 * @param r2_TrxId 易寶支付交易流水號 * @param r3_Amt 支付金額 * @param r4_Cur 交易幣種 * @param r5_Pid 商品名稱 * @param r6_Order 商戶訂單號 * @param r7_Uid 易寶支付會員ID * @param r8_MP 商戶擴展信息 * @param r9_BType 交易結果返回類型 * @param keyValue 密鑰 * @return */ public static boolean verifyCallback(String hmac, String p1_MerId, String r0_Cmd, String r1_Code, String r2_TrxId, String r3_Amt, String r4_Cur, String r5_Pid, String r6_Order, String r7_Uid, String r8_MP, String r9_BType, String keyValue) { StringBuilder sValue = new StringBuilder(); // 商戶編號 sValue.append(p1_MerId); // 業務類型 sValue.append(r0_Cmd); // 支付結果 sValue.append(r1_Code); // 易寶支付交易流水號 sValue.append(r2_TrxId); // 支付金額 sValue.append(r3_Amt); // 交易幣種 sValue.append(r4_Cur); // 商品名稱 sValue.append(r5_Pid); // 商戶訂單號 sValue.append(r6_Order); // 易寶支付會員ID sValue.append(r7_Uid); // 商戶擴展信息 sValue.append(r8_MP); // 交易結果返回類型 sValue.append(r9_BType); String sNewString = PaymentUtil.hmacSign(sValue.toString(), keyValue); return sNewString.equals(hmac); } /** * @param aValue * @param aKey * @return */ public static String hmacSign(String aValue, String aKey) { byte k_ipad[] = new byte [ 64 ]; byte k_opad[] = new byte [ 64 ]; byte keyb[]; byte value[]; try { keyb = aKey.getBytes(encodingCharset); value = aValue.getBytes(encodingCharset); } catch (UnsupportedEncodingException e) { keyb = aKey.getBytes(); value = aValue.getBytes(); } Arrays.fill(k_ipad, keyb.length, 64 , ( byte ) 54 ); Arrays.fill(k_opad, keyb.length, 64 , ( byte ) 92 ); for ( int i = 0 ; i < keyb.length; i++) { k_ipad[i] = ( byte ) (keyb[i] ^ 0x36 ); k_opad[i] = ( byte ) (keyb[i] ^ 0x5c ); } MessageDigest md = null ; try { md = MessageDigest.getInstance( "MD5" ); } catch (NoSuchAlgorithmException e) { return null ; } md.update(k_ipad); md.update(value); byte dg[] = md.digest(); md.reset(); md.update(k_opad); md.update(dg, 0 , 16 ); dg = md.digest(); return toHex(dg); } public static String toHex( byte input[]) { if (input == null ) return null ; StringBuffer output = new StringBuffer(input.length * 2 ); for ( int i = 0 ; i < input.length; i++) { int current = input[i] & 0xff ; if (current < 16 ) output.append( "0" ); output.append(Integer.toString(current, 16 )); } return output.toString(); } /** * * @param args * @param key * @return */ public static String getHmac(String[] args, String key) { if (args == null || args.length == 0 ) { return ( null ); } StringBuffer str = new StringBuffer(); for ( int i = 0 ; i < args.length; i++) { str.append(args[i]); } return (hmacSign(str.toString(), key)); } /** * @param aValue * @return */ public static String digest(String aValue) { aValue = aValue.trim(); byte value[]; try { value = aValue.getBytes(encodingCharset); } catch (UnsupportedEncodingException e) { value = aValue.getBytes(); } MessageDigest md = null ; try { md = MessageDigest.getInstance( "SHA" ); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return null ; } return toHex(md.digest(value)); } // public static void main(String[] args) { // System.out.println(hmacSign("AnnulCard1000043252120080620160450.0http://localhost/SZXpro/callback.asp榪?4564868265473632445648682654736324511","8UPp0KE8sq73zVP370vko7C39403rtK1YwX40Td6irH216036H27Eb12792t")); // } } |
易寶回調
點對點:易寶直接訪問電商,這里沒有客戶端什么事了
這種方式是必須要使用的,我們這種方式是收不到的!因為我們沒有固定IP
易寶有一個重發機制,如果它訪問你,你不給它回信息,它會一直重發!
電商需要返回一個以SUCCESS開頭的字符串即可!
引導客戶端瀏覽器重定向到電商。是讓客戶端訪問電商!
可以不使用的!
hmac:13參數值+keyValue(密鑰) + 算法(md5)
13參數值:自己設置的!
keyValue:易寶在我們注冊后發給我們的,這個東東只有我們和易寶知道!
底層為md5的算法:PaymentUtil.buildHmac(14個),它返回hmac
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。