1 package com.foxinmy.weixin4j.mp.api;
2
3 import java.io.BufferedReader;
4 import java.io.BufferedWriter;
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.FileOutputStream;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.io.InputStreamReader;
11 import java.io.OutputStreamWriter;
12 import java.security.KeyStore;
13 import java.security.SecureRandom;
14 import java.security.cert.CertificateFactory;
15 import java.util.Calendar;
16 import java.util.Date;
17 import java.util.HashMap;
18 import java.util.Map;
19
20 import javax.net.ssl.KeyManagerFactory;
21 import javax.net.ssl.SSLContext;
22 import javax.net.ssl.TrustManagerFactory;
23
24 import com.alibaba.fastjson.JSON;
25 import com.alibaba.fastjson.JSONObject;
26 import com.alibaba.fastjson.TypeReference;
27 import com.alibaba.fastjson.parser.Feature;
28 import com.foxinmy.weixin4j.cache.CacheStorager;
29 import com.foxinmy.weixin4j.cache.FileCacheStorager;
30 import com.foxinmy.weixin4j.exception.WeixinException;
31 import com.foxinmy.weixin4j.http.entity.FormUrlEntity;
32 import com.foxinmy.weixin4j.http.weixin.ApiResult;
33 import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
34 import com.foxinmy.weixin4j.model.Token;
35 import com.foxinmy.weixin4j.mp.oldpayment.OrderV2;
36 import com.foxinmy.weixin4j.mp.oldpayment.PayPackageV2;
37 import com.foxinmy.weixin4j.mp.oldpayment.RefundRecordV2;
38 import com.foxinmy.weixin4j.mp.oldpayment.RefundResultV2;
39 import com.foxinmy.weixin4j.mp.oldpayment.WeixinOldPayAccount;
40 import com.foxinmy.weixin4j.mp.oldpayment.WeixinOldPaymentSignature;
41 import com.foxinmy.weixin4j.mp.token.WeixinTokenCreator;
42 import com.foxinmy.weixin4j.payment.PayRequest;
43 import com.foxinmy.weixin4j.sign.WeixinPaymentSignature;
44 import com.foxinmy.weixin4j.sign.WeixinSignature;
45 import com.foxinmy.weixin4j.token.TokenManager;
46 import com.foxinmy.weixin4j.type.IdQuery;
47 import com.foxinmy.weixin4j.type.SignType;
48 import com.foxinmy.weixin4j.type.mch.BillType;
49 import com.foxinmy.weixin4j.type.mch.RefundType;
50 import com.foxinmy.weixin4j.util.Consts;
51 import com.foxinmy.weixin4j.util.DateUtil;
52 import com.foxinmy.weixin4j.util.DigestUtil;
53 import com.foxinmy.weixin4j.util.MapUtil;
54 import com.foxinmy.weixin4j.util.RandomUtil;
55 import com.foxinmy.weixin4j.util.StringUtil;
56 import com.foxinmy.weixin4j.util.Weixin4jConfigUtil;
57 import com.foxinmy.weixin4j.xml.ListsuffixResultDeserializer;
58
59
60
61
62
63
64
65
66
67
68 public class PayOldApi extends MpApi {
69
70 private final WeixinOldPayAccount weixinPayAccount;
71 private final TokenManager tokenManager;
72 private final WeixinSignature weixinMD5Signature;
73 private final WeixinOldPaymentSignature weixinSignature;
74
75
76
77
78 public PayOldApi() {
79 this(new FileCacheStorager<Token>());
80 }
81
82
83
84
85 public PayOldApi(CacheStorager<Token> cacheStorager) {
86 this(JSON.parseObject(Weixin4jConfigUtil.getValue("account"),
87 WeixinOldPayAccount.class), cacheStorager);
88 }
89
90
91
92
93
94
95
96
97
98 public PayOldApi(WeixinOldPayAccount weixinPayAccount,
99 CacheStorager<Token> cacheStorager) {
100 if (weixinPayAccount == null) {
101 throw new IllegalArgumentException(
102 "weixinPayAccount must not be empty");
103 }
104 if (cacheStorager == null) {
105 throw new IllegalArgumentException(
106 "cacheStorager must not be empty");
107 }
108 this.weixinPayAccount = weixinPayAccount;
109 this.tokenManager = new TokenManager(new WeixinTokenCreator(
110 weixinPayAccount.getId(), weixinPayAccount.getSecret()),
111 cacheStorager);
112 this.weixinMD5Signature = new WeixinPaymentSignature(
113 weixinPayAccount.getPartnerKey());
114 this.weixinSignature = new WeixinOldPaymentSignature(
115 weixinPayAccount.getPaySignKey(),
116 weixinPayAccount.getPartnerKey());
117 }
118
119
120
121
122
123
124 public WeixinOldPayAccount getWeixinPayAccount() {
125 return weixinPayAccount;
126 }
127
128
129
130
131
132
133 public TokenManager getTokenManager() {
134 return this.tokenManager;
135 }
136
137
138
139
140
141
142 public WeixinOldPaymentSignature getWeixinPaymentSignature() {
143 return this.weixinSignature;
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161 public String createPayJsRequestJson(String body, String outTradeNo,
162 double totalFee, String notifyUrl, String createIp) {
163 PayPackageV2 payPackage = new PayPackageV2(
164 weixinPayAccount.getPartnerId(), body, outTradeNo, totalFee,
165 notifyUrl, createIp);
166 return createPayJsRequestJson(payPackage);
167 }
168
169
170
171
172
173
174
175
176 public String createPayJsRequestJson(PayPackageV2 payPackage) {
177 PayRequest payRequest = new PayRequest(weixinPayAccount.getId(),
178 weixinSignature.sign(payPackage));
179 payRequest.setPaySign(weixinSignature.sign(payRequest));
180 payRequest.setSignType(SignType.SHA1);
181 return JSON.toJSONString(payRequest);
182 }
183
184
185
186
187
188
189
190
191 public String createNativePayRequestURL(String productId) {
192 Map<String, String> map = new HashMap<String, String>();
193 String timestamp = DateUtil.timestamp2string();
194 String noncestr = RandomUtil.generateString(16);
195 map.put("appid", weixinPayAccount.getId());
196 map.put("timestamp", timestamp);
197 map.put("noncestr", noncestr);
198 map.put("productid", productId);
199 map.put("appkey", weixinPayAccount.getPaySignKey());
200 String sign = weixinSignature.sign(map);
201 String nativepay_uri = getRequestUri("nativepay_old_uri");
202 return String.format(nativepay_uri, sign, weixinPayAccount.getId(),
203 productId, timestamp, noncestr);
204 }
205
206
207
208
209
210
211
212
213
214
215
216 public OrderV2 queryOrder(IdQuery idQuery) throws WeixinException {
217 String orderquery_uri = getRequestUri("orderquery_old_uri");
218 Token token = tokenManager.getCache();
219 StringBuilder sb = new StringBuilder();
220 sb.append(idQuery.getType().getName()).append("=")
221 .append(idQuery.getId());
222 sb.append("&partner=").append(weixinPayAccount.getPartnerId());
223 String part = sb.toString();
224 sb.append("&key=").append(weixinPayAccount.getPartnerKey());
225 String sign = DigestUtil.MD5(sb.toString()).toUpperCase();
226 sb.delete(0, sb.length());
227 sb.append(part).append("&sign=").append(sign);
228
229 String timestamp = DateUtil.timestamp2string();
230 JSONObject obj = new JSONObject();
231 obj.put("appid", weixinPayAccount.getId());
232 obj.put("appkey", weixinPayAccount.getPaySignKey());
233 obj.put("package", sb.toString());
234 obj.put("timestamp", timestamp);
235 String signature = weixinSignature.sign(obj);
236
237 obj.clear();
238 obj.put("appid", weixinPayAccount.getId());
239 obj.put("package", sb.toString());
240 obj.put("timestamp", timestamp);
241 obj.put("app_signature", signature);
242 obj.put("sign_method", SignType.SHA1.name().toLowerCase());
243
244 WeixinResponse response = weixinExecutor.post(
245 String.format(orderquery_uri, token.getAccessToken()),
246 obj.toJSONString());
247
248 String order_info = response.getAsJson().getString("order_info");
249 OrderV2 order = JSON.parseObject(order_info, OrderV2.class,
250 Feature.IgnoreNotMatch);
251 if (order.getRetCode() != 0) {
252 throw new WeixinException(Integer.toString(order.getRetCode()),
253 order.getRetMsg());
254 }
255 return order;
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
284
285
286 protected RefundResultV2 applyRefund(InputStream certificate,
287 IdQuery idQuery, String outRefundNo, double totalFee,
288 double refundFee, String opUserId, Map<String, String> mopara)
289 throws WeixinException {
290 String refund_uri = getRequestUri("refundapply_old_uri");
291 WeixinResponse response = null;
292 try {
293 Map<String, String> map = new HashMap<String, String>();
294 map.put("input_charset", Consts.UTF_8.name());
295
296
297
298 map.put("service_version", "1.1");
299 map.put("partner", weixinPayAccount.getPartnerId());
300 map.put("out_refund_no", outRefundNo);
301 map.put("total_fee",
302 Integer.toString(DateUtil.formatYuan2Fen(totalFee)));
303 map.put("refund_fee",
304 Integer.toString(DateUtil.formatYuan2Fen(refundFee)));
305 map.put(idQuery.getType().getName(), idQuery.getId());
306 if (StringUtil.isBlank(opUserId)) {
307 opUserId = weixinPayAccount.getPartnerId();
308 }
309 map.put("op_user_id", opUserId);
310 if (mopara != null && !mopara.isEmpty()) {
311 map.putAll(mopara);
312 }
313 String sign = weixinMD5Signature.sign(map);
314 map.put("sign", sign.toUpperCase());
315
316 SSLContext ctx = null;
317 KeyStore ks = null;
318 String jksPwd = "";
319 File jksFile = new File(String.format("%s%stenpay_cacert.jks",
320 System.getProperty("java.io.tmpdir"), File.separator));
321
322 if (!jksFile.exists()) {
323 CertificateFactory cf = CertificateFactory
324 .getInstance(Consts.X509);
325 java.security.cert.Certificate cert = cf
326 .generateCertificate(PayOldApi.class
327 .getResourceAsStream("cacert.pem"));
328 ks = KeyStore.getInstance(Consts.JKS);
329 ks.load(null, null);
330 ks.setCertificateEntry("tenpay", cert);
331 FileOutputStream os = new FileOutputStream(jksFile);
332 ks.store(os, jksPwd.toCharArray());
333 os.close();
334 }
335
336 TrustManagerFactory tmf = TrustManagerFactory
337 .getInstance(Consts.SunX509);
338 ks = KeyStore.getInstance(Consts.JKS);
339 FileInputStream is = new FileInputStream(jksFile);
340 ks.load(is, jksPwd.toCharArray());
341 tmf.init(ks);
342 is.close();
343
344 KeyManagerFactory kmf = KeyManagerFactory
345 .getInstance(Consts.SunX509);
346 ks = KeyStore.getInstance(Consts.PKCS12);
347 ks.load(certificate, weixinPayAccount.getPartnerId().toCharArray());
348 kmf.init(ks, weixinPayAccount.getPartnerId().toCharArray());
349
350 ctx = SSLContext.getInstance(Consts.TLS);
351 ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
352 new SecureRandom());
353 response = weixinExecutor.createSSLRequestExecutor(ctx).get(
354 String.format("%s?%s", refund_uri,
355 FormUrlEntity.formatParameters(map)));
356 } catch (WeixinException e) {
357 throw e;
358 } catch (Exception e) {
359 throw new WeixinException(e);
360 } finally {
361 if (certificate != null) {
362 try {
363 certificate.close();
364 } catch (IOException e) {
365 ;
366 }
367 }
368 }
369 return response.getAsObject(new TypeReference<RefundResultV2>() {
370 });
371 }
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393 public RefundResultV2 applyRefund(InputStream certificate, IdQuery idQuery,
394 String outRefundNo, double totalFee, double refundFee,
395 String opUserId, String opUserPasswd) throws WeixinException {
396 Map<String, String> mopara = new HashMap<String, String>();
397 mopara.put("op_user_passwd", DigestUtil.MD5(opUserPasswd));
398 return applyRefund(certificate, idQuery, outRefundNo, totalFee,
399 refundFee, opUserId, mopara);
400 }
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432 public RefundResultV2 applyRefund(InputStream certificate, IdQuery idQuery,
433 String outRefundNo, double totalFee, double refundFee,
434 String opUserId, String opUserPasswd, String recvUserId,
435 String reccvUserName, RefundType refundType) throws WeixinException {
436 Map<String, String> mopara = new HashMap<String, String>();
437 mopara.put("op_user_passwd", DigestUtil.MD5(opUserPasswd));
438 if (StringUtil.isNotBlank(recvUserId)) {
439 mopara.put("recv_user_id", recvUserId);
440 }
441 if (StringUtil.isNotBlank(reccvUserName)) {
442 mopara.put("reccv_user_name", reccvUserName);
443 }
444 if (refundType != null) {
445 mopara.put("refund_type", Integer.toString(refundType.getVal()));
446 }
447 return applyRefund(certificate, idQuery, outRefundNo, totalFee,
448 refundFee, opUserId, mopara);
449 }
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469 public File downloadBill(Date billDate, BillType billType, String billPath)
470 throws WeixinException {
471 if (billDate == null) {
472 Calendar now = Calendar.getInstance();
473 now.add(Calendar.DAY_OF_MONTH, -1);
474 billDate = now.getTime();
475 }
476 if (billType == null) {
477 billType = BillType.ALL;
478 }
479 String formatBillDate = DateUtil.fortmat2yyyyMMdd(billDate);
480 String fileName = String.format("weixin4j_bill_%s_%s_%s.txt",
481 formatBillDate, billType.name().toLowerCase(),
482 weixinPayAccount.getId());
483 File file = new File(String.format("%s%s%s", billPath, File.separator,
484 fileName));
485 if (file.exists()) {
486 return file;
487 }
488 String downloadbill_uri = getRequestUri("downloadbill_old_uri");
489
490 Map<String, String> map = new HashMap<String, String>();
491 map.put("spid", weixinPayAccount.getPartnerId());
492 map.put("trans_time", DateUtil.fortmat2yyyy_MM_dd(billDate));
493 map.put("stamp", DateUtil.timestamp2string());
494 map.put("cft_signtype", "0");
495 map.put("mchtype", Integer.toString(billType.getVal()));
496 map.put("key", weixinPayAccount.getPartnerKey());
497 String sign = DigestUtil.MD5(MapUtil.toJoinString(map, false, false));
498 map.put("sign", sign.toLowerCase());
499 WeixinResponse response = weixinExecutor.get(String.format("%s?%s",
500 downloadbill_uri, FormUrlEntity.formatParameters(map)));
501 BufferedReader reader = null;
502 BufferedWriter writer = null;
503 try {
504 writer = new BufferedWriter(new OutputStreamWriter(
505 new FileOutputStream(file), Consts.GBK));
506 reader = new BufferedReader(new InputStreamReader(
507 response.getBody(), com.foxinmy.weixin4j.util.Consts.GBK));
508 String line = null;
509 while ((line = reader.readLine()) != null) {
510 writer.write(line);
511 writer.newLine();
512 }
513 } catch (IOException e) {
514 throw new WeixinException(e);
515 } finally {
516 try {
517 if (reader != null) {
518 reader.close();
519 }
520 if (writer != null) {
521 writer.close();
522 }
523 } catch (IOException ignore) {
524 ;
525 }
526 }
527 return file;
528 }
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543 public RefundRecordV2 queryRefund(IdQuery idQuery) throws WeixinException {
544 String refundquery_uri = getRequestUri("refundquery_old_uri");
545 Map<String, String> map = new HashMap<String, String>();
546 map.put("input_charset", Consts.UTF_8.name());
547 map.put("partner", weixinPayAccount.getPartnerId());
548 map.put(idQuery.getType().getName(), idQuery.getId());
549 String sign = weixinMD5Signature.sign(map);
550 map.put("sign", sign.toLowerCase());
551 WeixinResponse response = weixinExecutor.get(String.format(
552 refundquery_uri, FormUrlEntity.formatParameters(map)));
553 return ListsuffixResultDeserializer.deserialize(response.getAsString(),
554 RefundRecordV2.class);
555 }
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573 public ApiResult deliverNotify(String openId, String transid,
574 String outTradeNo, boolean status, String statusMsg)
575 throws WeixinException {
576 String delivernotify_uri = getRequestUri("delivernotify_old_uri");
577 Token token = tokenManager.getCache();
578
579 Map<String, String> map = new HashMap<String, String>();
580 map.put("appid", weixinPayAccount.getId());
581 map.put("appkey", weixinPayAccount.getPaySignKey());
582 map.put("openid", openId);
583 map.put("transid", transid);
584 map.put("out_trade_no", outTradeNo);
585 map.put("deliver_timestamp", DateUtil.timestamp2string());
586 map.put("deliver_status", status ? "1" : "0");
587 map.put("deliver_msg", statusMsg);
588 map.put("app_signature", weixinSignature.sign(map));
589 map.put("sign_method", SignType.SHA1.name().toLowerCase());
590
591 WeixinResponse response = weixinExecutor.post(
592 String.format(delivernotify_uri, token.getAccessToken()),
593 JSON.toJSONString(map));
594 return response.getAsResult();
595 }
596
597
598
599
600
601
602
603
604
605
606
607 public ApiResult updateFeedback(String openId, String feedbackId)
608 throws WeixinException {
609 String payfeedback_uri = getRequestUri("payfeedback_old_uri");
610 Token token = tokenManager.getCache();
611 WeixinResponse response = weixinExecutor.get(String.format(
612 payfeedback_uri, token.getAccessToken(), openId, feedbackId));
613 return response.getAsResult();
614 }
615 }