lin
2024-03-25 9f8f9d13c42ca8cdccbf351069082a5fdccef2e4
src/main/java/org/springblade/modules/pay/service/impl/WxPayServiceImpl.java
@@ -1,25 +1,261 @@
package org.springblade.modules.pay.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.wxpay.sdk.WXPayUtil;
import com.google.gson.Gson;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.common.enums.OrderStatus;
import org.springblade.common.enums.wxpay.WxApiType;
import org.springblade.common.enums.wxpay.WxNotifyType;
import org.springblade.modules.pay.controller.WxPayController;
import org.springblade.modules.pay.entity.WxPayInfo;
import org.springblade.modules.pay.mapper.WxPayMapper;
import org.springblade.modules.pay.service.IWxPayService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
@Service
public class WxPayServiceImpl extends ServiceImpl<WxPayMapper,WxPayInfo> implements IWxPayService {
   private final String  MINI_PROGRAM_APP_ID = "wx41aa8a5d2e565a05";
   private static final Logger logger = LoggerFactory.getLogger(WxPayServiceImpl.class);
   private final ReentrantLock lock = new ReentrantLock();
   @Override
   public Object getOpenId(String code) {
      return null;
   }
   @Override
   public Map<String, String> jsapiPay(Long productId) throws IOException {
      String prepayId = "";
      //生成订单
//      OrderInfo orderInfo = orderInfoService.createOrderByProductId(productId);
      logger.info("调用统一下单API");
//      //调用统一下单API
//      HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.JSAPI_PAY.getType()));
//
//      // 请求body参数
//
//      Gson gson = new Gson();
//      Map paramsMap = new HashMap();
//      paramsMap.put("appid", wxPayConfig.getAppid());
//      paramsMap.put("mchid", wxPayConfig.getMchId());
//      paramsMap.put("description", orderInfo.getTitle());
//      paramsMap.put("out_trade_no", orderInfo.getOrderNo());
//      paramsMap.put("notify_url", wxPayConfig.getNotifyDomain().concat(WxNotifyType.NATIVE_NOTIFY.getType()));
//
//      Map amountMap = new HashMap();
//      amountMap.put("total", orderInfo.getTotalFee());
//      amountMap.put("currency", "CNY");
//
//      paramsMap.put("amount", amountMap);
//
//      Map payerMap = new HashMap();
//      payerMap.put("openid", "oSX3G6OUuGaKJbWzSHUpPbzgtDXo");
//
//      paramsMap.put("payer", payerMap);
//
//      //将参数转换成json字符串
//      String jsonParams = gson.toJson(paramsMap);
//      logger.info("请求参数 ===> {}" + jsonParams);
//
//      StringEntity entity = new StringEntity(jsonParams, "utf-8");
//      entity.setContentType("application/json");
//      httpPost.setEntity(entity);
//      httpPost.setHeader("Accept", "application/json");
//
//      //完成签名并执行请求
//      CloseableHttpResponse response = wxPayClient.execute(httpPost);
//
//      try {
//         String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
//         int statusCode = response.getStatusLine().getStatusCode();//响应状态码
//         if (statusCode == 200) { //处理成功
//            logger.info("成功, 返回结果 = " + bodyAsString);
//         } else if (statusCode == 204) { //处理成功,无返回Body
//            logger.info("成功");
//         } else {
//            logger.info("jszpi下单失败,响应码 = " + statusCode + ",返回结果 = " + bodyAsString);
//            throw new IOException("request failed");
//         }
//         //响应结果
//         Map<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
//         //预支付交易会话标识
//         prepayId = resultMap.get("prepay_id");
//
//         //返回预支付交易会话标识
//         // 组装前端预下单参数
//         /** 返回给前端所需要的数据 */
//         SortedMap<String, String> payMap = new TreeMap<>();
//         String nonceStr = WXPayUtil.generateNonceStr();
//         String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
//         payMap.put("appId", wxPayConfig.getAppid());
//         payMap.put("timeStamp", timestamp);
//         payMap.put("nonceStr", nonceStr);
//         payMap.put("signType", "RSA");
//         payMap.put("package", "prepay_id=" + prepayId);
//         String paySign = getSign(wxPayConfig.getAppid(), prepayId, timestamp, nonceStr);
//         payMap.put("paySign", paySign);
//         logger.info("返回参数 ===> {}" + gson.toJson(payMap));
//         return payMap;
//      } finally {
//         response.close();
//      }
      return null;
   }
   @Override
   public void processOrder(Map<String, Object> bodyMap) throws GeneralSecurityException {
      logger.info("处理订单");
      //解密报文
      String plainText = decryptFromResource(bodyMap);
      //将明文转换成map
      Gson gson = new Gson();
      HashMap plainTextMap = gson.fromJson(plainText, HashMap.class);
      String orderNo = (String) plainTextMap.get("out_trade_no");
        /*在对业务数据进行状态检查和处理之前,
        要采用数据锁进行并发控制,
        以避免函数重入造成的数据混乱*/
      //尝试获取锁:
      // 成功获取则立即返回true,获取失败则立即返回false。不必一直等待锁的释放
//      if (lock.tryLock()) {
//         try {
//            //处理重复的通知
//            //接口调用的幂等性:无论接口被调用多少次,产生的结果是一致的。
//            String orderStatus = orderInfoService.getOrderStatus(orderNo);
//            if (!OrderStatus.NOTPAY.getType().equals(orderStatus)) {
//               return;
//            }
//
//            //模拟通知并发
//            try {
//               TimeUnit.SECONDS.sleep(5);
//            } catch (InterruptedException e) {
//               e.printStackTrace();
//            }
//
//            //更新订单状态
//            orderInfoService.updateStatusByOrderNo(orderNo, OrderStatus.SUCCESS);
//
//            //记录支付日志
//            paymentInfoService.createPaymentInfo(plainText);
//         } finally {
//            //要主动释放锁
//            lock.unlock();
//         }
//      }
   }
   /**
    * 通过prepay_id获取签名
    *
    * @param appid
    * @param prepay_id
    * @param timestamp
    * @param nonceStr
    * @return
    * @throws IOException
    * @throws SignatureException
    * @throws NoSuchAlgorithmException
    * @throws InvalidKeyException
    */
   String getSign(String appid, String prepay_id, String timestamp, String nonceStr) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {
      //从下往上依次生成
      String message = getSignStr(appid, timestamp, nonceStr, "prepay_id=" + prepay_id);
      //签名
      String signature = sign(message.getBytes("utf-8"));
      return signature;
   }
   String sign(byte[] message) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {
      //签名方式
      Signature sign = Signature.getInstance("SHA256withRSA");
      //私钥,通过MyPrivateKey来获取,这是个静态类可以接调用方法 ,需要的是_key.pem文件的绝对路径配上文件名
//      sign.initSign(getPrivateKey(wxPayConfig.getPrivateKeyPath()));
      sign.update(message);
      return Base64.getEncoder().encodeToString(sign.sign());
   }
   /**
    * 按照前端签名文档规范进行排序
    *
    * @param appId
    * @param timeStamp
    * @param nonceStr
    * @param packageValue
    * @return
    */
   private String getSignStr(String appId, String timeStamp, String nonceStr, String packageValue) {
      return String.format("%s\n%s\n%s\n%s\n", appId, timeStamp, nonceStr, packageValue);
   }
   private PrivateKey getPrivateKey(String filename) {
      try {
         FileInputStream fileInputStream = new FileInputStream(filename);
         logger.info("文件内容:" + fileInputStream);
         return PemUtil.loadPrivateKey(fileInputStream);
      } catch (Exception e) {
         logger.info("私钥文件不存在", e);
         throw new RuntimeException("私钥文件不存在", e);
      }
   }
   /**
    * 对称解密
    *
    * @param bodyMap
    * @return
    */
   private String decryptFromResource(Map<String, Object> bodyMap) throws GeneralSecurityException {
      logger.info("密文解密");
      //通知数据
      Map<String, String> resourceMap = (Map) bodyMap.get("resource");
      //数据密文
      String ciphertext = resourceMap.get("ciphertext");
      //随机串
      String nonce = resourceMap.get("nonce");
      //附加数据
      String associatedData = resourceMap.get("associated_data");
      logger.info("密文 ===> {}", ciphertext);
//      AesUtil aesUtil = new AesUtil(wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
//      String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
//         nonce.getBytes(StandardCharsets.UTF_8),
//         ciphertext);
//      logger.info("明文 ===> {}", plainText);
//      return plainText;
      return null;
   }
}