微信支付工具类:WeChatPayUtil

微信支付工具类:WeChatPayUtil

1.获取调用微信接口的客户端工具对象
2.jsapi下单
3.小程序支付
4.申请退款

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 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 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);
        }
        }

posted @ 2025-07-14 14:08  大树2  阅读(56)  评论(0)    收藏  举报