微信转账未完成

This commit is contained in:
痴货 2024-10-01 12:16:10 +08:00
parent 2c4d8298cf
commit 3b9548aa2d
8 changed files with 207 additions and 11 deletions

View File

@ -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.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
@Tag(name = "管理后台 - 佣金提现") @Tag(name = "管理后台 - 佣金提现")
@RestController @RestController
@ -41,7 +42,7 @@ public class BrokerageWithdrawController {
@Operation(summary = "通过申请") @Operation(summary = "通过申请")
@PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')") @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')")
public CommonResult<Boolean> approveBrokerageWithdraw(@RequestParam("id") Integer id) { public CommonResult<Boolean> approveBrokerageWithdraw(@RequestParam("id") Integer id) {
brokerageWithdrawService.auditBrokerageWithdraw(id, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, ""); brokerageWithdrawService.auditBrokerageWithdraw(id, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, "", getClientIP());
return success(true); return success(true);
} }
@ -49,7 +50,7 @@ public class BrokerageWithdrawController {
@Operation(summary = "驳回申请") @Operation(summary = "驳回申请")
@PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')") @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')")
public CommonResult<Boolean> rejectBrokerageWithdraw(@Valid @RequestBody BrokerageWithdrawRejectReqVO reqVO) { public CommonResult<Boolean> 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); return success(true);
} }

View File

@ -28,7 +28,7 @@ public interface BrokerageWithdrawService {
* @param status 审核状态 * @param status 审核状态
* @param auditReason 驳回原因 * @param auditReason 驳回原因
*/ */
void auditBrokerageWithdraw(Integer id, BrokerageWithdrawStatusEnum status, String auditReason); void auditBrokerageWithdraw(Integer id, BrokerageWithdrawStatusEnum status, String auditReason, String userIp);
/** /**
* 获得佣金提现 * 获得佣金提现

View File

@ -4,20 +4,29 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil; 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.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; 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.NotifyMessageSendApi;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; 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.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO; 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.convert.brokerage.BrokerageWithdrawConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO; 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.dataobject.config.TradeConfigDO;
import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.BrokerageWithdrawMapper; 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.MessageTemplateConstants;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; 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.BrokerageWithdrawStatusEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum; 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.brokerage.bo.BrokerageWithdrawSummaryRespBO;
import cn.iocoder.yudao.module.trade.service.config.TradeConfigService; import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -27,12 +36,10 @@ import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.Validator; import jakarta.validation.Validator;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Collection; import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 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.*; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
/** /**
@ -46,6 +53,8 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
@Resource @Resource
private BrokerageWithdrawMapper brokerageWithdrawMapper; private BrokerageWithdrawMapper brokerageWithdrawMapper;
@Resource
private TradeNoRedisDAO tradeNoRedisDAO;
@Resource @Resource
private BrokerageRecordService brokerageRecordService; private BrokerageRecordService brokerageRecordService;
@ -54,15 +63,23 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
@Resource @Resource
private NotifyMessageSendApi notifyMessageSendApi; private NotifyMessageSendApi notifyMessageSendApi;
@Resource
private PayTransferApi payTransferApi;
@Resource
private SocialUserApi socialUserApi;
@Resource @Resource
private Validator validator; private Validator validator;
@Resource
private TradeOrderProperties tradeOrderProperties;
@Override @Override
@Transactional(rollbackFor = Exception.class) @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 校验存在 // 1.1 校验存在
BrokerageWithdrawDO withdraw = validateBrokerageWithdrawExists(id); BrokerageWithdrawDO withdraw = validateBrokerageWithdrawExists(id);
// 1.2 校验状态为审核中 // 1.2 校验状态为审核中
if (ObjectUtil.notEqual(BrokerageWithdrawStatusEnum.AUDITING.getStatus(), withdraw.getStatus())) { if (ObjectUtil.notEqual(BrokerageWithdrawStatusEnum.AUDITING.getStatus(), withdraw.getStatus())) {
throw exception(BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING); throw exception(BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING);
@ -81,6 +98,12 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
// 3.1 通过时佣金转余额 // 3.1 通过时佣金转余额
if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(withdraw.getType())) { if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(withdraw.getType())) {
// todo 疯狂 // 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 疯狂调用转账接口 // TODO 疯狂调用转账接口
} else if (BrokerageWithdrawStatusEnum.AUDIT_FAIL.equals(status)) { } else if (BrokerageWithdrawStatusEnum.AUDIT_FAIL.equals(status)) {
@ -102,6 +125,19 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
.setUserId(withdraw.getUserId()).setTemplateCode(templateCode).setTemplateParams(templateParams)); .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) { private BrokerageWithdrawDO validateBrokerageWithdrawExists(Integer id) {
BrokerageWithdrawDO withdraw = brokerageWithdrawMapper.selectById(id); BrokerageWithdrawDO withdraw = brokerageWithdrawMapper.selectById(id);
if (withdraw == null) { if (withdraw == null) {

View File

@ -96,13 +96,15 @@ public class PayTransferServiceImpl implements PayTransferService {
transfer = INSTANCE.convert(reqDTO) transfer = INSTANCE.convert(reqDTO)
.setChannelId(channel.getId()) .setChannelId(channel.getId())
.setNo(no).setStatus(WAITING.getStatus()) .setNo(no).setStatus(WAITING.getStatus())
.setNotifyUrl(payApp.getTransferNotifyUrl()); .setNotifyUrl(payApp.getTransferNotifyUrl())
.setAppId(channel.getAppId());
transferMapper.insert(transfer); transferMapper.insert(transfer);
} }
try { try {
// 3. 调用三方渠道发起转账 // 3. 调用三方渠道发起转账
PayTransferUnifiedReqDTO transferUnifiedReq = INSTANCE.convert2(transfer) PayTransferUnifiedReqDTO transferUnifiedReq = INSTANCE.convert2(transfer)
.setOutTransferNo(transfer.getNo()); .setOutTransferNo(transfer.getNo());
transferUnifiedReq.setNotifyUrl(payApp.getTransferNotifyUrl());
PayTransferRespDTO unifiedTransferResp = client.unifiedTransfer(transferUnifiedReq); PayTransferRespDTO unifiedTransferResp = client.unifiedTransfer(transferUnifiedReq);
// 4. 通知转账结果 // 4. 通知转账结果
getSelf().notifyTransfer(channel, unifiedTransferResp); getSelf().notifyTransfer(channel, unifiedTransferResp);

View File

@ -64,6 +64,10 @@
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId> <artifactId>weixin-java-pay</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
</dependency>
<!-- Test 测试相关 --> <!-- Test 测试相关 -->
<dependency> <dependency>
@ -71,6 +75,7 @@
<artifactId>yudao-spring-boot-starter-test</artifactId> <artifactId>yudao-spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -9,6 +9,8 @@ import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import org.hibernate.validator.constraints.URL;
import java.util.Map; import java.util.Map;
import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.*; import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.*;
@ -75,4 +77,11 @@ public class PayTransferUnifiedReqDTO {
* 支付渠道的额外参数 * 支付渠道的额外参数
*/ */
private Map<String, String> channelExtras; private Map<String, String> channelExtras;
/**
* 转账结果的 notify 回调地址
*/
@NotEmpty(message = "转账结果的回调地址不能为空")
@URL(message = "转账结果的 notify 回调地址必须是 URL 格式")
private String notifyUrl;
} }

View File

@ -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.client.impl.AbstractPayClient;
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; 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.WxPayNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result; import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.request.*;
import com.github.binarywang.wxpay.bean.result.*; 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.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException; 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.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -426,8 +433,26 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
} }
@Override @Override
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) { protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws WxPayException {
throw new UnsupportedOperationException("待实现"); TransferService transferService = client.getTransferService();
List<TransferBatchesRequest.TransferDetail> 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 @Override

View File

@ -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<TransferDetail> 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;
}
}