WeChatPayUtil.java 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. package com.sky.utils;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.sky.properties.WeChatProperties;
  5. import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
  6. import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
  7. import org.apache.commons.lang.RandomStringUtils;
  8. import org.apache.http.HttpHeaders;
  9. import org.apache.http.client.methods.CloseableHttpResponse;
  10. import org.apache.http.client.methods.HttpGet;
  11. import org.apache.http.client.methods.HttpPost;
  12. import org.apache.http.entity.ContentType;
  13. import org.apache.http.entity.StringEntity;
  14. import org.apache.http.impl.client.CloseableHttpClient;
  15. import org.apache.http.util.EntityUtils;
  16. import org.springframework.beans.factory.annotation.Autowired;
  17. import org.springframework.stereotype.Component;
  18. import java.io.File;
  19. import java.io.FileInputStream;
  20. import java.io.FileNotFoundException;
  21. import java.math.BigDecimal;
  22. import java.security.PrivateKey;
  23. import java.security.Signature;
  24. import java.security.cert.X509Certificate;
  25. import java.util.ArrayList;
  26. import java.util.Arrays;
  27. import java.util.Base64;
  28. import java.util.List;
  29. /**
  30. * 微信支付工具类
  31. */
  32. @Component
  33. public class WeChatPayUtil {
  34. //微信支付下单接口地址
  35. public static final String JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
  36. //申请退款接口地址
  37. public static final String REFUNDS = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
  38. @Autowired
  39. private WeChatProperties weChatProperties;
  40. /**
  41. * 获取调用微信接口的客户端工具对象
  42. *
  43. * @return
  44. */
  45. private CloseableHttpClient getClient() {
  46. PrivateKey merchantPrivateKey = null;
  47. try {
  48. //merchantPrivateKey商户API私钥,如何加载商户API私钥请看常见问题
  49. merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath())));
  50. //加载平台证书文件
  51. X509Certificate x509Certificate = PemUtil.loadCertificate(new FileInputStream(new File(weChatProperties.getWeChatPayCertFilePath())));
  52. //wechatPayCertificates微信支付平台证书列表。你也可以使用后面章节提到的“定时更新平台证书功能”,而不需要关心平台证书的来龙去脉
  53. List<X509Certificate> wechatPayCertificates = Arrays.asList(x509Certificate);
  54. WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
  55. .withMerchant(weChatProperties.getMchid(), weChatProperties.getMchSerialNo(), merchantPrivateKey)
  56. .withWechatPay(wechatPayCertificates);
  57. // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
  58. CloseableHttpClient httpClient = builder.build();
  59. return httpClient;
  60. } catch (FileNotFoundException e) {
  61. e.printStackTrace();
  62. return null;
  63. }
  64. }
  65. /**
  66. * 发送post方式请求
  67. *
  68. * @param url
  69. * @param body
  70. * @return
  71. */
  72. private String post(String url, String body) throws Exception {
  73. CloseableHttpClient httpClient = getClient();
  74. HttpPost httpPost = new HttpPost(url);
  75. httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
  76. httpPost.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
  77. httpPost.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());
  78. httpPost.setEntity(new StringEntity(body, "UTF-8"));
  79. CloseableHttpResponse response = httpClient.execute(httpPost);
  80. try {
  81. String bodyAsString = EntityUtils.toString(response.getEntity());
  82. return bodyAsString;
  83. } finally {
  84. httpClient.close();
  85. response.close();
  86. }
  87. }
  88. /**
  89. * 发送get方式请求
  90. *
  91. * @param url
  92. * @return
  93. */
  94. private String get(String url) throws Exception {
  95. CloseableHttpClient httpClient = getClient();
  96. HttpGet httpGet = new HttpGet(url);
  97. httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
  98. httpGet.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
  99. httpGet.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());
  100. CloseableHttpResponse response = httpClient.execute(httpGet);
  101. try {
  102. String bodyAsString = EntityUtils.toString(response.getEntity());
  103. return bodyAsString;
  104. } finally {
  105. httpClient.close();
  106. response.close();
  107. }
  108. }
  109. /**
  110. * jsapi下单
  111. *
  112. * @param orderNum 商户订单号
  113. * @param total 总金额
  114. * @param description 商品描述
  115. * @param openid 微信用户的openid
  116. * @return
  117. */
  118. private String jsapi(String orderNum, BigDecimal total, String description, String openid) throws Exception {
  119. JSONObject jsonObject = new JSONObject();
  120. jsonObject.put("appid", weChatProperties.getAppid());
  121. jsonObject.put("mchid", weChatProperties.getMchid());
  122. jsonObject.put("description", description);
  123. jsonObject.put("out_trade_no", orderNum);
  124. jsonObject.put("notify_url", weChatProperties.getNotifyUrl());
  125. JSONObject amount = new JSONObject();
  126. amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
  127. amount.put("currency", "CNY");
  128. jsonObject.put("amount", amount);
  129. JSONObject payer = new JSONObject();
  130. payer.put("openid", openid);
  131. jsonObject.put("payer", payer);
  132. String body = jsonObject.toJSONString();
  133. return post(JSAPI, body);
  134. }
  135. /**
  136. * 小程序支付
  137. *
  138. * @param orderNum 商户订单号
  139. * @param total 金额,单位 元
  140. * @param description 商品描述
  141. * @param openid 微信用户的openid
  142. * @return
  143. */
  144. public JSONObject pay(String orderNum, BigDecimal total, String description, String openid) throws Exception {
  145. //统一下单,生成预支付交易单
  146. String bodyAsString = jsapi(orderNum, total, description, openid);
  147. //解析返回结果
  148. JSONObject jsonObject = JSON.parseObject(bodyAsString);
  149. System.out.println(jsonObject);
  150. String prepayId = jsonObject.getString("prepay_id");
  151. if (prepayId != null) {
  152. String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
  153. String nonceStr = RandomStringUtils.randomNumeric(32);
  154. ArrayList<Object> list = new ArrayList<>();
  155. list.add(weChatProperties.getAppid());
  156. list.add(timeStamp);
  157. list.add(nonceStr);
  158. list.add("prepay_id=" + prepayId);
  159. //二次签名,调起支付需要重新签名
  160. StringBuilder stringBuilder = new StringBuilder();
  161. for (Object o : list) {
  162. stringBuilder.append(o).append("\n");
  163. }
  164. String signMessage = stringBuilder.toString();
  165. byte[] message = signMessage.getBytes();
  166. Signature signature = Signature.getInstance("SHA256withRSA");
  167. signature.initSign(PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath()))));
  168. signature.update(message);
  169. String packageSign = Base64.getEncoder().encodeToString(signature.sign());
  170. //构造数据给微信小程序,用于调起微信支付
  171. JSONObject jo = new JSONObject();
  172. jo.put("timeStamp", timeStamp);
  173. jo.put("nonceStr", nonceStr);
  174. jo.put("package", "prepay_id=" + prepayId);
  175. jo.put("signType", "RSA");
  176. jo.put("paySign", packageSign);
  177. return jo;
  178. }
  179. return jsonObject;
  180. }
  181. /**
  182. * 申请退款
  183. *
  184. * @param outTradeNo 商户订单号
  185. * @param outRefundNo 商户退款单号
  186. * @param refund 退款金额
  187. * @param total 原订单金额
  188. * @return
  189. */
  190. public String refund(String outTradeNo, String outRefundNo, BigDecimal refund, BigDecimal total) throws Exception {
  191. JSONObject jsonObject = new JSONObject();
  192. jsonObject.put("out_trade_no", outTradeNo);
  193. jsonObject.put("out_refund_no", outRefundNo);
  194. JSONObject amount = new JSONObject();
  195. amount.put("refund", refund.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
  196. amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
  197. amount.put("currency", "CNY");
  198. jsonObject.put("amount", amount);
  199. jsonObject.put("notify_url", weChatProperties.getRefundNotifyUrl());
  200. String body = jsonObject.toJSONString();
  201. //调用申请退款接口
  202. return post(REFUNDS, body);
  203. }
  204. }