本文讲解了Java如何实现JsApi方式的微信支付,代码内容详细,文章思路清晰,需要的朋友可以参考下
要使用JsApi进行微信支付,首先要从微信获得一个prepay_id,然后通过调用微信的jsapi完成支付,JS API的返回结果get_brand_wcpay_request:ok仅在用户成功完成支付时返回。由于前端交互复杂,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以统一处理为用户遇到错误或者主动放弃,不必细化区分。
示例代码如下:
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
|
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest'
, {
"appId"
:
"wx2421b1c4370ec43b"
,
//公众号名称,由商户传入
"timeStamp"
:
" 1395712654"
,
//时间戳,自1970年以来的秒数
"nonceStr"
:
"e61463f8efa94090b1f366cccfbbb444"
,
//随机串
"package"
:
"u802345jgfjsdfgsdg888"
,
"signType"
:
"MD5"
,
//微信签名方式:
"paySign"
:
"70EA570631E4BB79628FBCA90534C63FF7FADD89"
//微信签名
},
function(res){
if
(res.err_msg ==
"get_brand_wcpay_request:ok"
) {}
// 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}
);
}
if
(typeof WeixinJSBridge ==
"undefined"
){
if
( document.addEventListener ){
document.addEventListener(
'WeixinJSBridgeReady'
, onBridgeReady,
false
);
}
else
if
(document.attachEvent){
document.attachEvent(
'WeixinJSBridgeReady'
, onBridgeReady);
document.attachEvent(
'onWeixinJSBridgeReady'
, onBridgeReady);
}
}
else
{
onBridgeReady();
}
|
以上传入的参数package,即为prepay_id
下面讲的是获得参数来调用jsapi
我们调用JSAPI时,必须获得用户的openid,(trade_type=JSAPI,openid为必填参数。)
首先定义一个请求的对象:
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
|
package
com.unstoppedable.protocol;
import
com.unstoppedable.common.Configure;
import
com.unstoppedable.common.HttpService;
import
com.unstoppedable.common.RandomStringGenerator;
import
com.unstoppedable.common.Signature;
import
java.lang.reflect.Field;
import
java.util.HashMap;
import
java.util.Map;
public
class
UnifiedOrderReqData {
private
String appid;
private
String mch_id;
private
String device_info;
private
String nonce_str;
private
String sign;
private
String body;
private
String detail;
private
String attach;
private
String out_trade_no;
private
String fee_type;
private
int
total_fee;
private
String spbill_create_ip;
private
String time_start;
private
String time_expire;
private
String goods_tag;
private
String notify_url;
private
String trade_type;
private
String product_id;
private
String limit_pay;
private
String openid;
private
UnifiedOrderReqData(UnifiedOrderReqDataBuilder builder) {
this
.appid = builder.appid;
this
.mch_id = builder.mch_id;
this
.device_info = builder.device_info;
this
.nonce_str = RandomStringGenerator.getRandomStringByLength(
32
);
this
.body = builder.body;
this
.detail = builder.detail;
this
.attach = builder.attach;
this
.out_trade_no = builder.out_trade_no;
this
.fee_type = builder.fee_type;
this
.total_fee = builder.total_fee;
this
.spbill_create_ip = builder.spbill_create_ip;
this
.time_start = builder.time_start;
this
.time_expire = builder.time_expire;
this
.goods_tag = builder.goods_tag;
this
.notify_url = builder.notify_url;
this
.trade_type = builder.trade_type;
this
.product_id = builder.product_id;
this
.limit_pay = builder.limit_pay;
this
.openid = builder.openid;
this
.sign = Signature.getSign(toMap());
}
public
void
setAppid(String appid) {
this
.appid = appid;
}
public
void
setMch_id(String mch_id) {
this
.mch_id = mch_id;
}
public
void
setDevice_info(String device_info) {
this
.device_info = device_info;
}
public
void
setNonce_str(String nonce_str) {
this
.nonce_str = nonce_str;
}
public
void
setSign(String sign) {
this
.sign = sign;
}
public
void
setBody(String body) {
this
.body = body;
}
public
void
setDetail(String detail) {
this
.detail = detail;
}
public
void
setAttach(String attach) {
this
.attach = attach;
}
public
void
setOut_trade_no(String out_trade_no) {
this
.out_trade_no = out_trade_no;
}
public
void
setFee_type(String fee_type) {
this
.fee_type = fee_type;
}
public
void
setTotal_fee(
int
total_fee) {
this
.total_fee = total_fee;
}
public
void
setSpbill_create_ip(String spbill_create_ip) {
this
.spbill_create_ip = spbill_create_ip;
}
public
void
setTime_start(String time_start) {
this
.time_start = time_start;
}
public
void
setTime_expire(String time_expire) {
this
.time_expire = time_expire;
}
public
void
setGoods_tag(String goods_tag) {
this
.goods_tag = goods_tag;
}
public
void
setNotify_url(String notify_url) {
this
.notify_url = notify_url;
}
public
void
setTrade_type(String trade_type) {
this
.trade_type = trade_type;
}
public
void
setProduct_id(String product_id) {
this
.product_id = product_id;
}
public
void
setLimit_pay(String limit_pay) {
this
.limit_pay = limit_pay;
}
public
void
setOpenid(String openid) {
this
.openid = openid;
}
public
Map<String, Object> toMap() {
Map<String, Object> map =
new
HashMap<String, Object>();
Field[] fields =
this
.getClass().getDeclaredFields();
for
(Field field : fields) {
Object obj;
try
{
obj = field.get(
this
);
if
(obj !=
null
) {
map.put(field.getName(), obj);
}
}
catch
(IllegalArgumentException e) {
e.printStackTrace();
}
catch
(IllegalAccessException e) {
e.printStackTrace();
}
}
return
map;
}
public
static
class
UnifiedOrderReqDataBuilder {
private
String appid;
private
String mch_id;
private
String device_info;
private
String body;
private
String detail;
private
String attach;
private
String out_trade_no;
private
String fee_type;
private
int
total_fee;
private
String spbill_create_ip;
private
String time_start;
private
String time_expire;
private
String goods_tag;
private
String notify_url;
private
String trade_type;
private
String product_id;
private
String limit_pay;
private
String openid;
public
UnifiedOrderReqDataBuilder(String appid, String mch_id, String body, String out_trade_no, Integer total_fee,
String spbill_create_ip, String notify_url, String trade_type) {
if
(appid ==
null
) {
throw
new
IllegalArgumentException(
"传入参数appid不能为null"
);
}
if
(mch_id ==
null
) {
throw
new
IllegalArgumentException(
"传入参数mch_id不能为null"
);
}
if
(body ==
null
) {
throw
new
IllegalArgumentException(
"传入参数body不能为null"
);
}
if
(out_trade_no ==
null
) {
throw
new
IllegalArgumentException(
"传入参数out_trade_no不能为null"
);
}
if
(total_fee ==
null
) {
throw
new
IllegalArgumentException(
"传入参数total_fee不能为null"
);
}
if
(spbill_create_ip ==
null
) {
throw
new
IllegalArgumentException(
"传入参数spbill_create_ip不能为null"
);
}
if
(notify_url ==
null
) {
throw
new
IllegalArgumentException(
"传入参数notify_url不能为null"
);
}
if
(trade_type ==
null
) {
throw
new
IllegalArgumentException(
"传入参数trade_type不能为null"
);
}
this
.appid = appid;
this
.mch_id = mch_id;
this
.body = body;
this
.out_trade_no = out_trade_no;
this
.total_fee = total_fee;
this
.spbill_create_ip = spbill_create_ip;
this
.notify_url = notify_url;
this
.trade_type = trade_type;
}
public
UnifiedOrderReqDataBuilder setDevice_info(String device_info) {
this
.device_info = device_info;
return
this
;
}
public
UnifiedOrderReqDataBuilder setDetail(String detail) {
this
.detail = detail;
return
this
;
}
public
UnifiedOrderReqDataBuilder setAttach(String attach) {
this
.attach = attach;
return
this
;
}
public
UnifiedOrderReqDataBuilder setFee_type(String fee_type) {
this
.fee_type = fee_type;
return
this
;
}
public
UnifiedOrderReqDataBuilder setTime_start(String time_start) {
this
.time_start = time_start;
return
this
;
}
public
UnifiedOrderReqDataBuilder setTime_expire(String time_expire) {
this
.time_expire = time_expire;
return
this
;
}
public
UnifiedOrderReqDataBuilder setGoods_tag(String goods_tag) {
this
.goods_tag = goods_tag;
return
this
;
}
public
UnifiedOrderReqDataBuilder setProduct_id(String product_id) {
this
.product_id = product_id;
return
this
;
}
public
UnifiedOrderReqDataBuilder setLimit_pay(String limit_pay) {
this
.limit_pay = limit_pay;
return
this
;
}
public
UnifiedOrderReqDataBuilder setOpenid(String openid) {
this
.openid = openid;
return
this
;
}
public
UnifiedOrderReqData build() {
if
(
"JSAPI"
.equals(
this
.trade_type) &&
this
.openid ==
null
) {
throw
new
IllegalArgumentException(
"当传入trade_type为JSAPI时,openid为必填参数"
);
}
if
(
"NATIVE"
.equals(
this
.trade_type) &&
this
.product_id ==
null
) {
throw
new
IllegalArgumentException(
"当传入trade_type为NATIVE时,product_id为必填参数"
);
}
return
new
UnifiedOrderReqData(
this
);
}
}
}
|
因为有些参数为必填,有些参数为选填。而且sign要等所有参数传入之后才能计算的出,所以这里用了builder模式。关于builder模式。
我们选用httpclient进行网络传输。
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
|
package
com.unstoppedable.common;
import
com.thoughtworks.xstream.XStream;
import
com.thoughtworks.xstream.io.xml.DomDriver;
import
com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import
org.apache.commons.logging.Log;
import
org.apache.commons.logging.LogFactory;
import
org.apache.http.HttpEntity;
import
org.apache.http.HttpResponse;
import
org.apache.http.client.ClientProtocolException;
import
org.apache.http.client.ResponseHandler;
import
org.apache.http.client.config.RequestConfig;
import
org.apache.http.client.methods.HttpGet;
import
org.apache.http.client.methods.HttpPost;
import
org.apache.http.conn.ConnectTimeoutException;
import
org.apache.http.conn.ConnectionPoolTimeoutException;
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
javax.net.ssl.SSLContext;
import
java.io.File;
import
java.io.FileInputStream;
import
java.io.IOException;
import
java.net.SocketTimeoutException;
import
java.security.KeyStore;
/**
* Created by hupeng on 2015/7/28.
*/
public
class
HttpService {
private
static
Log logger = LogFactory.getLog(HttpService.
class
);
private
static
CloseableHttpClient httpClient = buildHttpClient();
//连接超时时间,默认10秒
private
static
int
socketTimeout =
5000
;
//传输超时时间,默认30秒
private
static
int
connectTimeout =
5000
;
private
static
int
requestTimeout =
5000
;
public
static
CloseableHttpClient buildHttpClient() {
try
{
KeyStore keyStore = KeyStore.getInstance(
"PKCS12"
);
FileInputStream instream =
new
FileInputStream(
new
File(Configure.getCertLocalPath()));
//加载本地的证书进行https加密传输
try
{
keyStore.load(instream, Configure.getCertPassword().toCharArray());
//设置证书密码
}
finally
{
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, Configure.getCertPassword().toCharArray())
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf =
new
SSLConnectionSocketFactory(
sslcontext,
new
String[]{
"TLSv1"
},
null
,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(connectTimeout)
.setConnectionRequestTimeout(requestTimeout)
.setSocketTimeout(socketTimeout).build();
httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.setSSLSocketFactory(sslsf)
.build();
return
httpClient;
}
catch
(Exception e) {
throw
new
RuntimeException(
"error create httpclient......"
, e);
}
}
public
static
String doGet(String requestUrl)
throws
Exception {
HttpGet httpget =
new
HttpGet(requestUrl);
try
{
logger.debug(
"Executing request "
+ httpget.getRequestLine());
// Create a custom response handler
ResponseHandler<String> responseHandler =
new
ResponseHandler<String>() {
@Override
public
String handleResponse(
final
HttpResponse response)
throws
ClientProtocolException, IOException {
int
status = response.getStatusLine().getStatusCode();
if
(status >=
200
&& status <
300
) {
HttpEntity entity = response.getEntity();
return
entity !=
null
? EntityUtils.toString(entity) :
null
;
}
else
{
throw
new
ClientProtocolException(
"Unexpected response status: "
+ status);
}
}
};
return
httpClient.execute(httpget, responseHandler);
}
finally
{
httpget.releaseConnection();
}
}
public
static
String doPost(String url, Object object2Xml) {
String result =
null
;
HttpPost httpPost =
new
HttpPost(url);
//解决XStream对出现双下划线的bug
XStream xStreamForRequestPostData =
new
XStream(
new
DomDriver(
"UTF-8"
,
new
XmlFriendlyNameCoder(
"-_"
,
"_"
)));
//将要提交给API的数据对象转换成XML格式数据Post给API
String postDataXML = xStreamForRequestPostData.toXML(object2Xml);
logger.info(
"API,POST过去的数据是:"
);
logger.info(postDataXML);
//得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
StringEntity postEntity =
new
StringEntity(postDataXML,
"UTF-8"
);
httpPost.addHeader(
"Content-Type"
,
"text/xml"
);
httpPost.setEntity(postEntity);
//设置请求器的配置
logger.info(
"executing request"
+ httpPost.getRequestLine());
try
{
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
result = EntityUtils.toString(entity,
"UTF-8"
);
}
catch
(ConnectionPoolTimeoutException e) {
logger.error(
"http get throw ConnectionPoolTimeoutException(wait time out)"
, e);
}
catch
(ConnectTimeoutException e) {
logger.error(
"http get throw ConnectTimeoutException"
, e);
}
catch
(SocketTimeoutException e) {
logger.error(
"http get throw SocketTimeoutException"
, e);
}
catch
(Exception e) {
logger.error(
"http get throw Exception"
, e);
}
finally
{
httpPost.abort();
}
return
result;
}
}
|
然后是我们的总入口:
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
|
package
com.unstoppedable.service;
import
com.unstoppedable.common.Configure;
import
com.unstoppedable.common.HttpService;
import
com.unstoppedable.common.XMLParser;
import
com.unstoppedable.protocol.UnifiedOrderReqData;
import
org.xml.sax.SAXException;
import
javax.xml.parsers.ParserConfigurationException;
import
java.io.IOException;
import
java.util.Map;
/**
* Created by hupeng on 2015/7/28.
*/
public
class
WxPayApi {
public
static
Map<String,Object> UnifiedOrder(UnifiedOrderReqData reqData)
throws
IOException, SAXException, ParserConfigurationException {
String res = HttpService.doPost(Configure.UNIFIED_ORDER_API, reqData);
return
XMLParser.getMapFromXML(res);
}
public
static
void
main(String[] args)
throws
Exception {
UnifiedOrderReqData reqData =
new
UnifiedOrderReqData.UnifiedOrderReqDataBuilder(
"appid"
,
"mch_id"
,
"body"
,
"out_trade_no"
,
1
,
"spbill_create_ip"
,
"notify_url"
,
"JSAPI"
).setOpenid(
"openid"
).build();
System.out.println(UnifiedOrder(reqData));
}
}
|
返回的xml为:
1
2
3
4
5
6
7
8
9
10
11
|
<
xml
>
<
return_code
>
<![CDATA[SUCCESS]]>
</
return_code
>
<
return_msg
>
<![CDATA[OK]]>
</
return_msg
>
<
appid
>
<![CDATA[wx2421b1c4370ec43b]]>
</
appid
>
<
mch_id
>
<![CDATA[10000100]]>
</
mch_id
>
<
nonce_str
>
<![CDATA[IITRi8Iabbblz1Jc]]>
</
nonce_str
>
<
sign
>
<![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]>
</
sign
>
<
result_code
>
<![CDATA[SUCCESS]]>
</
result_code
>
<
prepay_id
>
<![CDATA[wx201411101639507cbf6ffd8b0779950874]]>
</
prepay_id
>
<
trade_type
>
<![CDATA[JSAPI]]>
</
trade_type
>
</
xml
>
|
return_code 和result_code都为SUCCESS的时候会返回我们需要的prepay_id。。。,然后在jsapi中使用他就可以了。。
转载于:http://www.jb51.net/article/70202.htm