Commit 38c76806a3da36f8b97a5943c1410c94011623b0

Authored by YunaiV
1 parent f81dc105

feat:【PAY 支付】微信支付的转账,接入新的 API(需要继续测试,= = 真麻烦)

feat:【PAY 支付】钱包支持转账功能
Showing 17 changed files with 249 additions and 205 deletions
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java
@@ -113,7 +113,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @@ -113,7 +113,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
113 if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(withdraw.getType())) { 113 if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(withdraw.getType())) {
114 payWalletApi.addWalletBalance(new PayWalletAddBalanceReqDTO() 114 payWalletApi.addWalletBalance(new PayWalletAddBalanceReqDTO()
115 .setUserId(withdraw.getUserId()).setUserType(UserTypeEnum.MEMBER.getValue()) 115 .setUserId(withdraw.getUserId()).setUserType(UserTypeEnum.MEMBER.getValue())
116 - .setBizType(PayWalletBizTypeEnum.BROKERAGE_WITHDRAW.getType()).setBizId(withdraw.getId().toString()) 116 + .setBizType(PayWalletBizTypeEnum.TRANSFER.getType()).setBizId(withdraw.getId().toString())
117 .setPrice(withdraw.getPrice())); 117 .setPrice(withdraw.getPrice()));
118 // 1.2 微信 API 118 // 1.2 微信 API
119 } else if (BrokerageWithdrawTypeEnum.WECHAT_API.getType().equals(withdraw.getType())) { 119 } else if (BrokerageWithdrawTypeEnum.WECHAT_API.getType().equals(withdraw.getType())) {
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/dto/PayTransferCreateReqDTO.java
1 package cn.iocoder.yudao.module.pay.api.transfer.dto; 1 package cn.iocoder.yudao.module.pay.api.transfer.dto;
2 2
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
3 import jakarta.validation.constraints.Min; 5 import jakarta.validation.constraints.Min;
4 import jakarta.validation.constraints.NotEmpty; 6 import jakarta.validation.constraints.NotEmpty;
5 import jakarta.validation.constraints.NotNull; 7 import jakarta.validation.constraints.NotNull;
6 import lombok.Data; 8 import lombok.Data;
7 9
  10 +import java.util.ArrayList;
  11 +import java.util.HashMap;
  12 +import java.util.List;
8 import java.util.Map; 13 import java.util.Map;
9 14
10 /** 15 /**
@@ -70,4 +75,46 @@ public class PayTransferCreateReqDTO { @@ -70,4 +75,46 @@ public class PayTransferCreateReqDTO {
70 */ 75 */
71 private String userName; 76 private String userName;
72 77
  78 + /**
  79 + * 【微信】现金营销场景
  80 + *
  81 + * @param activityName 活动名称
  82 + * @param rewardDescription 奖励说明
  83 + * @return channelExtras
  84 + */
  85 + public static Map<String, String> buildWeiXinChannelExtra1000(String activityName, String rewardDescription) {
  86 + return buildWeiXinChannelExtra(1000,
  87 + "活动名称", activityName,
  88 + "奖励说明", rewardDescription);
  89 + }
  90 +
  91 + /**
  92 + * 【微信】企业报销场景
  93 + *
  94 + * @param expenseType 报销类型
  95 + * @param expenseDescription 报销说明
  96 + * @return channelExtras
  97 + */
  98 + public static Map<String, String> buildWeiXinChannelExtra1006(String expenseType, String expenseDescription) {
  99 + return buildWeiXinChannelExtra(1006,
  100 + "报销类型", expenseType,
  101 + "报销说明", expenseDescription);
  102 + }
  103 +
  104 + private static Map<String, String> buildWeiXinChannelExtra(Integer sceneId, String... values) {
  105 + Map<String, String> channelExtras = new HashMap<>();
  106 + // 构建场景报备信息列表
  107 + List<Map<String, String>> sceneReportInfos = new ArrayList<>();
  108 + for (int i = 0; i < values.length; i += 2) {
  109 + Map<String, String> info = new HashMap<>();
  110 + info.put("infoType", values[i]);
  111 + info.put("infoContent", values[i + 1]);
  112 + sceneReportInfos.add(info);
  113 + }
  114 + // 设置场景ID和场景报备信息
  115 + channelExtras.put("sceneId", StrUtil.toString(sceneId));
  116 + channelExtras.put("sceneReportInfos", JsonUtils.toJsonString(sceneReportInfos));
  117 + return channelExtras;
  118 + }
  119 +
73 } 120 }
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/wallet/PayWalletBizTypeEnum.java
@@ -20,7 +20,7 @@ public enum PayWalletBizTypeEnum implements ArrayValuable&lt;Integer&gt; { @@ -20,7 +20,7 @@ public enum PayWalletBizTypeEnum implements ArrayValuable&lt;Integer&gt; {
20 PAYMENT(3, "支付"), 20 PAYMENT(3, "支付"),
21 PAYMENT_REFUND(4, "支付退款"), 21 PAYMENT_REFUND(4, "支付退款"),
22 UPDATE_BALANCE(5, "更新余额"), 22 UPDATE_BALANCE(5, "更新余额"),
23 - BROKERAGE_WITHDRAW(6, "分佣提现"); 23 + TRANSFER(6, "转账");
24 24
25 /** 25 /**
26 * 业务分类 26 * 业务分类
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoWithdrawController.http
1 -### 请求 /pay/pay/demo-order 接口 => 成功 1 +### 请求 /pay/pay/demo-order 接口(支付宝) => 成功
2 POST {{baseUrl}}/pay/demo-withdraw/create 2 POST {{baseUrl}}/pay/demo-withdraw/create
3 Authorization: Bearer {{token}} 3 Authorization: Bearer {{token}}
4 Content-Type: application/json 4 Content-Type: application/json
@@ -10,4 +10,31 @@ tenant-id: {{adminTenantId}} @@ -10,4 +10,31 @@ tenant-id: {{adminTenantId}}
10 "price": 10, 10 "price": 10,
11 "userAccount": "oespxk7368@sandbox.com", 11 "userAccount": "oespxk7368@sandbox.com",
12 "userName": "oespxk7368" 12 "userName": "oespxk7368"
  13 +}
  14 +
  15 +### 请求 /pay/pay/demo-order 接口(微信余额) => 成功
  16 +POST {{baseUrl}}/pay/demo-withdraw/create
  17 +Authorization: Bearer {{token}}
  18 +Content-Type: application/json
  19 +tenant-id: {{adminTenantId}}
  20 +
  21 +{
  22 + "type": 2,
  23 + "subject": "测试转账",
  24 + "price": 1,
  25 + "userAccount": "oiSC85elO_OZogXODC5RoGyXamK4",
  26 + "userName": "芋艿"
  27 +}
  28 +
  29 +### 请求 /pay/pay/demo-order 接口(钱包余额) => 成功
  30 +POST {{baseUrl}}/pay/demo-withdraw/create
  31 +Authorization: Bearer {{token}}
  32 +Content-Type: application/json
  33 +tenant-id: {{adminTenantId}}
  34 +
  35 +{
  36 + "type": 3,
  37 + "subject": "测试转账",
  38 + "price": 1,
  39 + "userAccount": "1"
13 } 40 }
14 \ No newline at end of file 41 \ No newline at end of file
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/vo/withdraw/PayDemoWithdrawCreateReqVO.java
@@ -27,7 +27,6 @@ public class PayDemoWithdrawCreateReqVO { @@ -27,7 +27,6 @@ public class PayDemoWithdrawCreateReqVO {
27 private String userAccount; 27 private String userAccount;
28 28
29 @Schema(description = "收款人姓名", example = "test1") 29 @Schema(description = "收款人姓名", example = "test1")
30 - @NotBlank(message = "收款人姓名不能为空")  
31 private String userName; 30 private String userName;
32 31
33 @Schema(description = "提现方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 32 @Schema(description = "提现方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/PayOrderController.java
@@ -12,10 +12,12 @@ import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert; @@ -12,10 +12,12 @@ import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert;
12 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; 12 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
13 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; 13 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
14 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; 14 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
  15 +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
15 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; 16 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
16 import cn.iocoder.yudao.module.pay.framework.pay.core.WalletPayClient; 17 import cn.iocoder.yudao.module.pay.framework.pay.core.WalletPayClient;
17 import cn.iocoder.yudao.module.pay.service.app.PayAppService; 18 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
18 import cn.iocoder.yudao.module.pay.service.order.PayOrderService; 19 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
  20 +import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService;
19 import com.google.common.collect.Maps; 21 import com.google.common.collect.Maps;
20 import io.swagger.v3.oas.annotations.Operation; 22 import io.swagger.v3.oas.annotations.Operation;
21 import io.swagger.v3.oas.annotations.Parameter; 23 import io.swagger.v3.oas.annotations.Parameter;
@@ -51,6 +53,8 @@ public class PayOrderController { @@ -51,6 +53,8 @@ public class PayOrderController {
51 private PayOrderService orderService; 53 private PayOrderService orderService;
52 @Resource 54 @Resource
53 private PayAppService appService; 55 private PayAppService appService;
  56 + @Resource
  57 + private PayWalletService payWalletService;
54 58
55 @GetMapping("/get") 59 @GetMapping("/get")
56 @Operation(summary = "获得支付订单") 60 @Operation(summary = "获得支付订单")
@@ -92,11 +96,11 @@ public class PayOrderController { @@ -92,11 +96,11 @@ public class PayOrderController {
92 public CommonResult<PayOrderSubmitRespVO> submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) { 96 public CommonResult<PayOrderSubmitRespVO> submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) {
93 // 1. 钱包支付事,需要额外传 user_id 和 user_type 97 // 1. 钱包支付事,需要额外传 user_id 和 user_type
94 if (Objects.equals(reqVO.getChannelCode(), PayChannelEnum.WALLET.getCode())) { 98 if (Objects.equals(reqVO.getChannelCode(), PayChannelEnum.WALLET.getCode())) {
95 - Map<String, String> channelExtras = reqVO.getChannelExtras() == null ?  
96 - Maps.newHashMapWithExpectedSize(2) : reqVO.getChannelExtras();  
97 - channelExtras.put(WalletPayClient.USER_ID_KEY, String.valueOf(getLoginUserId()));  
98 - channelExtras.put(WalletPayClient.USER_TYPE_KEY, String.valueOf(getLoginUserType()));  
99 - reqVO.setChannelExtras(channelExtras); 99 + if (reqVO.getChannelExtras() == null) {
  100 + reqVO.setChannelExtras(Maps.newHashMapWithExpectedSize(1));
  101 + }
  102 + PayWalletDO wallet = payWalletService.getOrCreateWallet(getLoginUserId(), getLoginUserType());
  103 + reqVO.getChannelExtras().put(WalletPayClient.WALLET_ID_KEY, String.valueOf(wallet.getId()));
100 } 104 }
101 105
102 // 2. 提交支付 106 // 2. 提交支付
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.java
@@ -9,9 +9,11 @@ import cn.iocoder.yudao.module.pay.controller.app.order.vo.AppPayOrderSubmitReqV @@ -9,9 +9,11 @@ import cn.iocoder.yudao.module.pay.controller.app.order.vo.AppPayOrderSubmitReqV
9 import cn.iocoder.yudao.module.pay.controller.app.order.vo.AppPayOrderSubmitRespVO; 9 import cn.iocoder.yudao.module.pay.controller.app.order.vo.AppPayOrderSubmitRespVO;
10 import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert; 10 import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert;
11 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; 11 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
  12 +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
12 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; 13 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
13 import cn.iocoder.yudao.module.pay.framework.pay.core.WalletPayClient; 14 import cn.iocoder.yudao.module.pay.framework.pay.core.WalletPayClient;
14 import cn.iocoder.yudao.module.pay.service.order.PayOrderService; 15 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
  16 +import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService;
15 import com.google.common.collect.Maps; 17 import com.google.common.collect.Maps;
16 import io.swagger.v3.oas.annotations.Operation; 18 import io.swagger.v3.oas.annotations.Operation;
17 import io.swagger.v3.oas.annotations.Parameter; 19 import io.swagger.v3.oas.annotations.Parameter;
@@ -22,7 +24,6 @@ import lombok.extern.slf4j.Slf4j; @@ -22,7 +24,6 @@ import lombok.extern.slf4j.Slf4j;
22 import org.springframework.validation.annotation.Validated; 24 import org.springframework.validation.annotation.Validated;
23 import org.springframework.web.bind.annotation.*; 25 import org.springframework.web.bind.annotation.*;
24 26
25 -import java.util.Map;  
26 import java.util.Objects; 27 import java.util.Objects;
27 28
28 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 29 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -39,6 +40,8 @@ public class AppPayOrderController { @@ -39,6 +40,8 @@ public class AppPayOrderController {
39 40
40 @Resource 41 @Resource
41 private PayOrderService payOrderService; 42 private PayOrderService payOrderService;
  43 + @Resource
  44 + private PayWalletService payWalletService;
42 45
43 @GetMapping("/get") 46 @GetMapping("/get")
44 @Operation(summary = "获得支付订单") 47 @Operation(summary = "获得支付订单")
@@ -63,11 +66,11 @@ public class AppPayOrderController { @@ -63,11 +66,11 @@ public class AppPayOrderController {
63 public CommonResult<AppPayOrderSubmitRespVO> submitPayOrder(@RequestBody AppPayOrderSubmitReqVO reqVO) { 66 public CommonResult<AppPayOrderSubmitRespVO> submitPayOrder(@RequestBody AppPayOrderSubmitReqVO reqVO) {
64 // 1. 钱包支付事,需要额外传 user_id 和 user_type 67 // 1. 钱包支付事,需要额外传 user_id 和 user_type
65 if (Objects.equals(reqVO.getChannelCode(), PayChannelEnum.WALLET.getCode())) { 68 if (Objects.equals(reqVO.getChannelCode(), PayChannelEnum.WALLET.getCode())) {
66 - Map<String, String> channelExtras = reqVO.getChannelExtras() == null ?  
67 - Maps.newHashMapWithExpectedSize(2) : reqVO.getChannelExtras();  
68 - channelExtras.put(WalletPayClient.USER_ID_KEY, String.valueOf(getLoginUserId()));  
69 - channelExtras.put(WalletPayClient.USER_TYPE_KEY, String.valueOf(getLoginUserType()));  
70 - reqVO.setChannelExtras(channelExtras); 69 + if (reqVO.getChannelExtras() == null) {
  70 + reqVO.setChannelExtras(Maps.newHashMapWithExpectedSize(1));
  71 + }
  72 + PayWalletDO wallet = payWalletService.getOrCreateWallet(getLoginUserId(), getLoginUserType());
  73 + reqVO.getChannelExtras().put(WalletPayClient.WALLET_ID_KEY, String.valueOf(wallet.getId()));
71 } 74 }
72 75
73 // 2. 提交支付 76 // 2. 提交支付
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/transfer/PayTransferMapper.java
@@ -53,6 +53,10 @@ public interface PayTransferMapper extends BaseMapperX&lt;PayTransferDO&gt; { @@ -53,6 +53,10 @@ public interface PayTransferMapper extends BaseMapperX&lt;PayTransferDO&gt; {
53 PayTransferDO::getNo, no); 53 PayTransferDO::getNo, no);
54 } 54 }
55 55
  56 + default PayTransferDO selectByNo(String no) {
  57 + return selectOne(PayTransferDO::getNo, no);
  58 + }
  59 +
56 } 60 }
57 61
58 62
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/WalletPayClient.java
@@ -14,13 +14,16 @@ import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; @@ -14,13 +14,16 @@ import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
14 import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig; 14 import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig;
15 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 15 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
16 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; 16 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
  17 +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferStatusRespEnum;
17 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; 18 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
18 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; 19 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
  20 +import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO;
19 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; 21 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
20 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; 22 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
21 import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; 23 import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
22 import cn.iocoder.yudao.module.pay.service.order.PayOrderService; 24 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
23 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; 25 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
  26 +import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService;
24 import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService; 27 import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService;
25 import cn.iocoder.yudao.module.pay.service.wallet.PayWalletTransactionService; 28 import cn.iocoder.yudao.module.pay.service.wallet.PayWalletTransactionService;
26 import lombok.extern.slf4j.Slf4j; 29 import lombok.extern.slf4j.Slf4j;
@@ -39,13 +42,14 @@ import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.REFUND_NOT_FO @@ -39,13 +42,14 @@ import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.REFUND_NOT_FO
39 @Slf4j 42 @Slf4j
40 public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> { 43 public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
41 44
42 - public static final String USER_ID_KEY = "user_id";  
43 - public static final String USER_TYPE_KEY = "user_type"; 45 + public static final String WALLET_ID_KEY = "walletId";
44 46
45 private PayWalletService wallService; 47 private PayWalletService wallService;
46 private PayWalletTransactionService walletTransactionService; 48 private PayWalletTransactionService walletTransactionService;
  49 +
47 private PayOrderService orderService; 50 private PayOrderService orderService;
48 private PayRefundService refundService; 51 private PayRefundService refundService;
  52 + private PayTransferService transferService;
49 53
50 public WalletPayClient(Long channelId, NonePayClientConfig config) { 54 public WalletPayClient(Long channelId, NonePayClientConfig config) {
51 super(channelId, PayChannelEnum.WALLET.getCode(), config); 55 super(channelId, PayChannelEnum.WALLET.getCode(), config);
@@ -62,19 +66,18 @@ public class WalletPayClient extends AbstractPayClient&lt;NonePayClientConfig&gt; { @@ -62,19 +66,18 @@ public class WalletPayClient extends AbstractPayClient&lt;NonePayClientConfig&gt; {
62 } 66 }
63 67
64 @Override 68 @Override
  69 + @SuppressWarnings("PatternVariableCanBeUsed")
65 protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { 70 protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
66 try { 71 try {
67 - Long userId = MapUtil.getLong(reqDTO.getChannelExtras(), USER_ID_KEY);  
68 - Integer userType = MapUtil.getInt(reqDTO.getChannelExtras(), USER_TYPE_KEY);  
69 - Assert.notNull(userId, "用户 id 不能为空");  
70 - Assert.notNull(userType, "用户类型不能为空");  
71 - PayWalletTransactionDO transaction = wallService.orderPay(userId, userType, reqDTO.getOutTradeNo(),  
72 - reqDTO.getPrice()); 72 + Long walletId = MapUtil.getLong(reqDTO.getChannelExtras(), WALLET_ID_KEY);
  73 + Assert.notNull(walletId, "钱包编号");
  74 + PayWalletTransactionDO transaction = wallService.orderPay(walletId,
  75 + reqDTO.getOutTradeNo(), reqDTO.getPrice());
73 return PayOrderRespDTO.successOf(transaction.getNo(), transaction.getCreator(), 76 return PayOrderRespDTO.successOf(transaction.getNo(), transaction.getCreator(),
74 transaction.getCreateTime(), 77 transaction.getCreateTime(),
75 reqDTO.getOutTradeNo(), transaction); 78 reqDTO.getOutTradeNo(), transaction);
76 } catch (Throwable ex) { 79 } catch (Throwable ex) {
77 - log.error("[doUnifiedOrder] 失败", ex); 80 + log.error("[doUnifiedOrder][reqDTO({}) 异常]", reqDTO, ex);
78 Integer errorCode = INTERNAL_SERVER_ERROR.getCode(); 81 Integer errorCode = INTERNAL_SERVER_ERROR.getCode();
79 String errorMsg = INTERNAL_SERVER_ERROR.getMsg(); 82 String errorMsg = INTERNAL_SERVER_ERROR.getMsg();
80 if (ex instanceof ServiceException) { 83 if (ex instanceof ServiceException) {
@@ -122,6 +125,7 @@ public class WalletPayClient extends AbstractPayClient&lt;NonePayClientConfig&gt; { @@ -122,6 +125,7 @@ public class WalletPayClient extends AbstractPayClient&lt;NonePayClientConfig&gt; {
122 } 125 }
123 126
124 @Override 127 @Override
  128 + @SuppressWarnings("PatternVariableCanBeUsed")
125 protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { 129 protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
126 try { 130 try {
127 PayWalletTransactionDO payWalletTransaction = wallService.orderRefund(reqDTO.getOutRefundNo(), 131 PayWalletTransactionDO payWalletTransaction = wallService.orderRefund(reqDTO.getOutRefundNo(),
@@ -129,7 +133,7 @@ public class WalletPayClient extends AbstractPayClient&lt;NonePayClientConfig&gt; { @@ -129,7 +133,7 @@ public class WalletPayClient extends AbstractPayClient&lt;NonePayClientConfig&gt; {
129 return PayRefundRespDTO.successOf(payWalletTransaction.getNo(), payWalletTransaction.getCreateTime(), 133 return PayRefundRespDTO.successOf(payWalletTransaction.getNo(), payWalletTransaction.getCreateTime(),
130 reqDTO.getOutRefundNo(), payWalletTransaction); 134 reqDTO.getOutRefundNo(), payWalletTransaction);
131 } catch (Throwable ex) { 135 } catch (Throwable ex) {
132 - log.error("[doUnifiedRefund] 失败", ex); 136 + log.error("[doUnifiedRefund][reqDOT({}) 异常]", reqDTO, ex);
133 Integer errorCode = INTERNAL_SERVER_ERROR.getCode(); 137 Integer errorCode = INTERNAL_SERVER_ERROR.getCode();
134 String errorMsg = INTERNAL_SERVER_ERROR.getMsg(); 138 String errorMsg = INTERNAL_SERVER_ERROR.getMsg();
135 if (ex instanceof ServiceException) { 139 if (ex instanceof ServiceException) {
@@ -177,18 +181,71 @@ public class WalletPayClient extends AbstractPayClient&lt;NonePayClientConfig&gt; { @@ -177,18 +181,71 @@ public class WalletPayClient extends AbstractPayClient&lt;NonePayClientConfig&gt; {
177 } 181 }
178 182
179 @Override 183 @Override
180 - protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body, Map<String, String> headers) {  
181 - throw new UnsupportedOperationException("未实现"); 184 + @SuppressWarnings("PatternVariableCanBeUsed")
  185 + public PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
  186 + try {
  187 + Long walletId = Long.parseLong(reqDTO.getUserAccount());
  188 + PayWalletTransactionDO transaction = wallService.addWalletBalance(walletId, String.valueOf(reqDTO.getOutTransferNo()),
  189 + PayWalletBizTypeEnum.TRANSFER, reqDTO.getPrice());
  190 + return PayTransferRespDTO.successOf(transaction.getNo(), transaction.getCreateTime(),
  191 + reqDTO.getOutTransferNo(), transaction);
  192 + } catch (Throwable ex) {
  193 + log.error("[doUnifiedTransfer][reqDTO({}) 异常]", reqDTO, ex);
  194 + Integer errorCode = INTERNAL_SERVER_ERROR.getCode();
  195 + String errorMsg = INTERNAL_SERVER_ERROR.getMsg();
  196 + if (ex instanceof ServiceException) {
  197 + ServiceException serviceException = (ServiceException) ex;
  198 + errorCode = serviceException.getCode();
  199 + errorMsg = serviceException.getMessage();
  200 + }
  201 + return PayTransferRespDTO.closedOf(String.valueOf(errorCode), errorMsg,
  202 + reqDTO.getOutTransferNo(), "");
  203 + }
182 } 204 }
183 205
184 @Override 206 @Override
185 - public PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {  
186 - throw new UnsupportedOperationException("待实现"); 207 + protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body, Map<String, String> headers) {
  208 + throw new UnsupportedOperationException("钱包支付无转账回调");
187 } 209 }
188 210
189 @Override 211 @Override
190 protected PayTransferRespDTO doGetTransfer(String outTradeNo) { 212 protected PayTransferRespDTO doGetTransfer(String outTradeNo) {
191 - throw new UnsupportedOperationException("待实现"); 213 + if (transferService == null) {
  214 + transferService = SpringUtil.getBean(PayTransferService.class);
  215 + }
  216 + // 获取转账单
  217 + PayTransferDO transfer = transferService.getTransferByNo(outTradeNo);
  218 + // 转账单不存在,返回关闭状态
  219 + if (transfer == null) {
  220 + return PayTransferRespDTO.closedOf(String.valueOf(PAY_ORDER_EXTENSION_NOT_FOUND.getCode()),
  221 + PAY_ORDER_EXTENSION_NOT_FOUND.getMsg(), outTradeNo, "");
  222 + }
  223 + // 关闭状态
  224 + if (PayTransferStatusRespEnum.isClosed(transfer.getStatus())) {
  225 + return PayTransferRespDTO.closedOf(transfer.getChannelErrorCode(),
  226 + transfer.getChannelErrorMsg(), outTradeNo, "");
  227 + }
  228 + // 成功状态
  229 + if (PayTransferStatusRespEnum.isSuccess(transfer.getStatus())) {
  230 + PayWalletTransactionDO walletTransaction = walletTransactionService.getWalletTransaction(
  231 + String.valueOf(transfer.getId()), PayWalletBizTypeEnum.TRANSFER);
  232 + Assert.notNull(walletTransaction, "转账单 {} 钱包流水不能为空", outTradeNo);
  233 + return PayTransferRespDTO.successOf(walletTransaction.getNo(), walletTransaction.getCreateTime(),
  234 + outTradeNo, walletTransaction);
  235 + }
  236 + // 处理中状态
  237 + if (PayTransferStatusRespEnum.isProcessing(transfer.getStatus())) {
  238 + return PayTransferRespDTO.processingOf(transfer.getChannelTransferNo(),
  239 + outTradeNo, transfer);
  240 + }
  241 + // 等待状态
  242 + if (transfer.getStatus().equals(PayTransferStatusRespEnum.WAITING.getStatus())) {
  243 + return PayTransferRespDTO.waitingOf(transfer.getChannelTransferNo(),
  244 + outTradeNo, transfer);
  245 + }
  246 + // 其它状态为无效状态
  247 + log.error("[doGetTransfer] 转账单 {} 的状态不正确", outTradeNo);
  248 + throw new IllegalStateException(String.format("转账单[%s] 状态不正确", outTradeNo));
192 } 249 }
193 250
194 } 251 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoTransferServiceImpl.java
@@ -57,11 +57,16 @@ public class PayDemoTransferServiceImpl implements PayDemoWithdrawService { @@ -57,11 +57,16 @@ public class PayDemoTransferServiceImpl implements PayDemoWithdrawService {
57 demoTransferMapper.insert(withdraw); 57 demoTransferMapper.insert(withdraw);
58 58
59 // 2.1 创建支付单 59 // 2.1 创建支付单
60 - Long payTransferId = payTransferApi.createTransfer(new PayTransferCreateReqDTO() 60 + PayTransferCreateReqDTO transferReqDTO = new PayTransferCreateReqDTO()
61 .setAppKey(PAY_APP_KEY).setChannelCode(withdraw.getTransferChannelCode()).setUserIp(getClientIP()) // 支付应用 61 .setAppKey(PAY_APP_KEY).setChannelCode(withdraw.getTransferChannelCode()).setUserIp(getClientIP()) // 支付应用
62 .setMerchantOrderId(String.valueOf(withdraw.getId())) // 业务的订单编号 62 .setMerchantOrderId(String.valueOf(withdraw.getId())) // 业务的订单编号
63 .setSubject(reqVO.getSubject()).setPrice(withdraw.getPrice()) // 价格信息 63 .setSubject(reqVO.getSubject()).setPrice(withdraw.getPrice()) // 价格信息
64 - .setUserAccount(reqVO.getUserAccount()).setUserName(reqVO.getUserName())); // 收款信息 64 + .setUserAccount(reqVO.getUserAccount()).setUserName(reqVO.getUserName()); // 收款信息
  65 + if (ObjectUtil.equal(reqVO.getType(), PayDemoWithdrawTypeEnum.WECHAT.getType())) {
  66 + transferReqDTO.setChannelExtras(PayTransferCreateReqDTO.buildWeiXinChannelExtra1000(
  67 + "测试活动", "测试奖励"));
  68 + }
  69 + Long payTransferId = payTransferApi.createTransfer(transferReqDTO);
65 // 2.2 更新转账单到 demo 示例提现单 70 // 2.2 更新转账单到 demo 示例提现单
66 demoTransferMapper.updateById(new PayDemoWithdrawDO().setId(withdraw.getId()) 71 demoTransferMapper.updateById(new PayDemoWithdrawDO().setId(withdraw.getId())
67 .setPayTransferId(payTransferId)); 72 .setPayTransferId(payTransferId));
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferService.java
@@ -29,6 +29,14 @@ public interface PayTransferService { @@ -29,6 +29,14 @@ public interface PayTransferService {
29 PayTransferDO getTransfer(Long id); 29 PayTransferDO getTransfer(Long id);
30 30
31 /** 31 /**
  32 + * 根据转账单号获取转账单
  33 + *
  34 + * @param no 转账单号
  35 + * @return 转账单
  36 + */
  37 + PayTransferDO getTransferByNo(String no);
  38 +
  39 + /**
32 * 获得转账单分页 40 * 获得转账单分页
33 * 41 *
34 * @param pageReqVO 分页查询 42 * @param pageReqVO 分页查询
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java
@@ -239,6 +239,11 @@ public class PayTransferServiceImpl implements PayTransferService { @@ -239,6 +239,11 @@ public class PayTransferServiceImpl implements PayTransferService {
239 } 239 }
240 240
241 @Override 241 @Override
  242 + public PayTransferDO getTransferByNo(String no) {
  243 + return transferMapper.selectByNo(no);
  244 + }
  245 +
  246 + @Override
242 public PageResult<PayTransferDO> getTransferPage(PayTransferPageReqVO pageReqVO) { 247 public PageResult<PayTransferDO> getTransferPage(PayTransferPageReqVO pageReqVO) {
243 return transferMapper.selectPage(pageReqVO); 248 return transferMapper.selectPage(pageReqVO);
244 } 249 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletService.java
@@ -41,12 +41,11 @@ public interface PayWalletService { @@ -41,12 +41,11 @@ public interface PayWalletService {
41 /** 41 /**
42 * 钱包订单支付 42 * 钱包订单支付
43 * 43 *
44 - * @param userId 用户 id  
45 - * @param userType 用户类型 44 + * @param walletId 钱包编号
46 * @param outTradeNo 外部订单号 45 * @param outTradeNo 外部订单号
47 * @param price 金额 46 * @param price 金额
48 */ 47 */
49 - PayWalletTransactionDO orderPay(Long userId, Integer userType, String outTradeNo, Integer price); 48 + PayWalletTransactionDO orderPay(Long walletId, String outTradeNo, Integer price);
50 49
51 /** 50 /**
52 * 钱包订单支付退款 51 * 钱包订单支付退款
@@ -60,8 +59,8 @@ public interface PayWalletService { @@ -60,8 +59,8 @@ public interface PayWalletService {
60 /** 59 /**
61 * 扣减钱包余额 60 * 扣减钱包余额
62 * 61 *
63 - * @param walletId 钱包 id  
64 - * @param bizId 业务关联 id 62 + * @param walletId 钱包编号
  63 + * @param bizId 业务关联编号
65 * @param bizType 业务关联分类 64 * @param bizType 业务关联分类
66 * @param price 扣减金额 65 * @param price 扣减金额
67 * @return 钱包流水 66 * @return 钱包流水
@@ -72,8 +71,8 @@ public interface PayWalletService { @@ -72,8 +71,8 @@ public interface PayWalletService {
72 /** 71 /**
73 * 增加钱包余额 72 * 增加钱包余额
74 * 73 *
75 - * @param walletId 钱包 id  
76 - * @param bizId 业务关联 id 74 + * @param walletId 钱包编号
  75 + * @param bizId 业务关联编号
77 * @param bizType 业务关联分类 76 * @param bizType 业务关联分类
78 * @param price 增加金额 77 * @param price 增加金额
79 * @return 钱包流水 78 * @return 钱包流水
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java
@@ -81,13 +81,13 @@ public class PayWalletServiceImpl implements PayWalletService { @@ -81,13 +81,13 @@ public class PayWalletServiceImpl implements PayWalletService {
81 81
82 @Override 82 @Override
83 @Transactional(rollbackFor = Exception.class) 83 @Transactional(rollbackFor = Exception.class)
84 - public PayWalletTransactionDO orderPay(Long userId, Integer userType, String outTradeNo, Integer price) { 84 + public PayWalletTransactionDO orderPay(Long walletId, String outTradeNo, Integer price) {
85 // 1. 判断支付交易拓展单是否存 85 // 1. 判断支付交易拓展单是否存
86 PayOrderExtensionDO orderExtension = orderService.getOrderExtensionByNo(outTradeNo); 86 PayOrderExtensionDO orderExtension = orderService.getOrderExtensionByNo(outTradeNo);
87 if (orderExtension == null) { 87 if (orderExtension == null) {
88 throw exception(PAY_ORDER_EXTENSION_NOT_FOUND); 88 throw exception(PAY_ORDER_EXTENSION_NOT_FOUND);
89 } 89 }
90 - PayWalletDO wallet = getOrCreateWallet(userId, userType); 90 + PayWalletDO wallet = walletMapper.selectById(walletId);
91 // 2. 扣减余额 91 // 2. 扣减余额
92 return reduceWalletBalance(wallet.getId(), orderExtension.getOrderId(), PAYMENT, price); 92 return reduceWalletBalance(wallet.getId(), orderExtension.getOrderId(), PAYMENT, price);
93 } 93 }
@@ -198,7 +198,7 @@ public class PayWalletServiceImpl implements PayWalletService { @@ -198,7 +198,7 @@ public class PayWalletServiceImpl implements PayWalletService {
198 break; 198 break;
199 } 199 }
200 case UPDATE_BALANCE: // 更新余额 200 case UPDATE_BALANCE: // 更新余额
201 - case BROKERAGE_WITHDRAW: // 分佣提现 201 + case TRANSFER: // 分佣提现
202 walletMapper.updateWhenAdd(payWallet.getId(), price); 202 walletMapper.updateWhenAdd(payWallet.getId(), price);
203 break; 203 break;
204 default: { 204 default: {
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferUnifiedReqDTO.java
@@ -58,6 +58,8 @@ public class PayTransferUnifiedReqDTO { @@ -58,6 +58,8 @@ public class PayTransferUnifiedReqDTO {
58 58
59 /** 59 /**
60 * 支付渠道的额外参数 60 * 支付渠道的额外参数
  61 + *
  62 + * 微信支付:sceneId 和 scene_report_infos 字段,必须传递;参考 <a href="https://pay.weixin.qq.com/doc/v3/merchant/4012711988#%EF%BC%883%EF%BC%89%E6%8C%89%E8%BD%AC%E8%B4%A6%E5%9C%BA%E6%99%AF%E6%8A%A5%E5%A4%87%E8%83%8C%E6%99%AF%E4%BF%A1%E6%81%AF">按转账场景报备背景信息</>
61 */ 63 */
62 private Map<String, String> channelExtras; 64 private Map<String, String> channelExtras;
63 65
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
@@ -7,6 +7,7 @@ import cn.hutool.core.date.LocalDateTimeUtil; @@ -7,6 +7,7 @@ import cn.hutool.core.date.LocalDateTimeUtil;
7 import cn.hutool.core.date.TemporalAccessorUtil; 7 import cn.hutool.core.date.TemporalAccessorUtil;
8 import cn.hutool.core.util.StrUtil; 8 import cn.hutool.core.util.StrUtil;
9 import cn.iocoder.yudao.framework.common.util.io.FileUtils; 9 import cn.iocoder.yudao.framework.common.util.io.FileUtils;
  10 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
10 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 11 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
11 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 12 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
12 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 13 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
@@ -20,10 +21,9 @@ import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; @@ -20,10 +21,9 @@ import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
20 import com.github.binarywang.wxpay.bean.notify.*; 21 import com.github.binarywang.wxpay.bean.notify.*;
21 import com.github.binarywang.wxpay.bean.request.*; 22 import com.github.binarywang.wxpay.bean.request.*;
22 import com.github.binarywang.wxpay.bean.result.*; 23 import com.github.binarywang.wxpay.bean.result.*;
23 -import com.github.binarywang.wxpay.bean.transfer.QueryTransferBatchesRequest;  
24 -import com.github.binarywang.wxpay.bean.transfer.QueryTransferBatchesResult;  
25 -import com.github.binarywang.wxpay.bean.transfer.TransferBatchesRequest;  
26 -import com.github.binarywang.wxpay.bean.transfer.TransferBatchesResult; 24 +import com.github.binarywang.wxpay.bean.transfer.TransferBillsGetResult;
  25 +import com.github.binarywang.wxpay.bean.transfer.TransferBillsRequest;
  26 +import com.github.binarywang.wxpay.bean.transfer.TransferBillsResult;
27 import com.github.binarywang.wxpay.config.WxPayConfig; 27 import com.github.binarywang.wxpay.config.WxPayConfig;
28 import com.github.binarywang.wxpay.exception.WxPayException; 28 import com.github.binarywang.wxpay.exception.WxPayException;
29 import com.github.binarywang.wxpay.service.WxPayService; 29 import com.github.binarywang.wxpay.service.WxPayService;
@@ -32,8 +32,6 @@ import lombok.extern.slf4j.Slf4j; @@ -32,8 +32,6 @@ import lombok.extern.slf4j.Slf4j;
32 32
33 import java.time.LocalDateTime; 33 import java.time.LocalDateTime;
34 import java.time.ZoneId; 34 import java.time.ZoneId;
35 -import java.util.Collections;  
36 -import java.util.List;  
37 import java.util.Map; 35 import java.util.Map;
38 import java.util.Objects; 36 import java.util.Objects;
39 37
@@ -463,53 +461,57 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -463,53 +461,57 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
463 461
464 @Override 462 @Override
465 protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws WxPayException { 463 protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws WxPayException {
466 - // 1. 构建 TransferBatchesRequest 请求  
467 - List<TransferBatchesRequest.TransferDetail> transferDetailList = Collections.singletonList(  
468 - TransferBatchesRequest.TransferDetail.newBuilder()  
469 - .outDetailNo(reqDTO.getOutTransferNo())  
470 - .transferAmount(reqDTO.getPrice())  
471 - .transferRemark(reqDTO.getSubject())  
472 - .openid(reqDTO.getUserAccount())  
473 - .build());  
474 - // TODO @luchi:能不能我们搞个 TransferBatchesRequestX extends TransferBatchesRequest,这样更简洁一点。  
475 - TransferBatchesRequest transferBatches = TransferBatchesRequest.newBuilder() 464 + // 1. 构建 TransferBillsRequest 请求
  465 + TransferBillsRequest request = TransferBillsRequest.newBuilder()
476 .appid(this.config.getAppId()) 466 .appid(this.config.getAppId())
477 - .outBatchNo(reqDTO.getOutTransferNo())  
478 - .batchName(reqDTO.getSubject())  
479 - .batchRemark(reqDTO.getSubject())  
480 - .totalAmount(reqDTO.getPrice())  
481 - .totalNum(transferDetailList.size())  
482 - .transferDetailList(transferDetailList).build()  
483 - .setNotifyUrl(reqDTO.getNotifyUrl()); 467 + .outBillNo(reqDTO.getOutTransferNo())
  468 + .transferAmount(reqDTO.getPrice())
  469 + .transferRemark(reqDTO.getSubject())
  470 + .transferSceneId(reqDTO.getChannelExtras().get("sceneId"))
  471 + .openid(reqDTO.getUserAccount())
  472 + .userName(reqDTO.getUserName())
  473 + .transferSceneReportInfos(JsonUtils.parseArray(reqDTO.getChannelExtras().get("sceneReportInfos"),
  474 + TransferBillsRequest.TransferSceneReportInfo.class))
  475 + .notifyUrl(reqDTO.getNotifyUrl())
  476 + .build();
  477 + // 特殊:微信转账,必须 0.3 元起,才允许传入姓名
  478 + if (reqDTO.getPrice() < 30) {
  479 + request.setUserName(null);
  480 + }
  481 +
484 // 2.1 执行请求 482 // 2.1 执行请求
485 - TransferBatchesResult transferBatchesResult = client.getTransferService().transferBatches(transferBatches);  
486 - // 2.2 创建返回结果  
487 - return PayTransferRespDTO.processingOf(transferBatchesResult.getBatchId(), reqDTO.getOutTransferNo(), transferBatchesResult); 483 + try {
  484 + TransferBillsResult response = client.getTransferService().transferBills(request);
  485 + System.out.println(response);
  486 +
  487 + // 2.2 创建返回结果
  488 + // TODO @芋艿:这里要解析下;
  489 + return PayTransferRespDTO.processingOf(response.getTransferBillNo(), reqDTO.getOutTransferNo(), response);
  490 + } catch (WxPayException e) {
  491 + log.error("[doUnifiedTransfer][转账({}) 发起微信支付异常", reqDTO, e);
  492 + String errorCode = getErrorCode(e);
  493 + String errorMessage = getErrorMessage(e);
  494 + return PayTransferRespDTO.closedOf(errorCode, errorMessage,
  495 + reqDTO.getOutTransferNo(), e.getXmlString());
  496 + }
488 } 497 }
489 498
490 @Override 499 @Override
491 protected PayTransferRespDTO doGetTransfer(String outTradeNo) throws WxPayException { 500 protected PayTransferRespDTO doGetTransfer(String outTradeNo) throws WxPayException {
492 - QueryTransferBatchesRequest request = QueryTransferBatchesRequest.newBuilder()  
493 - .outBatchNo(outTradeNo).needQueryDetail(true).offset(0).limit(20).detailStatus("ALL")  
494 - .build();  
495 - QueryTransferBatchesResult response = client.getTransferService().transferBatchesOutBatchNo(request);  
496 - QueryTransferBatchesResult.TransferBatch transferBatch = response.getTransferBatch();  
497 - if (Objects.equals("FINISHED", transferBatch.getBatchStatus())) {  
498 - // 明细中全部成功则成功,任一失败则失败  
499 - if (response.getTransferDetailList().stream().allMatch(detail -> Objects.equals("SUCCESS", detail.getDetailStatus()))) {  
500 - return PayTransferRespDTO.successOf(transferBatch.getBatchId(), parseDateV3(transferBatch.getUpdateTime()),  
501 - transferBatch.getOutBatchNo(), response);  
502 - }  
503 - if (response.getTransferDetailList().stream().anyMatch(detail -> Objects.equals("FAIL", detail.getDetailStatus()))) {  
504 - return PayTransferRespDTO.closedOf(transferBatch.getBatchStatus(), transferBatch.getCloseReason(),  
505 - transferBatch.getOutBatchNo(), response);  
506 - } 501 + // 1. 执行请求
  502 + TransferBillsGetResult response = client.getTransferService().getBillsByTransferBillNo(outTradeNo);
  503 +
  504 + // 2. 创建返回结果
  505 + String state = response.getState();
  506 + if (ObjectUtils.equalsAny(state, "ACCEPTED", "PROCESSING", "WAIT_USER_CONFIRM", "TRANSFERING")) {
  507 + return PayTransferRespDTO.processingOf(response.getTransferBillNo(), response.getOutBillNo(), response);
507 } 508 }
508 - if (Objects.equals("CLOSED", transferBatch.getBatchStatus())) {  
509 - return PayTransferRespDTO.closedOf(transferBatch.getBatchStatus(), transferBatch.getCloseReason(),  
510 - transferBatch.getOutBatchNo(), response); 509 + if (Objects.equals("SUCCESS", state)) {
  510 + return PayTransferRespDTO.successOf(response.getTransferBillNo(), parseDateV3(response.getUpdateTime()),
  511 + response.getOutBillNo(), response);
511 } 512 }
512 - return PayTransferRespDTO.processingOf(transferBatch.getBatchId(), transferBatch.getOutBatchNo(), response); 513 + return PayTransferRespDTO.closedOf(state, response.getFailReason(),
  514 + response.getOutBillNo(), response);
513 } 515 }
514 516
515 // ========== 各种工具方法 ========== 517 // ========== 各种工具方法 ==========
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesRequest.java deleted
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 -}