Commit 602bd5f93c4d5fa50d6669c0c5d0daf624090b55
Committed by
Gitee
!1099 【新增功能】添加微信转账到零钱提现功能
Merge pull request !1099 from 痴货/jh-wxzz
Showing
46 changed files
with
746 additions
and
49 deletions
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageWithdrawTypeEnum.java
| ... | ... | @@ -19,6 +19,7 @@ public enum BrokerageWithdrawTypeEnum implements IntArrayValuable { |
| 19 | 19 | BANK(2, "银行卡"), |
| 20 | 20 | WECHAT(3, "微信"), |
| 21 | 21 | ALIPAY(4, "支付宝"), |
| 22 | + ALIPAY_SMALL(5, "微信零钱"), | |
| 22 | 23 | ; |
| 23 | 24 | |
| 24 | 25 | public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageWithdrawTypeEnum::getType).toArray(); | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageRecordController.java
| ... | ... | @@ -44,7 +44,7 @@ public class BrokerageRecordController { |
| 44 | 44 | @Operation(summary = "获得佣金记录") |
| 45 | 45 | @Parameter(name = "id", description = "编号", required = true, example = "1024") |
| 46 | 46 | @PreAuthorize("@ss.hasPermission('trade:brokerage-record:query')") |
| 47 | - public CommonResult<BrokerageRecordRespVO> getBrokerageRecord(@RequestParam("id") Integer id) { | |
| 47 | + public CommonResult<BrokerageRecordRespVO> getBrokerageRecord(@RequestParam("id") Long id) { | |
| 48 | 48 | BrokerageRecordDO brokerageRecord = brokerageRecordService.getBrokerageRecord(id); |
| 49 | 49 | return success(BrokerageRecordConvert.INSTANCE.convert(brokerageRecord)); |
| 50 | 50 | } | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageWithdrawController.java
| ... | ... | @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 5 | import cn.iocoder.yudao.module.member.api.user.MemberUserApi; |
| 6 | 6 | import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; |
| 7 | +import cn.iocoder.yudao.module.pay.api.notify.dto.PayTransferNotifyReqDTO; | |
| 7 | 8 | import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRejectReqVO; |
| 8 | 9 | import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO; |
| 9 | 10 | import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRespVO; |
| ... | ... | @@ -14,6 +15,8 @@ import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService; |
| 14 | 15 | import io.swagger.v3.oas.annotations.Operation; |
| 15 | 16 | import io.swagger.v3.oas.annotations.Parameter; |
| 16 | 17 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 18 | +import jakarta.annotation.security.PermitAll; | |
| 19 | +import lombok.extern.slf4j.Slf4j; | |
| 17 | 20 | import org.springframework.security.access.prepost.PreAuthorize; |
| 18 | 21 | import org.springframework.validation.annotation.Validated; |
| 19 | 22 | import org.springframework.web.bind.annotation.*; |
| ... | ... | @@ -24,11 +27,13 @@ import java.util.Map; |
| 24 | 27 | |
| 25 | 28 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 26 | 29 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| 30 | +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; | |
| 27 | 31 | |
| 28 | 32 | @Tag(name = "管理后台 - 佣金提现") |
| 29 | 33 | @RestController |
| 30 | 34 | @RequestMapping("/trade/brokerage-withdraw") |
| 31 | 35 | @Validated |
| 36 | +@Slf4j | |
| 32 | 37 | public class BrokerageWithdrawController { |
| 33 | 38 | |
| 34 | 39 | @Resource |
| ... | ... | @@ -40,8 +45,8 @@ public class BrokerageWithdrawController { |
| 40 | 45 | @PutMapping("/approve") |
| 41 | 46 | @Operation(summary = "通过申请") |
| 42 | 47 | @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')") |
| 43 | - public CommonResult<Boolean> approveBrokerageWithdraw(@RequestParam("id") Integer id) { | |
| 44 | - brokerageWithdrawService.auditBrokerageWithdraw(id, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, ""); | |
| 48 | + public CommonResult<Boolean> approveBrokerageWithdraw(@RequestParam("id") Long id) { | |
| 49 | + brokerageWithdrawService.auditBrokerageWithdraw(id, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, "", getClientIP()); | |
| 45 | 50 | return success(true); |
| 46 | 51 | } |
| 47 | 52 | |
| ... | ... | @@ -49,7 +54,7 @@ public class BrokerageWithdrawController { |
| 49 | 54 | @Operation(summary = "驳回申请") |
| 50 | 55 | @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')") |
| 51 | 56 | public CommonResult<Boolean> rejectBrokerageWithdraw(@Valid @RequestBody BrokerageWithdrawRejectReqVO reqVO) { |
| 52 | - brokerageWithdrawService.auditBrokerageWithdraw(reqVO.getId(), BrokerageWithdrawStatusEnum.AUDIT_FAIL, reqVO.getAuditReason()); | |
| 57 | + brokerageWithdrawService.auditBrokerageWithdraw(reqVO.getId(), BrokerageWithdrawStatusEnum.AUDIT_FAIL, reqVO.getAuditReason(), getClientIP()); | |
| 53 | 58 | return success(true); |
| 54 | 59 | } |
| 55 | 60 | |
| ... | ... | @@ -57,7 +62,7 @@ public class BrokerageWithdrawController { |
| 57 | 62 | @Operation(summary = "获得佣金提现") |
| 58 | 63 | @Parameter(name = "id", description = "编号", required = true, example = "1024") |
| 59 | 64 | @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:query')") |
| 60 | - public CommonResult<BrokerageWithdrawRespVO> getBrokerageWithdraw(@RequestParam("id") Integer id) { | |
| 65 | + public CommonResult<BrokerageWithdrawRespVO> getBrokerageWithdraw(@RequestParam("id") Long id) { | |
| 61 | 66 | BrokerageWithdrawDO brokerageWithdraw = brokerageWithdrawService.getBrokerageWithdraw(id); |
| 62 | 67 | return success(BrokerageWithdrawConvert.INSTANCE.convert(brokerageWithdraw)); |
| 63 | 68 | } |
| ... | ... | @@ -75,4 +80,15 @@ public class BrokerageWithdrawController { |
| 75 | 80 | return success(BrokerageWithdrawConvert.INSTANCE.convertPage(pageResult, userMap)); |
| 76 | 81 | } |
| 77 | 82 | |
| 83 | + @PostMapping("/update-transfer") | |
| 84 | + @Operation(summary = "更新转账订单为转账成功") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob | |
| 85 | + @PermitAll // 无需登录,安全由 PayDemoOrderService 内部校验实现 | |
| 86 | + public CommonResult<Boolean> updateAfterRefund(@RequestBody PayTransferNotifyReqDTO notifyReqDTO) { | |
| 87 | + // 目前业务逻辑,不需要做任何事情 | |
| 88 | + // 当然,退款会有小概率会失败的情况,可以监控失败状态,进行告警 | |
| 89 | + log.info("[updateAfterRefund][notifyReqDTO({})]", notifyReqDTO); | |
| 90 | + brokerageWithdrawService.updateTransfer(Long.parseLong(notifyReqDTO.getMerchantTransferId()), notifyReqDTO.getPayTransferId()); | |
| 91 | + return success(true); | |
| 92 | + } | |
| 93 | + | |
| 78 | 94 | } | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/record/BrokerageRecordRespVO.java
| ... | ... | @@ -14,7 +14,7 @@ import java.time.LocalDateTime; |
| 14 | 14 | public class BrokerageRecordRespVO extends BrokerageRecordBaseVO { |
| 15 | 15 | |
| 16 | 16 | @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28896") |
| 17 | - private Integer id; | |
| 17 | + private Long id; | |
| 18 | 18 | |
| 19 | 19 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 20 | 20 | private LocalDateTime createTime; | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawRejectReqVO.java
| ... | ... | @@ -14,7 +14,7 @@ public class BrokerageWithdrawRejectReqVO { |
| 14 | 14 | |
| 15 | 15 | @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "7161") |
| 16 | 16 | @NotNull(message = "编号不能为空") |
| 17 | - private Integer id; | |
| 17 | + private Long id; | |
| 18 | 18 | |
| 19 | 19 | @Schema(description = "审核驳回原因", example = "不对") |
| 20 | 20 | @NotEmpty(message = "审核驳回原因不能为空") | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawRespVO.java
| ... | ... | @@ -14,7 +14,7 @@ import java.time.LocalDateTime; |
| 14 | 14 | public class BrokerageWithdrawRespVO extends BrokerageWithdrawBaseVO { |
| 15 | 15 | |
| 16 | 16 | @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "7161") |
| 17 | - private Integer id; | |
| 17 | + private Long id; | |
| 18 | 18 | |
| 19 | 19 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 20 | 20 | private LocalDateTime createTime; | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageWithdrawMapper.java
| ... | ... | @@ -35,7 +35,7 @@ public interface BrokerageWithdrawMapper extends BaseMapperX<BrokerageWithdrawDO |
| 35 | 35 | .orderByAsc(BrokerageWithdrawDO::getStatus).orderByDesc(BrokerageWithdrawDO::getId)); |
| 36 | 36 | } |
| 37 | 37 | |
| 38 | - default int updateByIdAndStatus(Integer id, Integer status, BrokerageWithdrawDO updateObj) { | |
| 38 | + default int updateByIdAndStatus(Long id, Integer status, BrokerageWithdrawDO updateObj) { | |
| 39 | 39 | return update(updateObj, new LambdaUpdateWrapper<BrokerageWithdrawDO>() |
| 40 | 40 | .eq(BrokerageWithdrawDO::getId, id) |
| 41 | 41 | .eq(BrokerageWithdrawDO::getStatus, status)); | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/job/order/TradeOrderAutoCommentJob.java
| ... | ... | @@ -21,7 +21,7 @@ public class TradeOrderAutoCommentJob implements JobHandler { |
| 21 | 21 | @Override |
| 22 | 22 | @TenantJob |
| 23 | 23 | public String execute(String param) { |
| 24 | - int count = tradeOrderUpdateService.createOrderItemCommentBySystem(); | |
| 24 | + int count = tradeOrderUpdateService.createOrderItemCommentBySystem(); | |
| 25 | 25 | return String.format("评论订单 %s 个", count); |
| 26 | 26 | } |
| 27 | 27 | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordService.java
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordServiceImpl.java
| ... | ... | @@ -64,7 +64,7 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService { |
| 64 | 64 | private ProductSkuApi productSkuApi; |
| 65 | 65 | |
| 66 | 66 | @Override |
| 67 | - public BrokerageRecordDO getBrokerageRecord(Integer id) { | |
| 67 | + public BrokerageRecordDO getBrokerageRecord(Long id) { | |
| 68 | 68 | return brokerageRecordMapper.selectById(id); |
| 69 | 69 | } |
| 70 | 70 | ... | ... |
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 { |
| 28 | 28 | * @param status 审核状态 |
| 29 | 29 | * @param auditReason 驳回原因 |
| 30 | 30 | */ |
| 31 | - void auditBrokerageWithdraw(Integer id, BrokerageWithdrawStatusEnum status, String auditReason); | |
| 31 | + void auditBrokerageWithdraw(Long id, BrokerageWithdrawStatusEnum status, String auditReason, String userIp); | |
| 32 | 32 | |
| 33 | 33 | /** |
| 34 | 34 | * 获得佣金提现 |
| ... | ... | @@ -36,7 +36,7 @@ public interface BrokerageWithdrawService { |
| 36 | 36 | * @param id 编号 |
| 37 | 37 | * @return 佣金提现 |
| 38 | 38 | */ |
| 39 | - BrokerageWithdrawDO getBrokerageWithdraw(Integer id); | |
| 39 | + BrokerageWithdrawDO getBrokerageWithdraw(Long id); | |
| 40 | 40 | |
| 41 | 41 | /** |
| 42 | 42 | * 获得佣金提现分页 |
| ... | ... | @@ -77,4 +77,10 @@ public interface BrokerageWithdrawService { |
| 77 | 77 | return convertMap(getWithdrawSummaryListByUserId(userIds, status), BrokerageWithdrawSummaryRespBO::getUserId); |
| 78 | 78 | } |
| 79 | 79 | |
| 80 | + /** | |
| 81 | + * | |
| 82 | + * @param merchantTransferId 提现编号 | |
| 83 | + * @param payTransferId 转账订单编号 | |
| 84 | + */ | |
| 85 | + void updateTransfer(Long merchantTransferId, Long payTransferId); | |
| 80 | 86 | } | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java
| ... | ... | @@ -4,20 +4,35 @@ import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.date.LocalDateTimeUtil; |
| 5 | 5 | import cn.hutool.core.map.MapUtil; |
| 6 | 6 | import cn.hutool.core.util.ObjectUtil; |
| 7 | +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | |
| 7 | 8 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 8 | 9 | import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; |
| 10 | +import cn.iocoder.yudao.module.pay.api.transfer.PayTransferApi; | |
| 11 | +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; | |
| 12 | +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferRespDTO; | |
| 13 | +import cn.iocoder.yudao.module.pay.api.wallet.PayWalletApi; | |
| 14 | +import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletCreateReqDto; | |
| 15 | +import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletRespDTO; | |
| 16 | +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum; | |
| 17 | +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum; | |
| 18 | +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; | |
| 9 | 19 | import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi; |
| 10 | 20 | import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; |
| 21 | +import cn.iocoder.yudao.module.system.api.social.SocialUserApi; | |
| 22 | +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; | |
| 23 | +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; | |
| 11 | 24 | import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO; |
| 12 | 25 | import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO; |
| 13 | 26 | import cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageWithdrawConvert; |
| 14 | 27 | import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO; |
| 15 | 28 | import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO; |
| 16 | 29 | import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.BrokerageWithdrawMapper; |
| 30 | +import cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO; | |
| 17 | 31 | import cn.iocoder.yudao.module.trade.enums.MessageTemplateConstants; |
| 18 | 32 | import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; |
| 19 | 33 | import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum; |
| 20 | 34 | import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum; |
| 35 | +import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; | |
| 21 | 36 | import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO; |
| 22 | 37 | import cn.iocoder.yudao.module.trade.service.config.TradeConfigService; |
| 23 | 38 | import org.springframework.stereotype.Service; |
| ... | ... | @@ -27,10 +42,7 @@ import org.springframework.validation.annotation.Validated; |
| 27 | 42 | import jakarta.annotation.Resource; |
| 28 | 43 | import jakarta.validation.Validator; |
| 29 | 44 | import java.time.LocalDateTime; |
| 30 | -import java.util.Collection; | |
| 31 | -import java.util.Collections; | |
| 32 | -import java.util.List; | |
| 33 | -import java.util.Map; | |
| 45 | +import java.util.*; | |
| 34 | 46 | |
| 35 | 47 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 36 | 48 | import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; |
| ... | ... | @@ -46,6 +58,8 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { |
| 46 | 58 | |
| 47 | 59 | @Resource |
| 48 | 60 | private BrokerageWithdrawMapper brokerageWithdrawMapper; |
| 61 | + @Resource | |
| 62 | + private TradeNoRedisDAO tradeNoRedisDAO; | |
| 49 | 63 | |
| 50 | 64 | @Resource |
| 51 | 65 | private BrokerageRecordService brokerageRecordService; |
| ... | ... | @@ -54,15 +68,25 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { |
| 54 | 68 | |
| 55 | 69 | @Resource |
| 56 | 70 | private NotifyMessageSendApi notifyMessageSendApi; |
| 71 | + @Resource | |
| 72 | + private PayTransferApi payTransferApi; | |
| 73 | + @Resource | |
| 74 | + private SocialUserApi socialUserApi; | |
| 75 | + @Resource | |
| 76 | + private PayWalletApi payWalletApi; | |
| 57 | 77 | |
| 58 | 78 | @Resource |
| 59 | 79 | private Validator validator; |
| 60 | 80 | |
| 81 | + @Resource | |
| 82 | + private TradeOrderProperties tradeOrderProperties; | |
| 83 | + | |
| 61 | 84 | @Override |
| 62 | 85 | @Transactional(rollbackFor = Exception.class) |
| 63 | - public void auditBrokerageWithdraw(Integer id, BrokerageWithdrawStatusEnum status, String auditReason) { | |
| 86 | + public void auditBrokerageWithdraw(Long id, BrokerageWithdrawStatusEnum status, String auditReason, String userIp) { | |
| 64 | 87 | // 1.1 校验存在 |
| 65 | 88 | BrokerageWithdrawDO withdraw = validateBrokerageWithdrawExists(id); |
| 89 | + | |
| 66 | 90 | // 1.2 校验状态为审核中 |
| 67 | 91 | if (ObjectUtil.notEqual(BrokerageWithdrawStatusEnum.AUDITING.getStatus(), withdraw.getStatus())) { |
| 68 | 92 | throw exception(BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING); |
| ... | ... | @@ -80,9 +104,25 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { |
| 80 | 104 | templateCode = MessageTemplateConstants.SMS_BROKERAGE_WITHDRAW_AUDIT_APPROVE; |
| 81 | 105 | // 3.1 通过时佣金转余额 |
| 82 | 106 | if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(withdraw.getType())) { |
| 83 | - // todo 疯狂: | |
| 107 | + PayWalletRespDTO wallet = payWalletApi.getWalletByUserId(withdraw.getUserId()); | |
| 108 | + payWalletApi.addWallet(new PayWalletCreateReqDto() | |
| 109 | + .setWalletId(wallet.getId()) | |
| 110 | + .setBizType(PayWalletBizTypeEnum.WITHDRAW) | |
| 111 | + .setBizId(withdraw.getId().toString()) | |
| 112 | + .setPrice(withdraw.getPrice()) | |
| 113 | + .setTitle("分佣提现")); | |
| 114 | + rows = brokerageWithdrawMapper.updateByIdAndStatus(id, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.getStatus(), | |
| 115 | + new BrokerageWithdrawDO().setStatus(BrokerageWithdrawStatusEnum.WITHDRAW_SUCCESS.getStatus()).setAuditReason(auditReason).setAuditTime(LocalDateTime.now())); | |
| 116 | + if (rows == 0) { | |
| 117 | + throw exception(BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING); | |
| 118 | + } | |
| 119 | + }else if (BrokerageWithdrawTypeEnum.ALIPAY_SMALL.getType().equals(withdraw.getType())){ | |
| 120 | + //获取openid | |
| 121 | + SocialUserRespDTO socialUser = socialUserApi.getSocialUserByUserId(UserTypeEnum.MEMBER.getValue(), withdraw.getUserId(), SocialTypeEnum.WECHAT_MINI_APP.getType()); | |
| 122 | + // 微信提现 | |
| 123 | + PayTransferCreateReqDTO payTransferCreateReqDTO = getPayTransferCreateReqDTO(userIp, withdraw, socialUser); | |
| 124 | + payTransferApi.createTransfer(payTransferCreateReqDTO); | |
| 84 | 125 | } |
| 85 | - // TODO 疯狂:调用转账接口 | |
| 86 | 126 | } else if (BrokerageWithdrawStatusEnum.AUDIT_FAIL.equals(status)) { |
| 87 | 127 | templateCode = MessageTemplateConstants.SMS_BROKERAGE_WITHDRAW_AUDIT_REJECT; |
| 88 | 128 | // 3.2 驳回时需要退还用户佣金 |
| ... | ... | @@ -93,16 +133,29 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { |
| 93 | 133 | } |
| 94 | 134 | |
| 95 | 135 | // 4. 通知用户 |
| 96 | - Map<String, Object> templateParams = MapUtil.<String, Object>builder() | |
| 97 | - .put("createTime", LocalDateTimeUtil.formatNormal(withdraw.getCreateTime())) | |
| 98 | - .put("price", MoneyUtils.fenToYuanStr(withdraw.getPrice())) | |
| 99 | - .put("reason", auditReason) | |
| 100 | - .build(); | |
| 101 | - notifyMessageSendApi.sendSingleMessageToMember(new NotifySendSingleToUserReqDTO() | |
| 102 | - .setUserId(withdraw.getUserId()).setTemplateCode(templateCode).setTemplateParams(templateParams)); | |
| 136 | +// Map<String, Object> templateParams = MapUtil.<String, Object>builder() | |
| 137 | +// .put("createTime", LocalDateTimeUtil.formatNormal(withdraw.getCreateTime())) | |
| 138 | +// .put("price", MoneyUtils.fenToYuanStr(withdraw.getPrice())) | |
| 139 | +// .put("reason", auditReason) | |
| 140 | +// .build(); | |
| 141 | +// notifyMessageSendApi.sendSingleMessageToMember(new NotifySendSingleToUserReqDTO() | |
| 142 | +// .setUserId(withdraw.getUserId()).setTemplateCode(templateCode).setTemplateParams(templateParams)); | |
| 143 | + } | |
| 144 | + | |
| 145 | + private PayTransferCreateReqDTO getPayTransferCreateReqDTO(String userIp, BrokerageWithdrawDO withdraw, SocialUserRespDTO socialUser) { | |
| 146 | + PayTransferCreateReqDTO payTransferCreateReqDTO = new PayTransferCreateReqDTO(); | |
| 147 | + payTransferCreateReqDTO.setAppKey(tradeOrderProperties.getPayAppKey()); | |
| 148 | + payTransferCreateReqDTO.setChannelCode("wx_lite"); | |
| 149 | + payTransferCreateReqDTO.setUserIp(userIp); | |
| 150 | + payTransferCreateReqDTO.setType(PayTransferTypeEnum.WX_BALANCE.getType()); | |
| 151 | + payTransferCreateReqDTO.setMerchantTransferId(withdraw.getId().toString()); | |
| 152 | + payTransferCreateReqDTO.setPrice(withdraw.getPrice()); | |
| 153 | + payTransferCreateReqDTO.setSubject("佣金提现"); | |
| 154 | + payTransferCreateReqDTO.setOpenid(socialUser.getOpenid()); | |
| 155 | + return payTransferCreateReqDTO; | |
| 103 | 156 | } |
| 104 | 157 | |
| 105 | - private BrokerageWithdrawDO validateBrokerageWithdrawExists(Integer id) { | |
| 158 | + private BrokerageWithdrawDO validateBrokerageWithdrawExists(Long id) { | |
| 106 | 159 | BrokerageWithdrawDO withdraw = brokerageWithdrawMapper.selectById(id); |
| 107 | 160 | if (withdraw == null) { |
| 108 | 161 | throw exception(BROKERAGE_WITHDRAW_NOT_EXISTS); |
| ... | ... | @@ -111,7 +164,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { |
| 111 | 164 | } |
| 112 | 165 | |
| 113 | 166 | @Override |
| 114 | - public BrokerageWithdrawDO getBrokerageWithdraw(Integer id) { | |
| 167 | + public BrokerageWithdrawDO getBrokerageWithdraw(Long id) { | |
| 115 | 168 | return brokerageWithdrawMapper.selectById(id); |
| 116 | 169 | } |
| 117 | 170 | |
| ... | ... | @@ -150,6 +203,23 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { |
| 150 | 203 | return brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userIds, status.getStatus()); |
| 151 | 204 | } |
| 152 | 205 | |
| 206 | + @Override | |
| 207 | + public void updateTransfer(Long id, Long payTransferId) { | |
| 208 | + BrokerageWithdrawDO withdraw = validateBrokerageWithdrawExists(id); | |
| 209 | + PayTransferRespDTO transfer = payTransferApi.getTransfer(payTransferId); | |
| 210 | + if(PayTransferStatusEnum.isSuccess(transfer.getStatus())){ | |
| 211 | + withdraw.setStatus(BrokerageWithdrawStatusEnum.WITHDRAW_SUCCESS.getStatus()); | |
| 212 | + }else if(PayTransferStatusEnum.isPendingStatus(transfer.getStatus())){ | |
| 213 | + withdraw.setStatus(BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.getStatus()); | |
| 214 | + }else{ | |
| 215 | + withdraw.setStatus(BrokerageWithdrawStatusEnum.WITHDRAW_FAIL.getStatus()); | |
| 216 | + // 3.2 驳回时需要退还用户佣金 | |
| 217 | + brokerageRecordService.addBrokerage(withdraw.getUserId(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT, | |
| 218 | + String.valueOf(withdraw.getId()), withdraw.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT.getTitle()); | |
| 219 | + } | |
| 220 | + brokerageWithdrawMapper.updateById(withdraw); | |
| 221 | + } | |
| 222 | + | |
| 153 | 223 | /** |
| 154 | 224 | * 计算提现手续费 |
| 155 | 225 | * | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/PayTransferApi.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.pay.api.transfer; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; |
| 4 | 4 | |
| 5 | +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferRespDTO; | |
| 5 | 6 | import jakarta.validation.Valid; |
| 6 | 7 | |
| 7 | 8 | /** |
| ... | ... | @@ -19,4 +20,10 @@ public interface PayTransferApi { |
| 19 | 20 | */ |
| 20 | 21 | Long createTransfer(@Valid PayTransferCreateReqDTO reqDTO); |
| 21 | 22 | |
| 23 | + /** | |
| 24 | + * 获取转账单详细 | |
| 25 | + * @param id 转账单编号 | |
| 26 | + * @return 转账单详细 | |
| 27 | + */ | |
| 28 | + PayTransferRespDTO getTransfer(Long id); | |
| 22 | 29 | } | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/dto/PayTransferRespDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.pay.api.transfer.dto; | |
| 2 | + | |
| 3 | +import lombok.Data; | |
| 4 | + | |
| 5 | +@Data | |
| 6 | +public class PayTransferRespDTO { | |
| 7 | + | |
| 8 | + /** | |
| 9 | + * 编号 | |
| 10 | + */ | |
| 11 | + private Long id; | |
| 12 | + | |
| 13 | + /** | |
| 14 | + * 转账单号 | |
| 15 | + * | |
| 16 | + */ | |
| 17 | + private String no; | |
| 18 | + | |
| 19 | + /** | |
| 20 | + * 转账金额,单位:分 | |
| 21 | + */ | |
| 22 | + private Integer price; | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * 转账状态 | |
| 26 | + * | |
| 27 | + * 枚举 {@link PayTransferStatusRespEnum} | |
| 28 | + */ | |
| 29 | + private Integer status; | |
| 30 | + | |
| 31 | + | |
| 32 | +} | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/wallet/PayWalletApi.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.pay.api.wallet; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletCreateReqDto; | |
| 4 | +import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletRespDTO; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 钱包 API 接口 | |
| 8 | + * | |
| 9 | + * @author liurulin | |
| 10 | + */ | |
| 11 | +public interface PayWalletApi { | |
| 12 | + | |
| 13 | + /** | |
| 14 | + * 添加钱包 | |
| 15 | + * @param reqDTO 创建请求 | |
| 16 | + */ | |
| 17 | + void addWallet(PayWalletCreateReqDto reqDTO); | |
| 18 | + | |
| 19 | + /** | |
| 20 | + * 根据用户id获取钱包信息 | |
| 21 | + * @param userId 用户id | |
| 22 | + * @return 钱包信息 | |
| 23 | + */ | |
| 24 | + PayWalletRespDTO getWalletByUserId(Long userId); | |
| 25 | +} | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/wallet/dto/PayWalletCreateReqDto.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.pay.api.wallet.dto; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; | |
| 4 | +import jakarta.validation.constraints.NotNull; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +@Data | |
| 8 | +public class PayWalletCreateReqDto { | |
| 9 | + | |
| 10 | + /** | |
| 11 | + * 钱包编号 | |
| 12 | + */ | |
| 13 | + @NotNull(message = "钱包编号不能为空") | |
| 14 | + private Long walletId; | |
| 15 | + | |
| 16 | + /** | |
| 17 | + * 关联业务分类 | |
| 18 | + */ | |
| 19 | + @NotNull(message = "关联业务分类不能为空") | |
| 20 | + private PayWalletBizTypeEnum bizType; | |
| 21 | + | |
| 22 | + /** | |
| 23 | + * 关联业务编号 | |
| 24 | + */ | |
| 25 | + @NotNull(message = "关联业务编号不能为空") | |
| 26 | + private String bizId; | |
| 27 | + | |
| 28 | + /** | |
| 29 | + * 流水说明 | |
| 30 | + */ | |
| 31 | + @NotNull(message = "流水说明不能为空") | |
| 32 | + private String title; | |
| 33 | + | |
| 34 | + /** | |
| 35 | + * 交易金额,单位分 | |
| 36 | + * | |
| 37 | + * 正值表示余额增加,负值表示余额减少 | |
| 38 | + */ | |
| 39 | + @NotNull(message = "交易金额不能为空") | |
| 40 | + private Integer price; | |
| 41 | + | |
| 42 | +} | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/wallet/dto/PayWalletRespDTO.java
0 → 100644
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/wallet/PayWalletBizTypeEnum.java
| ... | ... | @@ -19,7 +19,8 @@ public enum PayWalletBizTypeEnum implements IntArrayValuable { |
| 19 | 19 | RECHARGE_REFUND(2, "充值退款"), |
| 20 | 20 | PAYMENT(3, "支付"), |
| 21 | 21 | PAYMENT_REFUND(4, "支付退款"), |
| 22 | - UPDATE_BALANCE(5, "更新余额"); | |
| 22 | + UPDATE_BALANCE(5, "更新余额"), | |
| 23 | + WITHDRAW(6, "分佣提现"); | |
| 23 | 24 | |
| 24 | 25 | /** |
| 25 | 26 | * 业务分类 |
| ... | ... | @@ -36,4 +37,5 @@ public enum PayWalletBizTypeEnum implements IntArrayValuable { |
| 36 | 37 | public int[] array() { |
| 37 | 38 | return ARRAYS; |
| 38 | 39 | } |
| 40 | + | |
| 39 | 41 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/PayTransferApiImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.api.transfer; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; |
| 4 | +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferRespDTO; | |
| 5 | +import cn.iocoder.yudao.module.pay.convert.transfer.PayTransferConvert; | |
| 6 | +import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO; | |
| 4 | 7 | import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService; |
| 5 | 8 | import org.springframework.stereotype.Service; |
| 6 | 9 | import org.springframework.validation.annotation.Validated; |
| ... | ... | @@ -24,4 +27,10 @@ public class PayTransferApiImpl implements PayTransferApi { |
| 24 | 27 | return payTransferService.createTransfer(reqDTO); |
| 25 | 28 | } |
| 26 | 29 | |
| 30 | + @Override | |
| 31 | + public PayTransferRespDTO getTransfer(Long id) { | |
| 32 | + PayTransferDO transfer = payTransferService.getTransfer(id); | |
| 33 | + return PayTransferConvert.INSTANCE.convert3(transfer); | |
| 34 | + } | |
| 35 | + | |
| 27 | 36 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/wallet/PayWalletApiImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.pay.api.wallet; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | |
| 4 | +import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletCreateReqDto; | |
| 5 | +import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletRespDTO; | |
| 6 | +import cn.iocoder.yudao.module.pay.convert.wallet.PayWalletConvert; | |
| 7 | +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; | |
| 8 | +import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService; | |
| 9 | +import jakarta.annotation.Resource; | |
| 10 | +import org.springframework.stereotype.Service; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * 钱包 API 实现类 | |
| 14 | + * | |
| 15 | + * @author 芋道源码 | |
| 16 | + */ | |
| 17 | +@Service | |
| 18 | +public class PayWalletApiImpl implements PayWalletApi { | |
| 19 | + | |
| 20 | + @Resource | |
| 21 | + private PayWalletService payWalletService; | |
| 22 | + | |
| 23 | + @Override | |
| 24 | + public void addWallet(PayWalletCreateReqDto reqDTO) { | |
| 25 | + //添加钱包金额 | |
| 26 | + payWalletService.addWalletBalance(reqDTO.getWalletId(), reqDTO.getBizId(), reqDTO.getBizType(), reqDTO.getPrice()); | |
| 27 | + } | |
| 28 | + | |
| 29 | + @Override | |
| 30 | + public PayWalletRespDTO getWalletByUserId(Long userId) { | |
| 31 | + PayWalletDO orCreateWallet = payWalletService.getOrCreateWallet(userId, UserTypeEnum.MEMBER.getValue()); | |
| 32 | + return PayWalletConvert.INSTANCE.convert03(orCreateWallet); | |
| 33 | + } | |
| 34 | +} | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/app/vo/PayAppBaseVO.java
| ... | ... | @@ -40,4 +40,9 @@ public class PayAppBaseVO { |
| 40 | 40 | @URL(message = "退款结果的回调地址必须为 URL 格式") |
| 41 | 41 | private String refundNotifyUrl; |
| 42 | 42 | |
| 43 | + @Schema(description = "转账结果的回调地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://127.0.0.1:48080/refund-callback") | |
| 44 | + @NotNull(message = "转账结果的回调地址不能为空") | |
| 45 | + @URL(message = "转账结果的回调地址必须为 URL 格式") | |
| 46 | + private String transferNotifyUrl; | |
| 47 | + | |
| 43 | 48 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java
| ... | ... | @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 6 | import cn.iocoder.yudao.framework.pay.core.client.PayClient; |
| 7 | 7 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; |
| 8 | 8 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; |
| 9 | +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; | |
| 9 | 10 | import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO; |
| 10 | 11 | import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; |
| 11 | 12 | import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO; |
| ... | ... | @@ -18,6 +19,7 @@ import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; |
| 18 | 19 | import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; |
| 19 | 20 | import cn.iocoder.yudao.module.pay.service.order.PayOrderService; |
| 20 | 21 | import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; |
| 22 | +import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService; | |
| 21 | 23 | import io.swagger.v3.oas.annotations.Operation; |
| 22 | 24 | import io.swagger.v3.oas.annotations.Parameter; |
| 23 | 25 | import io.swagger.v3.oas.annotations.tags.Tag; |
| ... | ... | @@ -49,6 +51,8 @@ public class PayNotifyController { |
| 49 | 51 | @Resource |
| 50 | 52 | private PayRefundService refundService; |
| 51 | 53 | @Resource |
| 54 | + private PayTransferService payTransferService; | |
| 55 | + @Resource | |
| 52 | 56 | private PayNotifyService notifyService; |
| 53 | 57 | @Resource |
| 54 | 58 | private PayAppService appService; |
| ... | ... | @@ -95,6 +99,26 @@ public class PayNotifyController { |
| 95 | 99 | return "success"; |
| 96 | 100 | } |
| 97 | 101 | |
| 102 | + @PostMapping(value = "/transfer/{channelId}") | |
| 103 | + @Operation(summary = "支付渠道的统一【转账】回调") | |
| 104 | + @PermitAll | |
| 105 | + public String notifyTransfer(@PathVariable("channelId") Long channelId, | |
| 106 | + @RequestParam(required = false) Map<String, String> params, | |
| 107 | + @RequestBody(required = false) String body) { | |
| 108 | + log.info("[notifyRefund][channelId({}) 回调数据({}/{})]", channelId, params, body); | |
| 109 | + // 1. 校验支付渠道是否存在 | |
| 110 | + PayClient payClient = channelService.getPayClient(channelId); | |
| 111 | + if (payClient == null) { | |
| 112 | + log.error("[notifyCallback][渠道编号({}) 找不到对应的支付客户端]", channelId); | |
| 113 | + throw exception(CHANNEL_NOT_FOUND); | |
| 114 | + } | |
| 115 | + | |
| 116 | + // 2. 解析通知数据 | |
| 117 | + PayTransferRespDTO notify = payClient.parseTransferNotify(params, body); | |
| 118 | + payTransferService.notifyTransfer(channelId, notify); | |
| 119 | + return "success"; | |
| 120 | + } | |
| 121 | + | |
| 98 | 122 | @GetMapping("/get-detail") |
| 99 | 123 | @Operation(summary = "获得回调通知的明细") |
| 100 | 124 | @Parameter(name = "id", description = "编号", required = true, example = "1024") | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/transfer/PayTransferConvert.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.convert.transfer; |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 4 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; |
| 5 | 5 | import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; |
| 6 | +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferRespDTO; | |
| 6 | 7 | import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO; |
| 7 | 8 | import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferCreateReqVO; |
| 8 | 9 | import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferPageItemRespVO; |
| ... | ... | @@ -24,7 +25,9 @@ public interface PayTransferConvert { |
| 24 | 25 | |
| 25 | 26 | PayTransferCreateReqDTO convert(PayDemoTransferCreateReqVO vo); |
| 26 | 27 | |
| 27 | - PayTransferRespVO convert(PayTransferDO bean); | |
| 28 | + PayTransferRespVO convert(PayTransferDO bean); | |
| 29 | + | |
| 30 | + PayTransferRespDTO convert3(PayTransferDO bean); | |
| 28 | 31 | |
| 29 | 32 | PageResult<PayTransferPageItemRespVO> convertPage(PageResult<PayTransferDO> pageResult); |
| 30 | 33 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/wallet/PayWalletConvert.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.convert.wallet; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | +import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletCreateReqDto; | |
| 5 | +import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletRespDTO; | |
| 4 | 6 | import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.wallet.PayWalletRespVO; |
| 5 | 7 | import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.wallet.AppPayWalletRespVO; |
| 6 | 8 | import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; |
| ... | ... | @@ -18,4 +20,6 @@ public interface PayWalletConvert { |
| 18 | 20 | |
| 19 | 21 | PageResult<PayWalletRespVO> convertPage(PageResult<PayWalletDO> page); |
| 20 | 22 | |
| 23 | + PayWalletRespDTO convert03(PayWalletDO orCreateWallet); | |
| 24 | + | |
| 21 | 25 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/transfer/PayTransferMapper.java
| ... | ... | @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 5 | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | 6 | import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferPageReqVO; |
| 7 | +import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; | |
| 7 | 8 | import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO; |
| 8 | 9 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| 9 | 10 | import org.apache.ibatis.annotations.Mapper; |
| ... | ... | @@ -44,6 +45,12 @@ public interface PayTransferMapper extends BaseMapperX<PayTransferDO> { |
| 44 | 45 | default List<PayTransferDO> selectListByStatus(Integer status){ |
| 45 | 46 | return selectList(PayTransferDO::getStatus, status); |
| 46 | 47 | } |
| 48 | + | |
| 49 | + default PayTransferDO selectByAppIdAndNo(Long appId, String no){ | |
| 50 | + return selectOne(new LambdaQueryWrapperX<PayTransferDO>() | |
| 51 | + .eq(PayTransferDO::getAppId, appId) | |
| 52 | + .eq(PayTransferDO::getNo, no)); | |
| 53 | + } | |
| 47 | 54 | } |
| 48 | 55 | |
| 49 | 56 | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/config/PayProperties.java
| ... | ... | @@ -40,6 +40,15 @@ public class PayProperties { |
| 40 | 40 | private String refundNotifyUrl; |
| 41 | 41 | |
| 42 | 42 | /** |
| 43 | + * 转账回调地址 | |
| 44 | + * | |
| 45 | + * 实际上,对应的 PayNotifyController 的 notifyTransfer 方法的 URL | |
| 46 | + * | |
| 47 | + * 回调顺序:支付渠道(支付宝支付、微信支付) => yudao-module-pay 的 transferNotifyUrl 地址 => 业务的 PayAppDO.transferNotifyUrl 地址 | |
| 48 | + */ | |
| 49 | + private String transferNotifyUrl; | |
| 50 | + | |
| 51 | + /** | |
| 43 | 52 | * 支付订单 no 的前缀 |
| 44 | 53 | */ |
| 45 | 54 | @NotEmpty(message = "支付订单 no 的前缀不能为空") | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/WalletPayClient.java
| ... | ... | @@ -178,6 +178,11 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> { |
| 178 | 178 | } |
| 179 | 179 | |
| 180 | 180 | @Override |
| 181 | + protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) throws Throwable { | |
| 182 | + throw new UnsupportedOperationException("未实现"); | |
| 183 | + } | |
| 184 | + | |
| 185 | + @Override | |
| 181 | 186 | public PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) { |
| 182 | 187 | throw new UnsupportedOperationException("待实现"); |
| 183 | 188 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferService.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.service.transfer; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; | |
| 4 | 5 | import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; |
| 5 | 6 | import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferCreateReqVO; |
| 6 | 7 | import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferPageReqVO; |
| ... | ... | @@ -54,4 +55,12 @@ public interface PayTransferService { |
| 54 | 55 | * @return 同步到状态的转账数量,包括转账成功、转账失败、转账中的 |
| 55 | 56 | */ |
| 56 | 57 | int syncTransfer(); |
| 58 | + | |
| 59 | + /** | |
| 60 | + * 渠道的转账通知 | |
| 61 | + * | |
| 62 | + * @param channelId 渠道编号 | |
| 63 | + * @param notify 通知 | |
| 64 | + */ | |
| 65 | + void notifyTransfer(Long channelId, PayTransferRespDTO notify); | |
| 57 | 66 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java
| ... | ... | @@ -21,6 +21,7 @@ import cn.iocoder.yudao.module.pay.dal.mysql.transfer.PayTransferMapper; |
| 21 | 21 | import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO; |
| 22 | 22 | import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; |
| 23 | 23 | import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum; |
| 24 | +import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; | |
| 24 | 25 | import cn.iocoder.yudao.module.pay.service.app.PayAppService; |
| 25 | 26 | import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; |
| 26 | 27 | import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; |
| ... | ... | @@ -28,6 +29,7 @@ import jakarta.annotation.Resource; |
| 28 | 29 | import jakarta.validation.Validator; |
| 29 | 30 | import lombok.extern.slf4j.Slf4j; |
| 30 | 31 | import org.springframework.stereotype.Service; |
| 32 | +import org.springframework.transaction.annotation.Propagation; | |
| 31 | 33 | import org.springframework.transaction.annotation.Transactional; |
| 32 | 34 | |
| 33 | 35 | import java.util.List; |
| ... | ... | @@ -51,6 +53,9 @@ public class PayTransferServiceImpl implements PayTransferService { |
| 51 | 53 | private static final String TRANSFER_NO_PREFIX = "T"; |
| 52 | 54 | |
| 53 | 55 | @Resource |
| 56 | + private PayProperties payProperties; | |
| 57 | + | |
| 58 | + @Resource | |
| 54 | 59 | private PayTransferMapper transferMapper; |
| 55 | 60 | @Resource |
| 56 | 61 | private PayAppService appService; |
| ... | ... | @@ -96,13 +101,15 @@ public class PayTransferServiceImpl implements PayTransferService { |
| 96 | 101 | transfer = INSTANCE.convert(reqDTO) |
| 97 | 102 | .setChannelId(channel.getId()) |
| 98 | 103 | .setNo(no).setStatus(WAITING.getStatus()) |
| 99 | - .setNotifyUrl(payApp.getTransferNotifyUrl()); | |
| 104 | + .setNotifyUrl(payApp.getTransferNotifyUrl()) | |
| 105 | + .setAppId(channel.getAppId()); | |
| 100 | 106 | transferMapper.insert(transfer); |
| 101 | 107 | } |
| 102 | 108 | try { |
| 103 | 109 | // 3. 调用三方渠道发起转账 |
| 104 | 110 | PayTransferUnifiedReqDTO transferUnifiedReq = INSTANCE.convert2(transfer) |
| 105 | 111 | .setOutTransferNo(transfer.getNo()); |
| 112 | + transferUnifiedReq.setNotifyUrl(genChannelTransferNotifyUrl(channel)); | |
| 106 | 113 | PayTransferRespDTO unifiedTransferResp = client.unifiedTransfer(transferUnifiedReq); |
| 107 | 114 | // 4. 通知转账结果 |
| 108 | 115 | getSelf().notifyTransfer(channel, unifiedTransferResp); |
| ... | ... | @@ -116,6 +123,16 @@ public class PayTransferServiceImpl implements PayTransferService { |
| 116 | 123 | return transfer.getId(); |
| 117 | 124 | } |
| 118 | 125 | |
| 126 | + /** | |
| 127 | + * 根据支付渠道的编码,生成支付渠道的回调地址 | |
| 128 | + * | |
| 129 | + * @param channel 支付渠道 | |
| 130 | + * @return 支付渠道的回调地址 配置地址 + "/" + channel id | |
| 131 | + */ | |
| 132 | + private String genChannelTransferNotifyUrl(PayChannelDO channel) { | |
| 133 | + return payProperties.getTransferNotifyUrl() + "/" + channel.getId(); | |
| 134 | + } | |
| 135 | + | |
| 119 | 136 | private PayTransferDO validateTransferCanCreate(PayTransferCreateReqDTO dto, Long appId) { |
| 120 | 137 | PayTransferDO transfer = transferMapper.selectByAppIdAndMerchantTransferId(appId, dto.getMerchantTransferId()); |
| 121 | 138 | if (transfer != null) { |
| ... | ... | @@ -154,7 +171,8 @@ public class PayTransferServiceImpl implements PayTransferService { |
| 154 | 171 | |
| 155 | 172 | private void notifyTransferInProgress(PayChannelDO channel, PayTransferRespDTO notify) { |
| 156 | 173 | // 1.校验 |
| 157 | - PayTransferDO transfer = transferMapper.selectByNo(notify.getOutTransferNo()); | |
| 174 | + PayTransferDO transfer = transferMapper.selectByAppIdAndNo( | |
| 175 | + channel.getAppId(), notify.getOutTransferNo()); | |
| 158 | 176 | if (transfer == null) { |
| 159 | 177 | throw exception(PAY_TRANSFER_NOT_FOUND); |
| 160 | 178 | } |
| ... | ... | @@ -172,16 +190,13 @@ public class PayTransferServiceImpl implements PayTransferService { |
| 172 | 190 | throw exception(PAY_TRANSFER_STATUS_IS_NOT_WAITING); |
| 173 | 191 | } |
| 174 | 192 | log.info("[notifyTransferInProgress][transfer({}) 更新为转账进行中状态]", transfer.getId()); |
| 175 | - | |
| 176 | - // 3. 插入转账通知记录 | |
| 177 | - notifyService.createPayNotifyTask(PayNotifyTypeEnum.TRANSFER.getType(), | |
| 178 | - transfer.getId()); | |
| 179 | 193 | } |
| 180 | 194 | |
| 181 | 195 | |
| 182 | 196 | private void notifyTransferSuccess(PayChannelDO channel, PayTransferRespDTO notify) { |
| 183 | 197 | // 1.校验 |
| 184 | - PayTransferDO transfer = transferMapper.selectByNo(notify.getOutTransferNo()); | |
| 198 | + PayTransferDO transfer = transferMapper.selectByAppIdAndNo( | |
| 199 | + channel.getAppId(), notify.getOutTransferNo()); | |
| 185 | 200 | if (transfer == null) { |
| 186 | 201 | throw exception(PAY_TRANSFER_NOT_FOUND); |
| 187 | 202 | } |
| ... | ... | @@ -210,7 +225,8 @@ public class PayTransferServiceImpl implements PayTransferService { |
| 210 | 225 | |
| 211 | 226 | private void notifyTransferClosed(PayChannelDO channel, PayTransferRespDTO notify) { |
| 212 | 227 | // 1.校验 |
| 213 | - PayTransferDO transfer = transferMapper.selectByNo(notify.getOutTransferNo()); | |
| 228 | + PayTransferDO transfer = transferMapper.selectByAppIdAndNo( | |
| 229 | + channel.getAppId(), notify.getOutTransferNo()); | |
| 214 | 230 | if (transfer == null) { |
| 215 | 231 | throw exception(PAY_TRANSFER_NOT_FOUND); |
| 216 | 232 | } |
| ... | ... | @@ -283,7 +299,7 @@ public class PayTransferServiceImpl implements PayTransferService { |
| 283 | 299 | } |
| 284 | 300 | } |
| 285 | 301 | |
| 286 | - private void notifyTransfer(Long channelId, PayTransferRespDTO notify) { | |
| 302 | + public void notifyTransfer(Long channelId, PayTransferRespDTO notify) { | |
| 287 | 303 | // 校验渠道是否有效 |
| 288 | 304 | PayChannelDO channel = channelService.validPayChannel(channelId); |
| 289 | 305 | // 通知转账结果给对应的业务 | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java
| ... | ... | @@ -200,6 +200,9 @@ public class PayWalletServiceImpl implements PayWalletService { |
| 200 | 200 | case UPDATE_BALANCE: // 更新余额 |
| 201 | 201 | walletMapper.updateWhenRecharge(payWallet.getId(), price); |
| 202 | 202 | break; |
| 203 | + case WITHDRAW: | |
| 204 | + walletMapper.updateWhenRecharge(payWallet.getId(), price); | |
| 205 | + break; | |
| 203 | 206 | default: { |
| 204 | 207 | // TODO 其它类型待实现 |
| 205 | 208 | throw new UnsupportedOperationException("待实现"); | ... | ... |
yudao-module-pay/yudao-spring-boot-starter-biz-pay/pom.xml
| ... | ... | @@ -64,6 +64,10 @@ |
| 64 | 64 | <groupId>com.github.binarywang</groupId> |
| 65 | 65 | <artifactId>weixin-java-pay</artifactId> |
| 66 | 66 | </dependency> |
| 67 | + <dependency> | |
| 68 | + <groupId>com.github.binarywang</groupId> | |
| 69 | + <artifactId>wx-java-miniapp-spring-boot-starter</artifactId> | |
| 70 | + </dependency> | |
| 67 | 71 | |
| 68 | 72 | <!-- Test 测试相关 --> |
| 69 | 73 | <dependency> |
| ... | ... | @@ -71,6 +75,7 @@ |
| 71 | 75 | <artifactId>yudao-spring-boot-starter-test</artifactId> |
| 72 | 76 | <scope>test</scope> |
| 73 | 77 | </dependency> |
| 78 | + | |
| 74 | 79 | </dependencies> |
| 75 | 80 | |
| 76 | 81 | </project> | ... | ... |
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
| ... | ... | @@ -79,6 +79,8 @@ public interface PayClient { |
| 79 | 79 | */ |
| 80 | 80 | PayRefundRespDTO getRefund(String outTradeNo, String outRefundNo); |
| 81 | 81 | |
| 82 | + // ============ 转账相关 ========== | |
| 83 | + | |
| 82 | 84 | /** |
| 83 | 85 | * 调用渠道,进行转账 |
| 84 | 86 | * |
| ... | ... | @@ -95,4 +97,13 @@ public interface PayClient { |
| 95 | 97 | * @return 转账信息 |
| 96 | 98 | */ |
| 97 | 99 | PayTransferRespDTO getTransfer(String outTradeNo, PayTransferTypeEnum type); |
| 100 | + | |
| 101 | + /** | |
| 102 | + * 解析 transfer 回调数据 | |
| 103 | + * | |
| 104 | + * @param params HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数 | |
| 105 | + * @param body HTTP 回调接口的 request body | |
| 106 | + * @return 转账信息 | |
| 107 | + */ | |
| 108 | + PayTransferRespDTO parseTransferNotify(Map<String, String> params, String body); | |
| 98 | 109 | } | ... | ... |
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; |
| 9 | 9 | import jakarta.validation.constraints.NotBlank; |
| 10 | 10 | import jakarta.validation.constraints.NotEmpty; |
| 11 | 11 | import jakarta.validation.constraints.NotNull; |
| 12 | +import org.hibernate.validator.constraints.URL; | |
| 13 | + | |
| 12 | 14 | import java.util.Map; |
| 13 | 15 | |
| 14 | 16 | import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.*; |
| ... | ... | @@ -75,4 +77,11 @@ public class PayTransferUnifiedReqDTO { |
| 75 | 77 | * 支付渠道的额外参数 |
| 76 | 78 | */ |
| 77 | 79 | private Map<String, String> channelExtras; |
| 80 | + | |
| 81 | + /** | |
| 82 | + * 转账结果的 notify 回调地址 | |
| 83 | + */ | |
| 84 | + @NotEmpty(message = "转账结果的回调地址不能为空") | |
| 85 | + @URL(message = "转账结果的 notify 回调地址必须是 URL 格式") | |
| 86 | + private String notifyUrl; | |
| 78 | 87 | } | ... | ... |
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/WxPayTransferPartnerNotifyV3Result.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.pay.core.client.dto.transfer; | |
| 2 | + | |
| 3 | +import com.github.binarywang.wxpay.bean.notify.OriginNotifyResponse; | |
| 4 | +import com.github.binarywang.wxpay.bean.notify.WxPayBaseNotifyV3Result; | |
| 5 | +import com.google.gson.annotations.SerializedName; | |
| 6 | +import lombok.Data; | |
| 7 | +import lombok.NoArgsConstructor; | |
| 8 | + | |
| 9 | +import java.io.Serializable; | |
| 10 | + | |
| 11 | +@NoArgsConstructor | |
| 12 | +public class WxPayTransferPartnerNotifyV3Result implements Serializable, WxPayBaseNotifyV3Result<WxPayTransferPartnerNotifyV3Result.TransferNotifyResult> { | |
| 13 | + | |
| 14 | + private static final long serialVersionUID = -1L; | |
| 15 | + | |
| 16 | + /** | |
| 17 | + * 源数据 | |
| 18 | + */ | |
| 19 | + private OriginNotifyResponse rawData; | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * 解密后的数据 | |
| 23 | + */ | |
| 24 | + private TransferNotifyResult result; | |
| 25 | + | |
| 26 | + @Override | |
| 27 | + public void setRawData(OriginNotifyResponse rawData) { | |
| 28 | + this.rawData = rawData; | |
| 29 | + } | |
| 30 | + | |
| 31 | + @Override | |
| 32 | + public void setResult(TransferNotifyResult data) { | |
| 33 | + this.result = data; | |
| 34 | + } | |
| 35 | + | |
| 36 | + public TransferNotifyResult getResult() { | |
| 37 | + return result; | |
| 38 | + } | |
| 39 | + | |
| 40 | + public OriginNotifyResponse getRawData() { | |
| 41 | + return rawData; | |
| 42 | + } | |
| 43 | + | |
| 44 | + @Data | |
| 45 | + @NoArgsConstructor | |
| 46 | + public static class TransferNotifyResult implements Serializable { | |
| 47 | + private static final long serialVersionUID = 1L; | |
| 48 | + | |
| 49 | + /*********************** 公共字段 ******************** | |
| 50 | + | |
| 51 | + /** | |
| 52 | + * 商家批次单号 | |
| 53 | + */ | |
| 54 | + @SerializedName(value = "out_batch_no") | |
| 55 | + protected String outBatchNo; | |
| 56 | + | |
| 57 | + /** | |
| 58 | + * 微信批次单号 | |
| 59 | + */ | |
| 60 | + @SerializedName(value = "batch_id") | |
| 61 | + protected String batchId; | |
| 62 | + | |
| 63 | + /** | |
| 64 | + * 批次状态 | |
| 65 | + */ | |
| 66 | + @SerializedName(value = "batch_status") | |
| 67 | + protected String batchStatus; | |
| 68 | + | |
| 69 | + /** | |
| 70 | + * 批次总笔数 | |
| 71 | + */ | |
| 72 | + @SerializedName(value = "total_num") | |
| 73 | + protected Integer totalNum; | |
| 74 | + | |
| 75 | + /** | |
| 76 | + * 批次总金额 | |
| 77 | + */ | |
| 78 | + @SerializedName(value = "total_amount") | |
| 79 | + protected Integer totalAmount; | |
| 80 | + | |
| 81 | + /** | |
| 82 | + * 批次更新时间 | |
| 83 | + */ | |
| 84 | + @SerializedName(value = "update_time") | |
| 85 | + private String updateTime; | |
| 86 | + | |
| 87 | + /*********************** FINISHED ******************** | |
| 88 | + | |
| 89 | + /** | |
| 90 | + * 转账成功金额 | |
| 91 | + */ | |
| 92 | + @SerializedName(value = "success_amount") | |
| 93 | + protected Integer successAmount; | |
| 94 | + | |
| 95 | + /** | |
| 96 | + * 转账成功笔数 | |
| 97 | + */ | |
| 98 | + @SerializedName(value = "success_num") | |
| 99 | + protected Integer successNum; | |
| 100 | + | |
| 101 | + /** | |
| 102 | + * 转账失败金额 | |
| 103 | + */ | |
| 104 | + @SerializedName(value = "fail_amount") | |
| 105 | + protected Integer failAmount; | |
| 106 | + | |
| 107 | + /** | |
| 108 | + * 转账失败笔数 | |
| 109 | + */ | |
| 110 | + @SerializedName(value = "fail_num") | |
| 111 | + protected Integer failNum; | |
| 112 | + | |
| 113 | + /*********************** CLOSED ******************** | |
| 114 | + | |
| 115 | + /** | |
| 116 | + * 商户号 | |
| 117 | + */ | |
| 118 | + @SerializedName(value = "mchid") | |
| 119 | + protected String mchId; | |
| 120 | + | |
| 121 | + /** | |
| 122 | + * 批次关闭原因 | |
| 123 | + */ | |
| 124 | + @SerializedName(value = "close_reason") | |
| 125 | + protected String closeReason; | |
| 126 | + | |
| 127 | + } | |
| 128 | +} | ... | ... |
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
| ... | ... | @@ -220,6 +220,22 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen |
| 220 | 220 | } |
| 221 | 221 | |
| 222 | 222 | @Override |
| 223 | + public final PayTransferRespDTO parseTransferNotify(Map<String, String> params, String body) { | |
| 224 | + try { | |
| 225 | + return doParseTransferNotify(params, body); | |
| 226 | + } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可 | |
| 227 | + throw ex; | |
| 228 | + } catch (Throwable ex) { | |
| 229 | + log.error("[doParseTransferNotify][客户端({}) params({}) body({}) 解析失败]", | |
| 230 | + getId(), params, body, ex); | |
| 231 | + throw buildPayException(ex); | |
| 232 | + } | |
| 233 | + } | |
| 234 | + | |
| 235 | + protected abstract PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) | |
| 236 | + throws Throwable; | |
| 237 | + | |
| 238 | + @Override | |
| 223 | 239 | public final PayTransferRespDTO getTransfer(String outTradeNo, PayTransferTypeEnum type) { |
| 224 | 240 | try { |
| 225 | 241 | return doGetTransfer(outTradeNo, type); | ... | ... |
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
| ... | ... | @@ -325,6 +325,11 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa |
| 325 | 325 | } |
| 326 | 326 | } |
| 327 | 327 | |
| 328 | + @Override | |
| 329 | + protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) throws Throwable { | |
| 330 | + throw new UnsupportedOperationException("未实现"); | |
| 331 | + } | |
| 332 | + | |
| 328 | 333 | // ========== 各种工具方法 ========== |
| 329 | 334 | |
| 330 | 335 | protected String formatAmount(Integer amount) { | ... | ... |
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; |
| 4 | 4 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; |
| 5 | +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; | |
| 5 | 6 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; |
| 6 | 7 | import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; |
| 7 | 8 | import com.alipay.api.AlipayApiException; |
| ... | ... | @@ -56,4 +57,5 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient { |
| 56 | 57 | return PayOrderRespDTO.waitingOf(displayMode, response.getBody(), |
| 57 | 58 | reqDTO.getOutTradeNo(), response); |
| 58 | 59 | } |
| 60 | + | |
| 59 | 61 | } | ... | ... |
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java
| ... | ... | @@ -5,6 +5,7 @@ import cn.hutool.core.map.MapUtil; |
| 5 | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | 6 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; |
| 7 | 7 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; |
| 8 | +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; | |
| 8 | 9 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; |
| 9 | 10 | import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; |
| 10 | 11 | import com.alipay.api.AlipayApiException; |
| ... | ... | @@ -82,4 +83,5 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient { |
| 82 | 83 | return PayOrderRespDTO.waitingOf(displayMode, "", |
| 83 | 84 | reqDTO.getOutTradeNo(), response); |
| 84 | 85 | } |
| 86 | + | |
| 85 | 87 | } | ... | ... |
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java
| ... | ... | @@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjectUtil; |
| 4 | 4 | import cn.hutool.http.Method; |
| 5 | 5 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; |
| 6 | 6 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; |
| 7 | +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; | |
| 7 | 8 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; |
| 8 | 9 | import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; |
| 9 | 10 | import com.alipay.api.AlipayApiException; |
| ... | ... | @@ -66,4 +67,5 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient { |
| 66 | 67 | return PayOrderRespDTO.waitingOf(displayMode, response.getBody(), |
| 67 | 68 | reqDTO.getOutTradeNo(), response); |
| 68 | 69 | } |
| 70 | + | |
| 69 | 71 | } | ... | ... |
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClient.java
| ... | ... | @@ -58,6 +58,11 @@ public class MockPayClient extends AbstractPayClient<NonePayClientConfig> { |
| 58 | 58 | } |
| 59 | 59 | |
| 60 | 60 | @Override |
| 61 | + protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) throws Throwable { | |
| 62 | + throw new UnsupportedOperationException("未实现"); | |
| 63 | + } | |
| 64 | + | |
| 65 | + @Override | |
| 61 | 66 | protected PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) { |
| 62 | 67 | throw new UnsupportedOperationException("模拟支付无退款回调"); |
| 63 | 68 | } | ... | ... |
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
| ... | ... | @@ -14,23 +14,26 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; |
| 14 | 14 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; |
| 15 | 15 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; |
| 16 | 16 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; |
| 17 | +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.WxPayTransferPartnerNotifyV3Result; | |
| 17 | 18 | import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; |
| 18 | 19 | import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; |
| 19 | 20 | import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; |
| 20 | -import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result; | |
| 21 | -import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; | |
| 22 | -import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; | |
| 23 | -import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result; | |
| 21 | +import com.github.binarywang.wxpay.bean.notify.*; | |
| 24 | 22 | import com.github.binarywang.wxpay.bean.request.*; |
| 25 | 23 | import com.github.binarywang.wxpay.bean.result.*; |
| 24 | +import com.github.binarywang.wxpay.bean.transfer.TransferBatchesRequest; | |
| 25 | +import com.github.binarywang.wxpay.bean.transfer.TransferBatchesResult; | |
| 26 | 26 | import com.github.binarywang.wxpay.config.WxPayConfig; |
| 27 | 27 | import com.github.binarywang.wxpay.exception.WxPayException; |
| 28 | +import com.github.binarywang.wxpay.service.TransferService; | |
| 28 | 29 | import com.github.binarywang.wxpay.service.WxPayService; |
| 29 | 30 | import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; |
| 30 | 31 | import lombok.extern.slf4j.Slf4j; |
| 31 | 32 | |
| 32 | 33 | import java.time.LocalDateTime; |
| 33 | 34 | import java.time.ZoneId; |
| 35 | +import java.util.ArrayList; | |
| 36 | +import java.util.List; | |
| 34 | 37 | import java.util.Map; |
| 35 | 38 | import java.util.Objects; |
| 36 | 39 | |
| ... | ... | @@ -349,6 +352,29 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC |
| 349 | 352 | } |
| 350 | 353 | |
| 351 | 354 | @Override |
| 355 | + public PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) throws WxPayException { | |
| 356 | + switch (config.getApiVersion()) { | |
| 357 | + case API_VERSION_V3: | |
| 358 | + return parseTransferNotifyV3(body); | |
| 359 | + default: | |
| 360 | + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); | |
| 361 | + } | |
| 362 | + } | |
| 363 | + | |
| 364 | + private PayTransferRespDTO parseTransferNotifyV3(String body) throws WxPayException { | |
| 365 | + WxPayTransferPartnerNotifyV3Result response = client.baseParseOrderNotifyV3Result(body, null, WxPayTransferPartnerNotifyV3Result.class, WxPayTransferPartnerNotifyV3Result.TransferNotifyResult.class); | |
| 366 | + WxPayTransferPartnerNotifyV3Result.TransferNotifyResult result = response.getResult(); | |
| 367 | + // 2. 构建结果 | |
| 368 | + if (Objects.equals("FINISHED", result.getBatchStatus())) { | |
| 369 | + if(result.getFailNum() <= 0){ | |
| 370 | + return PayTransferRespDTO.successOf(result.getBatchId(), parseDateV3(result.getUpdateTime()), | |
| 371 | + result.getOutBatchNo(), response); | |
| 372 | + } | |
| 373 | + } | |
| 374 | + return PayTransferRespDTO.closedOf(result.getBatchStatus(), result.getCloseReason(), result.getOutBatchNo(), response); | |
| 375 | + } | |
| 376 | + | |
| 377 | + @Override | |
| 352 | 378 | protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) throws WxPayException { |
| 353 | 379 | try { |
| 354 | 380 | switch (config.getApiVersion()) { |
| ... | ... | @@ -426,8 +452,26 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC |
| 426 | 452 | } |
| 427 | 453 | |
| 428 | 454 | @Override |
| 429 | - protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) { | |
| 430 | - throw new UnsupportedOperationException("待实现"); | |
| 455 | + protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws WxPayException { | |
| 456 | + TransferService transferService = client.getTransferService(); | |
| 457 | + List<TransferBatchesRequest.TransferDetail> transferDetailList = new ArrayList<>(); | |
| 458 | + transferDetailList.add(TransferBatchesRequest.TransferDetail.newBuilder() | |
| 459 | + .outDetailNo(reqDTO.getOutTransferNo()) | |
| 460 | + .transferAmount(reqDTO.getPrice()) | |
| 461 | + .transferRemark(reqDTO.getSubject()) | |
| 462 | + .openid(reqDTO.getOpenid()) | |
| 463 | + .build()); | |
| 464 | + TransferBatchesRequest transferBatches = TransferBatchesRequest.newBuilder() | |
| 465 | + .appid(this.config.getAppId()) | |
| 466 | + .outBatchNo(reqDTO.getOutTransferNo()) | |
| 467 | + .batchName(reqDTO.getSubject()) | |
| 468 | + .batchRemark(reqDTO.getSubject()) | |
| 469 | + .totalAmount(reqDTO.getPrice()) | |
| 470 | + .totalNum(1) | |
| 471 | + .transferDetailList(transferDetailList).build(); | |
| 472 | + transferBatches.setNotifyUrl(reqDTO.getNotifyUrl()); | |
| 473 | + TransferBatchesResult transferBatchesResult = transferService.transferBatches(transferBatches); | |
| 474 | + return PayTransferRespDTO.dealingOf(transferBatchesResult.getBatchId(), reqDTO.getOutTransferNo(), transferBatchesResult); | |
| 431 | 475 | } |
| 432 | 476 | |
| 433 | 477 | @Override | ... | ... |
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesRequest.java
0 → 100644
| 1 | +package com.github.binarywang.wxpay.bean.transfer; | |
| 2 | + | |
| 3 | +import com.github.binarywang.wxpay.v3.SpecEncrypt; | |
| 4 | +import com.google.gson.annotations.SerializedName; | |
| 5 | +import lombok.AllArgsConstructor; | |
| 6 | +import lombok.Builder; | |
| 7 | +import lombok.Data; | |
| 8 | +import lombok.NoArgsConstructor; | |
| 9 | + | |
| 10 | +import java.io.Serializable; | |
| 11 | +import java.util.List; | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * 发起商家转账API参数 | |
| 15 | + * | |
| 16 | + * @author zhongjun | |
| 17 | + * created on 2022/6/17 | |
| 18 | + **/ | |
| 19 | +@Data | |
| 20 | +@Builder(builderMethodName = "newBuilder") | |
| 21 | +@NoArgsConstructor | |
| 22 | +@AllArgsConstructor | |
| 23 | +public class TransferBatchesRequest implements Serializable { | |
| 24 | + private static final long serialVersionUID = -2175582517588397426L; | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * 直连商户的appid | |
| 28 | + */ | |
| 29 | + @SerializedName("appid") | |
| 30 | + private String appid; | |
| 31 | + | |
| 32 | + /** | |
| 33 | + * 商家批次单号 | |
| 34 | + */ | |
| 35 | + @SerializedName("out_batch_no") | |
| 36 | + private String outBatchNo; | |
| 37 | + | |
| 38 | + /** | |
| 39 | + * 批次名称 | |
| 40 | + */ | |
| 41 | + @SerializedName("batch_name") | |
| 42 | + private String batchName; | |
| 43 | + | |
| 44 | + /** | |
| 45 | + * 批次备注 | |
| 46 | + */ | |
| 47 | + @SerializedName("batch_remark") | |
| 48 | + private String batchRemark; | |
| 49 | + | |
| 50 | + /** | |
| 51 | + * 转账总金额 | |
| 52 | + */ | |
| 53 | + @SerializedName("total_amount") | |
| 54 | + private Integer totalAmount; | |
| 55 | + | |
| 56 | + /** | |
| 57 | + * 转账总笔数 | |
| 58 | + */ | |
| 59 | + @SerializedName("total_num") | |
| 60 | + private Integer totalNum; | |
| 61 | + | |
| 62 | + /** | |
| 63 | + * 转账明细列表 | |
| 64 | + */ | |
| 65 | + @SpecEncrypt | |
| 66 | + @SerializedName("transfer_detail_list") | |
| 67 | + private List<TransferDetail> transferDetailList; | |
| 68 | + | |
| 69 | + /** | |
| 70 | + * 转账场景ID | |
| 71 | + */ | |
| 72 | + @SerializedName("transfer_scene_id") | |
| 73 | + private String transferSceneId; | |
| 74 | + | |
| 75 | + /** | |
| 76 | + * 通知地址 说明:异步接收微信支付结果通知的回调地址,通知url必须为公网可访问的url,必须为https,不能携带参数。 | |
| 77 | + */ | |
| 78 | + @SerializedName("notify_url") | |
| 79 | + private String notifyUrl; | |
| 80 | + | |
| 81 | + @Data | |
| 82 | + @Builder(builderMethodName = "newBuilder") | |
| 83 | + @AllArgsConstructor | |
| 84 | + @NoArgsConstructor | |
| 85 | + public static class TransferDetail { | |
| 86 | + | |
| 87 | + /** | |
| 88 | + * 商家明细单号 | |
| 89 | + */ | |
| 90 | + @SerializedName("out_detail_no") | |
| 91 | + private String outDetailNo; | |
| 92 | + | |
| 93 | + /** | |
| 94 | + * 转账金额 | |
| 95 | + */ | |
| 96 | + @SerializedName("transfer_amount") | |
| 97 | + private Integer transferAmount; | |
| 98 | + | |
| 99 | + /** | |
| 100 | + * 转账备注 | |
| 101 | + */ | |
| 102 | + @SerializedName("transfer_remark") | |
| 103 | + private String transferRemark; | |
| 104 | + | |
| 105 | + /** | |
| 106 | + * 用户在直连商户应用下的用户标示 | |
| 107 | + */ | |
| 108 | + @SerializedName("openid") | |
| 109 | + private String openid; | |
| 110 | + | |
| 111 | + /** | |
| 112 | + * 收款用户姓名 | |
| 113 | + */ | |
| 114 | + @SpecEncrypt | |
| 115 | + @SerializedName("user_name") | |
| 116 | + private String userName; | |
| 117 | + } | |
| 118 | +} | ... | ... |
yudao-server/src/main/resources/application-dev.yaml
| ... | ... | @@ -165,7 +165,8 @@ yudao: |
| 165 | 165 | pay: |
| 166 | 166 | order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # 支付渠道的【支付】回调地址 |
| 167 | 167 | refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址 |
| 168 | - demo: true # 开启演示模式 | |
| 168 | + transfer-notify-url: https://yunai.natapp1.cc/admin-api/pay/notify/transfer # 支付渠道的【转账】回调地址 | |
| 169 | + demo: false # 开启演示模式 | |
| 169 | 170 | tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc |
| 170 | 171 | |
| 171 | 172 | justauth: | ... | ... |
yudao-server/src/main/resources/application-local.yaml
| ... | ... | @@ -218,6 +218,7 @@ yudao: |
| 218 | 218 | pay: |
| 219 | 219 | order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # 支付渠道的【支付】回调地址 |
| 220 | 220 | refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址 |
| 221 | + transfer-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/transfer # 支付渠道的【转账】回调地址 | |
| 221 | 222 | access-log: # 访问日志的配置项 |
| 222 | 223 | enable: false |
| 223 | 224 | demo: false # 关闭演示模式 | ... | ... |