Ver código fonte

微信支付相关代码封装

wangzhijun 1 semana atrás
pai
commit
deb696def9

+ 6 - 0
nightFragrance-common/pom.xml

@@ -206,6 +206,12 @@
             <artifactId>dysmsapi20170525</artifactId>
             <artifactId>dysmsapi20170525</artifactId>
             <version>3.1.0</version>
             <version>3.1.0</version>
         </dependency>
         </dependency>
+
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>wx-java-pay-spring-boot-starter</artifactId>
+            <version>4.5.0</version>
+        </dependency>
     </dependencies>
     </dependencies>
 
 
 </project>
 </project>

+ 82 - 0
nightFragrance-common/src/main/java/com/ylx/common/config/WxPayBeanConfig.java

@@ -0,0 +1,82 @@
+package com.ylx.common.config;
+
+import cn.hutool.core.util.StrUtil;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+
+import java.io.InputStream;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+
+@Slf4j
+@Configuration
+public class WxPayBeanConfig {
+
+    @Autowired
+    private WxPayConfig wxPayConfig;
+    @Autowired
+    private ResourceLoader resourceLoader;
+
+    @Bean
+    public WxPayService wxPayService() throws Exception {
+        // SDK原生配置
+        com.github.binarywang.wxpay.config.WxPayConfig sdkConfig = new com.github.binarywang.wxpay.config.WxPayConfig();
+        sdkConfig.setAppId(wxPayConfig.getAppId());
+        sdkConfig.setMchId(wxPayConfig.getMchId());
+        // v3密钥
+        sdkConfig.setApiV3Key(wxPayConfig.getMchKey());
+
+        if (StrUtil.isBlank(wxPayConfig.getCertKeyPath())) {
+            log.warn("wx.cert-key-path未配置,跳过初始化微信支付服务");
+            return null;
+        }
+
+        // 读取私钥文件 apiclient_key.pem
+        Resource keyRes = resourceLoader.getResource(wxPayConfig.getCertKeyPath());
+        if(keyRes.exists()){
+            try (InputStream is = keyRes.getInputStream()) {
+                PrivateKey privateKey = loadPrivateKey(is);
+                sdkConfig.setPrivateKey(privateKey);
+                log.info("商户私钥文件加载成功:{}",wxPayConfig.getCertKeyPath());
+            } catch (Exception e) {
+                log.error("私钥文件存在但读取失败:{}",wxPayConfig.getCertKeyPath(),e);
+            }
+        }else{
+            // 文件不存在,只打日志,不中断启动
+            log.warn("商户私钥文件【{}】不存在,微信支付功能禁用",wxPayConfig.getCertKeyPath());
+        }
+
+        WxPayService payService = new WxPayServiceImpl();
+        payService.setConfig(sdkConfig);
+        return payService;
+    }
+
+    /** 从pem输入流读取RSA私钥 */
+    private PrivateKey loadPrivateKey(InputStream inputStream) throws Exception {
+        try {
+            // JDK8不能用inputStream.readAllBytes(),替换成IOUtils
+            byte[] bytes = IOUtils.toByteArray(inputStream);
+            String pem = new String(bytes)
+                    .replace("-----BEGIN PRIVATE KEY-----", "")
+                    .replace("-----END PRIVATE KEY-----", "")
+                    .replaceAll("\\s+", ""); // \s+ 清除所有换行、空格
+
+            byte[] decode = Base64.getDecoder().decode(pem);
+            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decode);
+            KeyFactory kf = KeyFactory.getInstance("RSA");
+            return kf.generatePrivate(spec);
+        } finally {
+            // 关闭流,防止资源泄漏
+            IOUtils.closeQuietly(inputStream);
+        }
+    }
+}

+ 84 - 0
nightFragrance-common/src/main/java/com/ylx/common/weixinPay/service/WxPayV3Service.java

@@ -0,0 +1,84 @@
+package com.ylx.common.weixinPay.service;
+
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
+import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
+import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.ylx.common.exception.ServiceException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+@Slf4j
+public class WxPayV3Service {
+
+    @Resource
+    private WxPayService wxPayService;
+
+    /**
+     * V3 JSAPI下单 公众号/小程序
+     */
+    public Map<String, Object> createV3JsapiOrder(String orderNo, BigDecimal amount, String body, String openId) {
+        try {
+            // 1. 获取支付配置
+            WxPayConfig payConfig = wxPayService.getConfig();
+
+            // 2. 组装支付请求
+            WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
+            request.setOutTradeNo(orderNo);
+            request.setDescription(body);
+            request.setNotifyUrl(payConfig.getNotifyUrl());
+
+            // 金额转换(元转分,精度校验)
+            int total = amount.multiply(new BigDecimal(100))
+                    .setScale(0, RoundingMode.HALF_UP).intValue();
+            WxPayUnifiedOrderV3Request.Amount amountObj = new WxPayUnifiedOrderV3Request.Amount();
+            amountObj.setTotal(total);
+            request.setAmount(amountObj);
+
+            // 付款人信息
+            WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
+            payer.setOpenid(openId);
+            request.setPayer(payer);
+
+            // 3. 发起支付
+            WxPayUnifiedOrderV3Result result = wxPayService.createOrderV3(TradeTypeEnum.JSAPI, request);
+            log.info("微信支付预支付成功,prepayId: {}", result.getPrepayId());
+
+            // 4. 生成前端JSAPI支付参数
+            WxPayUnifiedOrderV3Result.JsapiResult jsapi = result.getPayInfo(
+                    TradeTypeEnum.JSAPI,
+                    payConfig.getAppId(),
+                    payConfig.getMchId(),
+                    payConfig.getPrivateKey()
+            );
+
+            // 5. 封装返回结果
+            Map<String, Object> payMap = new HashMap<>();
+            payMap.put("appId", jsapi.getAppId());
+            payMap.put("timeStamp", jsapi.getTimeStamp());
+            payMap.put("nonceStr", jsapi.getNonceStr());
+            payMap.put("package", jsapi.getPackageValue());
+            payMap.put("signType", jsapi.getSignType());
+            payMap.put("paySign", jsapi.getPaySign());
+            return payMap;
+
+        } catch (WxPayException e) {
+            // 微信支付业务异常(如签名错误、余额不足)
+            log.error("微信支付业务异常,订单号: {}", orderNo, e);
+            throw new ServiceException("支付失败:" + e.getMessage());
+        } catch (Exception e) {
+            // 系统级异常(如网络超时、配置错误)
+            log.error("微信支付系统异常,订单号: {}", orderNo, e);
+            throw new ServiceException("支付服务异常,请稍后重试");
+        }
+    }
+}