diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageWithdrawController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageWithdrawController.java index bb35c0d7f4..8e33e403d8 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageWithdrawController.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageWithdrawController.java @@ -24,6 +24,7 @@ import java.util.Map; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; @Tag(name = "管理后台 - 佣金提现") @RestController @@ -41,7 +42,7 @@ public class BrokerageWithdrawController { @Operation(summary = "通过申请") @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')") public CommonResult approveBrokerageWithdraw(@RequestParam("id") Integer id) { - brokerageWithdrawService.auditBrokerageWithdraw(id, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, ""); + brokerageWithdrawService.auditBrokerageWithdraw(id, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, "", getClientIP()); return success(true); } @@ -49,7 +50,7 @@ public class BrokerageWithdrawController { @Operation(summary = "驳回申请") @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')") public CommonResult rejectBrokerageWithdraw(@Valid @RequestBody BrokerageWithdrawRejectReqVO reqVO) { - brokerageWithdrawService.auditBrokerageWithdraw(reqVO.getId(), BrokerageWithdrawStatusEnum.AUDIT_FAIL, reqVO.getAuditReason()); + brokerageWithdrawService.auditBrokerageWithdraw(reqVO.getId(), BrokerageWithdrawStatusEnum.AUDIT_FAIL, reqVO.getAuditReason(), getClientIP()); return success(true); } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawService.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawService.java index 04ea9c49bc..865b4c97b1 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawService.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawService.java @@ -28,7 +28,7 @@ public interface BrokerageWithdrawService { * @param status 审核状态 * @param auditReason 驳回原因 */ - void auditBrokerageWithdraw(Integer id, BrokerageWithdrawStatusEnum status, String auditReason); + void auditBrokerageWithdraw(Integer id, BrokerageWithdrawStatusEnum status, String auditReason, String userIp); /** * 获得佣金提现 diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java index 86814f8a55..0b6d88bfc0 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java @@ -4,20 +4,29 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.module.pay.api.transfer.PayTransferApi; +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum; import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi; import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; +import cn.iocoder.yudao.module.system.api.social.SocialUserApi; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO; import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO; import cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageWithdrawConvert; import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO; import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO; import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.BrokerageWithdrawMapper; +import cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO; import cn.iocoder.yudao.module.trade.enums.MessageTemplateConstants; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum; import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum; +import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO; import cn.iocoder.yudao.module.trade.service.config.TradeConfigService; import org.springframework.stereotype.Service; @@ -27,12 +36,10 @@ import org.springframework.validation.annotation.Validated; import jakarta.annotation.Resource; import jakarta.validation.Validator; import java.time.LocalDateTime; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO.TRADE_ORDER_NO_PREFIX; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; /** @@ -46,6 +53,8 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @Resource private BrokerageWithdrawMapper brokerageWithdrawMapper; + @Resource + private TradeNoRedisDAO tradeNoRedisDAO; @Resource private BrokerageRecordService brokerageRecordService; @@ -54,15 +63,23 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @Resource private NotifyMessageSendApi notifyMessageSendApi; + @Resource + private PayTransferApi payTransferApi; + @Resource + private SocialUserApi socialUserApi; @Resource private Validator validator; + @Resource + private TradeOrderProperties tradeOrderProperties; + @Override @Transactional(rollbackFor = Exception.class) - public void auditBrokerageWithdraw(Integer id, BrokerageWithdrawStatusEnum status, String auditReason) { + public void auditBrokerageWithdraw(Integer id, BrokerageWithdrawStatusEnum status, String auditReason, String userIp) { // 1.1 校验存在 BrokerageWithdrawDO withdraw = validateBrokerageWithdrawExists(id); + // 1.2 校验状态为审核中 if (ObjectUtil.notEqual(BrokerageWithdrawStatusEnum.AUDITING.getStatus(), withdraw.getStatus())) { throw exception(BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING); @@ -81,6 +98,12 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { // 3.1 通过时佣金转余额 if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(withdraw.getType())) { // todo 疯狂: + }else if (BrokerageWithdrawTypeEnum.WECHAT.getType().equals(withdraw.getType())){ + //获取openid + SocialUserRespDTO socialUser = socialUserApi.getSocialUserByUserId(UserTypeEnum.MEMBER.getValue(), withdraw.getUserId(), SocialTypeEnum.WECHAT_MINI_APP.getType()); + // 微信提现 + PayTransferCreateReqDTO payTransferCreateReqDTO = getPayTransferCreateReqDTO(userIp, withdraw, socialUser); + payTransferApi.createTransfer(payTransferCreateReqDTO); } // TODO 疯狂:调用转账接口 } else if (BrokerageWithdrawStatusEnum.AUDIT_FAIL.equals(status)) { @@ -102,6 +125,19 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { .setUserId(withdraw.getUserId()).setTemplateCode(templateCode).setTemplateParams(templateParams)); } + private PayTransferCreateReqDTO getPayTransferCreateReqDTO(String userIp, BrokerageWithdrawDO withdraw, SocialUserRespDTO socialUser) { + PayTransferCreateReqDTO payTransferCreateReqDTO = new PayTransferCreateReqDTO(); + payTransferCreateReqDTO.setAppKey(tradeOrderProperties.getPayAppKey()); + payTransferCreateReqDTO.setChannelCode("wx_lite"); + payTransferCreateReqDTO.setUserIp(userIp); + payTransferCreateReqDTO.setType(PayTransferTypeEnum.WX_BALANCE.getType()); + payTransferCreateReqDTO.setMerchantTransferId(withdraw.getId().toString()); + payTransferCreateReqDTO.setPrice(withdraw.getPrice()); + payTransferCreateReqDTO.setSubject("佣金提现"); + payTransferCreateReqDTO.setOpenid(socialUser.getOpenid()); + return payTransferCreateReqDTO; + } + private BrokerageWithdrawDO validateBrokerageWithdrawExists(Integer id) { BrokerageWithdrawDO withdraw = brokerageWithdrawMapper.selectById(id); if (withdraw == null) { diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java index 5ace5ab4be..04d86d1848 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java @@ -96,13 +96,15 @@ public class PayTransferServiceImpl implements PayTransferService { transfer = INSTANCE.convert(reqDTO) .setChannelId(channel.getId()) .setNo(no).setStatus(WAITING.getStatus()) - .setNotifyUrl(payApp.getTransferNotifyUrl()); + .setNotifyUrl(payApp.getTransferNotifyUrl()) + .setAppId(channel.getAppId()); transferMapper.insert(transfer); } try { // 3. 调用三方渠道发起转账 PayTransferUnifiedReqDTO transferUnifiedReq = INSTANCE.convert2(transfer) .setOutTransferNo(transfer.getNo()); + transferUnifiedReq.setNotifyUrl(payApp.getTransferNotifyUrl()); PayTransferRespDTO unifiedTransferResp = client.unifiedTransfer(transferUnifiedReq); // 4. 通知转账结果 getSelf().notifyTransfer(channel, unifiedTransferResp); diff --git a/yudao-module-pay/yudao-spring-boot-starter-biz-pay/pom.xml b/yudao-module-pay/yudao-spring-boot-starter-biz-pay/pom.xml index 93193d008e..c214af0f50 100644 --- a/yudao-module-pay/yudao-spring-boot-starter-biz-pay/pom.xml +++ b/yudao-module-pay/yudao-spring-boot-starter-biz-pay/pom.xml @@ -64,6 +64,10 @@ com.github.binarywang weixin-java-pay + + com.github.binarywang + wx-java-miniapp-spring-boot-starter + @@ -71,6 +75,7 @@ yudao-spring-boot-starter-test test + diff --git a/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferUnifiedReqDTO.java b/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferUnifiedReqDTO.java index 43afa86a30..61501cc58b 100644 --- a/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferUnifiedReqDTO.java +++ b/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferUnifiedReqDTO.java @@ -9,6 +9,8 @@ import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; +import org.hibernate.validator.constraints.URL; + import java.util.Map; import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.*; @@ -75,4 +77,11 @@ public class PayTransferUnifiedReqDTO { * 支付渠道的额外参数 */ private Map channelExtras; + + /** + * 转账结果的 notify 回调地址 + */ + @NotEmpty(message = "转账结果的回调地址不能为空") + @URL(message = "转账结果的 notify 回调地址必须是 URL 格式") + private String notifyUrl; } diff --git a/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java b/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java index 298e314d85..f5c753b8e3 100644 --- a/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java +++ b/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java @@ -17,20 +17,27 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifie import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; +import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties; import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result; import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result; import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.result.*; +import com.github.binarywang.wxpay.bean.transfer.TransferBatchesRequest; +import com.github.binarywang.wxpay.bean.transfer.TransferBatchesResult; import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.TransferService; import com.github.binarywang.wxpay.service.WxPayService; import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import java.time.LocalDateTime; import java.time.ZoneId; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -426,8 +433,26 @@ public abstract class AbstractWxPayClient extends AbstractPayClient transferDetailList = new ArrayList<>(); + transferDetailList.add(TransferBatchesRequest.TransferDetail.newBuilder() + .outDetailNo(reqDTO.getOutTransferNo()) + .transferAmount(reqDTO.getPrice()) + .transferRemark(reqDTO.getSubject()) + .openid(reqDTO.getOpenid()) + .build()); + TransferBatchesRequest transferBatches = TransferBatchesRequest.newBuilder() + .appid(this.config.getAppId()) + .outBatchNo(reqDTO.getOutTransferNo()) + .batchName(reqDTO.getSubject()) + .batchRemark(reqDTO.getSubject()) + .totalAmount(reqDTO.getPrice()) + .totalNum(1) + .transferDetailList(transferDetailList).build(); + transferBatches.setNotifyUrl(reqDTO.getNotifyUrl()); + TransferBatchesResult transferBatchesResult = transferService.transferBatches(transferBatches); + return PayTransferRespDTO.waitingOf(reqDTO.getOutTransferNo(), transferBatchesResult.getBatchId() ,transferBatchesResult); } @Override diff --git a/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesRequest.java b/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesRequest.java new file mode 100644 index 0000000000..72296daa83 --- /dev/null +++ b/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesRequest.java @@ -0,0 +1,118 @@ +package com.github.binarywang.wxpay.bean.transfer; + +import com.github.binarywang.wxpay.v3.SpecEncrypt; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 发起商家转账API参数 + * + * @author zhongjun + * created on 2022/6/17 + **/ +@Data +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +public class TransferBatchesRequest implements Serializable { + private static final long serialVersionUID = -2175582517588397426L; + + /** + * 直连商户的appid + */ + @SerializedName("appid") + private String appid; + + /** + * 商家批次单号 + */ + @SerializedName("out_batch_no") + private String outBatchNo; + + /** + * 批次名称 + */ + @SerializedName("batch_name") + private String batchName; + + /** + * 批次备注 + */ + @SerializedName("batch_remark") + private String batchRemark; + + /** + * 转账总金额 + */ + @SerializedName("total_amount") + private Integer totalAmount; + + /** + * 转账总笔数 + */ + @SerializedName("total_num") + private Integer totalNum; + + /** + * 转账明细列表 + */ + @SpecEncrypt + @SerializedName("transfer_detail_list") + private List transferDetailList; + + /** + * 转账场景ID + */ + @SerializedName("transfer_scene_id") + private String transferSceneId; + + /** + * 通知地址 说明:异步接收微信支付结果通知的回调地址,通知url必须为公网可访问的url,必须为https,不能携带参数。 + */ + @SerializedName("notify_url") + private String notifyUrl; + + @Data + @Builder(builderMethodName = "newBuilder") + @AllArgsConstructor + @NoArgsConstructor + public static class TransferDetail { + + /** + * 商家明细单号 + */ + @SerializedName("out_detail_no") + private String outDetailNo; + + /** + * 转账金额 + */ + @SerializedName("transfer_amount") + private Integer transferAmount; + + /** + * 转账备注 + */ + @SerializedName("transfer_remark") + private String transferRemark; + + /** + * 用户在直连商户应用下的用户标示 + */ + @SerializedName("openid") + private String openid; + + /** + * 收款用户姓名 + */ + @SpecEncrypt + @SerializedName("user_name") + private String userName; + } +}