123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- package com.sky.utils;
- import com.alibaba.fastjson.JSON;
- import com.alibaba.fastjson.JSONObject;
- import com.sky.properties.WeChatProperties;
- import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
- import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
- import org.apache.commons.lang.RandomStringUtils;
- import org.apache.http.HttpHeaders;
- import org.apache.http.client.methods.CloseableHttpResponse;
- import org.apache.http.client.methods.HttpGet;
- import org.apache.http.client.methods.HttpPost;
- import org.apache.http.entity.ContentType;
- import org.apache.http.entity.StringEntity;
- import org.apache.http.impl.client.CloseableHttpClient;
- import org.apache.http.util.EntityUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.math.BigDecimal;
- import java.security.PrivateKey;
- import java.security.Signature;
- import java.security.cert.X509Certificate;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Base64;
- import java.util.List;
- /**
- * 微信支付工具类
- */
- @Component
- public class WeChatPayUtil {
- //微信支付下单接口地址
- public static final String JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
- //申请退款接口地址
- public static final String REFUNDS = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
- @Autowired
- private WeChatProperties weChatProperties;
- /**
- * 获取调用微信接口的客户端工具对象
- *
- * @return
- */
- private CloseableHttpClient getClient() {
- PrivateKey merchantPrivateKey = null;
- try {
- //merchantPrivateKey商户API私钥,如何加载商户API私钥请看常见问题
- merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath())));
- //加载平台证书文件
- X509Certificate x509Certificate = PemUtil.loadCertificate(new FileInputStream(new File(weChatProperties.getWeChatPayCertFilePath())));
- //wechatPayCertificates微信支付平台证书列表。你也可以使用后面章节提到的“定时更新平台证书功能”,而不需要关心平台证书的来龙去脉
- List<X509Certificate> wechatPayCertificates = Arrays.asList(x509Certificate);
- WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
- .withMerchant(weChatProperties.getMchid(), weChatProperties.getMchSerialNo(), merchantPrivateKey)
- .withWechatPay(wechatPayCertificates);
- // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
- CloseableHttpClient httpClient = builder.build();
- return httpClient;
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- return null;
- }
- }
- /**
- * 发送post方式请求
- *
- * @param url
- * @param body
- * @return
- */
- private String post(String url, String body) throws Exception {
- CloseableHttpClient httpClient = getClient();
- HttpPost httpPost = new HttpPost(url);
- httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
- httpPost.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
- httpPost.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());
- httpPost.setEntity(new StringEntity(body, "UTF-8"));
- CloseableHttpResponse response = httpClient.execute(httpPost);
- try {
- String bodyAsString = EntityUtils.toString(response.getEntity());
- return bodyAsString;
- } finally {
- httpClient.close();
- response.close();
- }
- }
- /**
- * 发送get方式请求
- *
- * @param url
- * @return
- */
- private String get(String url) throws Exception {
- CloseableHttpClient httpClient = getClient();
- HttpGet httpGet = new HttpGet(url);
- httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
- httpGet.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
- httpGet.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());
- CloseableHttpResponse response = httpClient.execute(httpGet);
- try {
- String bodyAsString = EntityUtils.toString(response.getEntity());
- return bodyAsString;
- } finally {
- httpClient.close();
- response.close();
- }
- }
- /**
- * jsapi下单
- *
- * @param orderNum 商户订单号
- * @param total 总金额
- * @param description 商品描述
- * @param openid 微信用户的openid
- * @return
- */
- private String jsapi(String orderNum, BigDecimal total, String description, String openid) throws Exception {
- JSONObject jsonObject = new JSONObject();
- jsonObject.put("appid", weChatProperties.getAppid());
- jsonObject.put("mchid", weChatProperties.getMchid());
- jsonObject.put("description", description);
- jsonObject.put("out_trade_no", orderNum);
- jsonObject.put("notify_url", weChatProperties.getNotifyUrl());
- JSONObject amount = new JSONObject();
- amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
- amount.put("currency", "CNY");
- jsonObject.put("amount", amount);
- JSONObject payer = new JSONObject();
- payer.put("openid", openid);
- jsonObject.put("payer", payer);
- String body = jsonObject.toJSONString();
- return post(JSAPI, body);
- }
- /**
- * 小程序支付
- *
- * @param orderNum 商户订单号
- * @param total 金额,单位 元
- * @param description 商品描述
- * @param openid 微信用户的openid
- * @return
- */
- public JSONObject pay(String orderNum, BigDecimal total, String description, String openid) throws Exception {
- //统一下单,生成预支付交易单
- String bodyAsString = jsapi(orderNum, total, description, openid);
- //解析返回结果
- JSONObject jsonObject = JSON.parseObject(bodyAsString);
- System.out.println(jsonObject);
- String prepayId = jsonObject.getString("prepay_id");
- if (prepayId != null) {
- String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
- String nonceStr = RandomStringUtils.randomNumeric(32);
- ArrayList<Object> list = new ArrayList<>();
- list.add(weChatProperties.getAppid());
- list.add(timeStamp);
- list.add(nonceStr);
- list.add("prepay_id=" + prepayId);
- //二次签名,调起支付需要重新签名
- StringBuilder stringBuilder = new StringBuilder();
- for (Object o : list) {
- stringBuilder.append(o).append("\n");
- }
- String signMessage = stringBuilder.toString();
- byte[] message = signMessage.getBytes();
- Signature signature = Signature.getInstance("SHA256withRSA");
- signature.initSign(PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath()))));
- signature.update(message);
- String packageSign = Base64.getEncoder().encodeToString(signature.sign());
- //构造数据给微信小程序,用于调起微信支付
- JSONObject jo = new JSONObject();
- jo.put("timeStamp", timeStamp);
- jo.put("nonceStr", nonceStr);
- jo.put("package", "prepay_id=" + prepayId);
- jo.put("signType", "RSA");
- jo.put("paySign", packageSign);
- return jo;
- }
- return jsonObject;
- }
- /**
- * 申请退款
- *
- * @param outTradeNo 商户订单号
- * @param outRefundNo 商户退款单号
- * @param refund 退款金额
- * @param total 原订单金额
- * @return
- */
- public String refund(String outTradeNo, String outRefundNo, BigDecimal refund, BigDecimal total) throws Exception {
- JSONObject jsonObject = new JSONObject();
- jsonObject.put("out_trade_no", outTradeNo);
- jsonObject.put("out_refund_no", outRefundNo);
- JSONObject amount = new JSONObject();
- amount.put("refund", refund.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
- amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
- amount.put("currency", "CNY");
- jsonObject.put("amount", amount);
- jsonObject.put("notify_url", weChatProperties.getRefundNotifyUrl());
- String body = jsonObject.toJSONString();
- //调用申请退款接口
- return post(REFUNDS, body);
- }
- }
|