Browse Source

微信支付

Gqingci 15 hours ago
parent
commit
a178e92818

+ 10 - 0
ruoyi-modules/ruoyi-main/src/main/java/org/dromara/main/controller/PortalWithdrawController.java

@@ -139,6 +139,16 @@ public class PortalWithdrawController extends BaseController {
         return withdrawService.handleAlipayTransferNotify(params);
         return withdrawService.handleAlipayTransferNotify(params);
     }
     }
 
 
+    /**
+     * 微信转账回调通知
+     * 注意:由于微信没有沙盒环境,此接口暂时注释掉实际逻辑
+     */
+    // @SaIgnore
+    // @PostMapping("/wechat/transfer/notify")
+    // public String wechatTransferNotify(@RequestBody String requestBody) {
+    //     return withdrawService.handleWechatTransferNotify(requestBody);
+    // }
+
     private SysTenantVo getCurrentCompany() {
     private SysTenantVo getCurrentCompany() {
         String tenantId = LoginHelper.getTenantId();
         String tenantId = LoginHelper.getTenantId();
         if (tenantId == null || tenantId.isBlank()) {
         if (tenantId == null || tenantId.isBlank()) {

+ 6 - 0
ruoyi-modules/ruoyi-main/src/main/java/org/dromara/main/service/IPaymentConfigService.java

@@ -14,6 +14,12 @@ public interface IPaymentConfigService {
 
 
     PaymentConfig getEnabledWechatConfig();
     PaymentConfig getEnabledWechatConfig();
 
 
+    /**
+     * 获取启用的微信付款(转账/提现)配置
+     * config_type=2(付款),payment_type=2(微信)
+     */
+    PaymentConfig getEnabledWechatTransferConfig();
+
     PaymentConfigVo getWechatPayConfig();
     PaymentConfigVo getWechatPayConfig();
 
 
     void saveWechatPayConfig(PaymentConfigBo bo);
     void saveWechatPayConfig(PaymentConfigBo bo);

+ 5 - 0
ruoyi-modules/ruoyi-main/src/main/java/org/dromara/main/service/IWithdrawService.java

@@ -23,4 +23,9 @@ public interface IWithdrawService {
     Boolean auditReject(Long id, String auditRemark);
     Boolean auditReject(Long id, String auditRemark);
 
 
     String handleAlipayTransferNotify(Map<String, String> params);
     String handleAlipayTransferNotify(Map<String, String> params);
+
+    /**
+     * 处理微信转账回调通知
+     */
+    String handleWechatTransferNotify(String requestBody);
 }
 }

+ 12 - 0
ruoyi-modules/ruoyi-main/src/main/java/org/dromara/main/service/impl/PaymentConfigServiceImpl.java

@@ -57,6 +57,18 @@ public class PaymentConfigServiceImpl implements IPaymentConfigService {
         );
         );
     }
     }
 
 
+    @Override
+    public PaymentConfig getEnabledWechatTransferConfig() {
+        return paymentConfigMapper.selectOne(
+            Wrappers.<PaymentConfig>lambdaQuery()
+                .eq(PaymentConfig::getConfigType, 2)
+                .eq(PaymentConfig::getPaymentType, 2)
+                .eq(PaymentConfig::getIsEnabled, 1)
+                .orderByDesc(PaymentConfig::getCreateTime)
+                .last("limit 1")
+        );
+    }
+
     @Override
     @Override
     public PaymentConfigVo getWechatPayConfig() {
     public PaymentConfigVo getWechatPayConfig() {
         PaymentConfig config = getOrCreateWechatConfig();
         PaymentConfig config = getOrCreateWechatConfig();

+ 132 - 2
ruoyi-modules/ruoyi-main/src/main/java/org/dromara/main/service/impl/WithdrawServiceImpl.java

@@ -152,6 +152,12 @@ public class WithdrawServiceImpl implements IWithdrawService {
             throw new ServiceException("提现状态不正确,当前状态:{}", withdraw.getWithdrawStatus());
             throw new ServiceException("提现状态不正确,当前状态:{}", withdraw.getWithdrawStatus());
         }
         }
 
 
+        // 查询收款账户,判断提现方式
+        WithdrawAccount account = withdrawAccountMapper.selectById(withdraw.getAccountId());
+        if (account == null) {
+            throw new ServiceException("收款账户不存在");
+        }
+
         withdraw.setWithdrawStatus(1);
         withdraw.setWithdrawStatus(1);
         withdraw.setAuditorId(LoginHelper.getUserId());
         withdraw.setAuditorId(LoginHelper.getUserId());
         withdraw.setAuditTime(new Date());
         withdraw.setAuditTime(new Date());
@@ -159,7 +165,14 @@ public class WithdrawServiceImpl implements IWithdrawService {
         withdrawMapper.updateById(withdraw);
         withdrawMapper.updateById(withdraw);
 
 
         try {
         try {
-            String tradeNo = transferToAlipay(withdraw);
+            String tradeNo;
+            if (Objects.equals(account.getAccountType(), 2)) {
+                // 微信提现
+                tradeNo = transferToWechat(withdraw);
+            } else {
+                // 支付宝提现(默认)
+                tradeNo = transferToAlipay(withdraw);
+            }
 
 
             SysTenant tenant = sysTenantMapper.selectById(withdraw.getCompanyId());
             SysTenant tenant = sysTenantMapper.selectById(withdraw.getCompanyId());
             if (tenant == null) {
             if (tenant == null) {
@@ -174,8 +187,9 @@ public class WithdrawServiceImpl implements IWithdrawService {
             update.setWithdrawingBalance(withdrawingBalance.subtract(withdraw.getWithdrawAmount()));
             update.setWithdrawingBalance(withdrawingBalance.subtract(withdraw.getWithdrawAmount()));
             sysTenantMapper.updateById(update);
             sysTenantMapper.updateById(update);
 
 
+            String channelName = Objects.equals(account.getAccountType(), 2) ? "微信" : "支付宝";
             insertFlow(tenant.getId(), 2, withdraw.getWithdrawAmount(), 3, withdrawingBalance,
             insertFlow(tenant.getId(), 2, withdraw.getWithdrawAmount(), 3, withdrawingBalance,
-                withdrawingBalance.subtract(withdraw.getWithdrawAmount()), 6, withdraw.getId(), withdraw.getWithdrawNo(), "提现成功:" + withdraw.getWithdrawNo());
+                withdrawingBalance.subtract(withdraw.getWithdrawAmount()), 6, withdraw.getId(), withdraw.getWithdrawNo(), channelName + "提现成功:" + withdraw.getWithdrawNo());
 
 
             withdraw.setWithdrawStatus(3);
             withdraw.setWithdrawStatus(3);
             withdraw.setTradeNo(tradeNo);
             withdraw.setTradeNo(tradeNo);
@@ -456,6 +470,122 @@ public class WithdrawServiceImpl implements IWithdrawService {
         throw new ServiceException("支付宝返回错误:{}", response.getSubMsg());
         throw new ServiceException("支付宝返回错误:{}", response.getSubMsg());
     }
     }
 
 
+    /**
+     * 微信商家转账到零钱
+     * 注意:由于微信没有沙盒环境,实际API调用已注释,仅做数据库操作测试
+     */
+    private String transferToWechat(Withdraw withdraw) throws Exception {
+        // ---------- 以下为实际微信转账逻辑,暂时注释掉 ----------
+        /*
+        PaymentConfig config = paymentConfigService.getEnabledWechatTransferConfig();
+        if (config == null) {
+            throw new ServiceException("未找到启用的微信付款配置,请先配置微信付款参数");
+        }
+
+        WithdrawAccount account = withdrawAccountMapper.selectById(withdraw.getAccountId());
+        if (account == null) {
+            throw new ServiceException("收款账户不存在");
+        }
+
+        // 使用 wechatpay-java SDK 发起商家转账
+        // 1. 构建配置
+        RSAAutoCertificateConfig wxConfig = new RSAAutoCertificateConfig.Builder()
+            .merchantId(config.getMchId())
+            .privateKeyFromPath(config.getPrivateKeyPath())
+            .merchantSerialNumber(config.getSerialNo())
+            .apiV3Key(config.getApiV3Key())
+            .build();
+
+        // 2. 构建转账请求
+        com.wechat.pay.java.service.transferbatch.TransferBatchService transferService =
+            new com.wechat.pay.java.service.transferbatch.TransferBatchService.Builder().config(wxConfig).build();
+
+        com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferRequest request =
+            new com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferRequest();
+        request.setAppid(config.getAppId());
+        request.setOutBatchNo(withdraw.getWithdrawNo());
+        request.setBatchName("平台背调佣金提现");
+        request.setBatchRemark("提现单号:" + withdraw.getWithdrawNo());
+        request.setTotalAmount(withdraw.getWithdrawAmount().multiply(new java.math.BigDecimal(100)).longValue());
+        request.setTotalNum(1);
+
+        com.wechat.pay.java.service.transferbatch.model.TransferDetailInput detail =
+            new com.wechat.pay.java.service.transferbatch.model.TransferDetailInput();
+        detail.setOutDetailNo(withdraw.getWithdrawNo() + "D1");
+        detail.setTransferAmount(withdraw.getWithdrawAmount().multiply(new java.math.BigDecimal(100)).longValue());
+        detail.setTransferRemark("佣金提现");
+        detail.setOpenid(account.getAccountNumber()); // 微信账户存的是openid
+        request.setTransferDetailList(java.util.List.of(detail));
+
+        // 3. 发起转账
+        com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferResponse response =
+            transferService.initiateBatchTransfer(request);
+        return response.getBatchId();
+        */
+        // ---------- 以上为实际微信转账逻辑,暂时注释掉 ----------
+
+        // 测试模式:跳过实际微信打款,直接返回模拟交易号
+        return "WX_TEST_" + System.currentTimeMillis();
+    }
+
+    /**
+     * 处理微信转账回调通知
+     * 注意:由于微信没有沙盒环境,此方法暂时注释核心逻辑
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public String handleWechatTransferNotify(String requestBody) {
+        // ---------- 以下为实际微信回调处理逻辑,暂时注释掉 ----------
+        /*
+        try {
+            PaymentConfig config = paymentConfigService.getEnabledWechatTransferConfig();
+            if (config == null) {
+                return "{\"code\":\"FAIL\",\"message\":\"未找到微信付款配置\"}";
+            }
+
+            // 解密回调数据中的 resource 字段,获取转账详情
+            // 根据 out_batch_no 查找提现记录
+            // 根据转账状态更新提现记录
+            // SUCCESS -> 提现成功(状态3)
+            // FAIL -> 提现失败(状态4),退回余额
+
+            JSONObject notify = JSONUtil.parseObj(requestBody);
+            String outBatchNo = notify.getStr("out_batch_no");
+            String batchStatus = notify.getStr("batch_status");
+
+            Withdraw withdraw = withdrawMapper.selectOne(Wrappers.<Withdraw>lambdaQuery()
+                .eq(Withdraw::getWithdrawNo, outBatchNo)
+                .last("limit 1"));
+            if (withdraw == null) {
+                return "{\"code\":\"FAIL\",\"message\":\"提现记录不存在\"}";
+            }
+            if (Objects.equals(withdraw.getWithdrawStatus(), 3)) {
+                return "{\"code\":\"SUCCESS\",\"message\":\"已处理\"}";
+            }
+
+            if ("FINISHED".equals(batchStatus)) {
+                withdraw.setTradeNo(notify.getStr("batch_id"));
+                withdraw.setWithdrawStatus(3);
+                withdraw.setTransferTime(new Date());
+                withdrawMapper.updateById(withdraw);
+            } else if ("CLOSED".equals(batchStatus)) {
+                refundWithdrawToAvailable(withdraw, "微信转账失败,退回可使用余额");
+                withdraw.setWithdrawStatus(4);
+                withdraw.setFailReason("微信转账批次关闭");
+                withdrawMapper.updateById(withdraw);
+            }
+
+            return "{\"code\":\"SUCCESS\",\"message\":\"成功\"}";
+        } catch (Exception e) {
+            return "{\"code\":\"FAIL\",\"message\":\"处理异常\"}";
+        }
+        */
+        // ---------- 以上为实际微信回调处理逻辑,暂时注释掉 ----------
+
+        // 测试模式:直接返回成功
+        return "{\"code\":\"SUCCESS\",\"message\":\"测试模式\"}";
+    }
+
     private void insertFlow(Long companyId, Integer flowType, BigDecimal amount, Integer balanceType,
     private void insertFlow(Long companyId, Integer flowType, BigDecimal amount, Integer balanceType,
                             BigDecimal balanceBefore, BigDecimal balanceAfter, Integer businessType,
                             BigDecimal balanceBefore, BigDecimal balanceAfter, Integer businessType,
                             Long businessId, String businessNo, String remark) {
                             Long businessId, String businessNo, String remark) {