Commit 38c76806a3da36f8b97a5943c1410c94011623b0
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 | 113 | if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(withdraw.getType())) { |
| 114 | 114 | payWalletApi.addWalletBalance(new PayWalletAddBalanceReqDTO() |
| 115 | 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 | 117 | .setPrice(withdraw.getPrice())); |
| 118 | 118 | // 1.2 微信 API |
| 119 | 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 | 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 | 5 | import jakarta.validation.constraints.Min; |
| 4 | 6 | import jakarta.validation.constraints.NotEmpty; |
| 5 | 7 | import jakarta.validation.constraints.NotNull; |
| 6 | 8 | import lombok.Data; |
| 7 | 9 | |
| 10 | +import java.util.ArrayList; | |
| 11 | +import java.util.HashMap; | |
| 12 | +import java.util.List; | |
| 8 | 13 | import java.util.Map; |
| 9 | 14 | |
| 10 | 15 | /** |
| ... | ... | @@ -70,4 +75,46 @@ public class PayTransferCreateReqDTO { |
| 70 | 75 | */ |
| 71 | 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
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 | 2 | POST {{baseUrl}}/pay/demo-withdraw/create |
| 3 | 3 | Authorization: Bearer {{token}} |
| 4 | 4 | Content-Type: application/json |
| ... | ... | @@ -10,4 +10,31 @@ tenant-id: {{adminTenantId}} |
| 10 | 10 | "price": 10, |
| 11 | 11 | "userAccount": "oespxk7368@sandbox.com", |
| 12 | 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 | 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 | 27 | private String userAccount; |
| 28 | 28 | |
| 29 | 29 | @Schema(description = "收款人姓名", example = "test1") |
| 30 | - @NotBlank(message = "收款人姓名不能为空") | |
| 31 | 30 | private String userName; |
| 32 | 31 | |
| 33 | 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 | 12 | import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; |
| 13 | 13 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 14 | 14 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; |
| 15 | +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; | |
| 15 | 16 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 16 | 17 | import cn.iocoder.yudao.module.pay.framework.pay.core.WalletPayClient; |
| 17 | 18 | import cn.iocoder.yudao.module.pay.service.app.PayAppService; |
| 18 | 19 | import cn.iocoder.yudao.module.pay.service.order.PayOrderService; |
| 20 | +import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService; | |
| 19 | 21 | import com.google.common.collect.Maps; |
| 20 | 22 | import io.swagger.v3.oas.annotations.Operation; |
| 21 | 23 | import io.swagger.v3.oas.annotations.Parameter; |
| ... | ... | @@ -51,6 +53,8 @@ public class PayOrderController { |
| 51 | 53 | private PayOrderService orderService; |
| 52 | 54 | @Resource |
| 53 | 55 | private PayAppService appService; |
| 56 | + @Resource | |
| 57 | + private PayWalletService payWalletService; | |
| 54 | 58 | |
| 55 | 59 | @GetMapping("/get") |
| 56 | 60 | @Operation(summary = "获得支付订单") |
| ... | ... | @@ -92,11 +96,11 @@ public class PayOrderController { |
| 92 | 96 | public CommonResult<PayOrderSubmitRespVO> submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) { |
| 93 | 97 | // 1. 钱包支付事,需要额外传 user_id 和 user_type |
| 94 | 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 | 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 | import cn.iocoder.yudao.module.pay.controller.app.order.vo.AppPayOrderSubmitRespVO; |
| 10 | 10 | import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert; |
| 11 | 11 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 12 | +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; | |
| 12 | 13 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 13 | 14 | import cn.iocoder.yudao.module.pay.framework.pay.core.WalletPayClient; |
| 14 | 15 | import cn.iocoder.yudao.module.pay.service.order.PayOrderService; |
| 16 | +import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService; | |
| 15 | 17 | import com.google.common.collect.Maps; |
| 16 | 18 | import io.swagger.v3.oas.annotations.Operation; |
| 17 | 19 | import io.swagger.v3.oas.annotations.Parameter; |
| ... | ... | @@ -22,7 +24,6 @@ import lombok.extern.slf4j.Slf4j; |
| 22 | 24 | import org.springframework.validation.annotation.Validated; |
| 23 | 25 | import org.springframework.web.bind.annotation.*; |
| 24 | 26 | |
| 25 | -import java.util.Map; | |
| 26 | 27 | import java.util.Objects; |
| 27 | 28 | |
| 28 | 29 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| ... | ... | @@ -39,6 +40,8 @@ public class AppPayOrderController { |
| 39 | 40 | |
| 40 | 41 | @Resource |
| 41 | 42 | private PayOrderService payOrderService; |
| 43 | + @Resource | |
| 44 | + private PayWalletService payWalletService; | |
| 42 | 45 | |
| 43 | 46 | @GetMapping("/get") |
| 44 | 47 | @Operation(summary = "获得支付订单") |
| ... | ... | @@ -63,11 +66,11 @@ public class AppPayOrderController { |
| 63 | 66 | public CommonResult<AppPayOrderSubmitRespVO> submitPayOrder(@RequestBody AppPayOrderSubmitReqVO reqVO) { |
| 64 | 67 | // 1. 钱包支付事,需要额外传 user_id 和 user_type |
| 65 | 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 | 76 | // 2. 提交支付 | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/transfer/PayTransferMapper.java
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 | 14 | import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig; |
| 15 | 15 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; |
| 16 | 16 | import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; |
| 17 | +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferStatusRespEnum; | |
| 17 | 18 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; |
| 18 | 19 | import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; |
| 20 | +import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO; | |
| 19 | 21 | import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; |
| 20 | 22 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 21 | 23 | import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; |
| 22 | 24 | import cn.iocoder.yudao.module.pay.service.order.PayOrderService; |
| 23 | 25 | import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; |
| 26 | +import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService; | |
| 24 | 27 | import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService; |
| 25 | 28 | import cn.iocoder.yudao.module.pay.service.wallet.PayWalletTransactionService; |
| 26 | 29 | import lombok.extern.slf4j.Slf4j; |
| ... | ... | @@ -39,13 +42,14 @@ import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.REFUND_NOT_FO |
| 39 | 42 | @Slf4j |
| 40 | 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 | 47 | private PayWalletService wallService; |
| 46 | 48 | private PayWalletTransactionService walletTransactionService; |
| 49 | + | |
| 47 | 50 | private PayOrderService orderService; |
| 48 | 51 | private PayRefundService refundService; |
| 52 | + private PayTransferService transferService; | |
| 49 | 53 | |
| 50 | 54 | public WalletPayClient(Long channelId, NonePayClientConfig config) { |
| 51 | 55 | super(channelId, PayChannelEnum.WALLET.getCode(), config); |
| ... | ... | @@ -62,19 +66,18 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> { |
| 62 | 66 | } |
| 63 | 67 | |
| 64 | 68 | @Override |
| 69 | + @SuppressWarnings("PatternVariableCanBeUsed") | |
| 65 | 70 | protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { |
| 66 | 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 | 76 | return PayOrderRespDTO.successOf(transaction.getNo(), transaction.getCreator(), |
| 74 | 77 | transaction.getCreateTime(), |
| 75 | 78 | reqDTO.getOutTradeNo(), transaction); |
| 76 | 79 | } catch (Throwable ex) { |
| 77 | - log.error("[doUnifiedOrder] 失败", ex); | |
| 80 | + log.error("[doUnifiedOrder][reqDTO({}) 异常]", reqDTO, ex); | |
| 78 | 81 | Integer errorCode = INTERNAL_SERVER_ERROR.getCode(); |
| 79 | 82 | String errorMsg = INTERNAL_SERVER_ERROR.getMsg(); |
| 80 | 83 | if (ex instanceof ServiceException) { |
| ... | ... | @@ -122,6 +125,7 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> { |
| 122 | 125 | } |
| 123 | 126 | |
| 124 | 127 | @Override |
| 128 | + @SuppressWarnings("PatternVariableCanBeUsed") | |
| 125 | 129 | protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { |
| 126 | 130 | try { |
| 127 | 131 | PayWalletTransactionDO payWalletTransaction = wallService.orderRefund(reqDTO.getOutRefundNo(), |
| ... | ... | @@ -129,7 +133,7 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> { |
| 129 | 133 | return PayRefundRespDTO.successOf(payWalletTransaction.getNo(), payWalletTransaction.getCreateTime(), |
| 130 | 134 | reqDTO.getOutRefundNo(), payWalletTransaction); |
| 131 | 135 | } catch (Throwable ex) { |
| 132 | - log.error("[doUnifiedRefund] 失败", ex); | |
| 136 | + log.error("[doUnifiedRefund][reqDOT({}) 异常]", reqDTO, ex); | |
| 133 | 137 | Integer errorCode = INTERNAL_SERVER_ERROR.getCode(); |
| 134 | 138 | String errorMsg = INTERNAL_SERVER_ERROR.getMsg(); |
| 135 | 139 | if (ex instanceof ServiceException) { |
| ... | ... | @@ -177,18 +181,71 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> { |
| 177 | 181 | } |
| 178 | 182 | |
| 179 | 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 | 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 | 211 | @Override |
| 190 | 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 | 57 | demoTransferMapper.insert(withdraw); |
| 58 | 58 | |
| 59 | 59 | // 2.1 创建支付单 |
| 60 | - Long payTransferId = payTransferApi.createTransfer(new PayTransferCreateReqDTO() | |
| 60 | + PayTransferCreateReqDTO transferReqDTO = new PayTransferCreateReqDTO() | |
| 61 | 61 | .setAppKey(PAY_APP_KEY).setChannelCode(withdraw.getTransferChannelCode()).setUserIp(getClientIP()) // 支付应用 |
| 62 | 62 | .setMerchantOrderId(String.valueOf(withdraw.getId())) // 业务的订单编号 |
| 63 | 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 | 70 | // 2.2 更新转账单到 demo 示例提现单 |
| 66 | 71 | demoTransferMapper.updateById(new PayDemoWithdrawDO().setId(withdraw.getId()) |
| 67 | 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 | 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 | 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 | 239 | } |
| 240 | 240 | |
| 241 | 241 | @Override |
| 242 | + public PayTransferDO getTransferByNo(String no) { | |
| 243 | + return transferMapper.selectByNo(no); | |
| 244 | + } | |
| 245 | + | |
| 246 | + @Override | |
| 242 | 247 | public PageResult<PayTransferDO> getTransferPage(PayTransferPageReqVO pageReqVO) { |
| 243 | 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 | 41 | /** |
| 42 | 42 | * 钱包订单支付 |
| 43 | 43 | * |
| 44 | - * @param userId 用户 id | |
| 45 | - * @param userType 用户类型 | |
| 44 | + * @param walletId 钱包编号 | |
| 46 | 45 | * @param outTradeNo 外部订单号 |
| 47 | 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 | 59 | /** |
| 61 | 60 | * 扣减钱包余额 |
| 62 | 61 | * |
| 63 | - * @param walletId 钱包 id | |
| 64 | - * @param bizId 业务关联 id | |
| 62 | + * @param walletId 钱包编号 | |
| 63 | + * @param bizId 业务关联编号 | |
| 65 | 64 | * @param bizType 业务关联分类 |
| 66 | 65 | * @param price 扣减金额 |
| 67 | 66 | * @return 钱包流水 |
| ... | ... | @@ -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 | 76 | * @param bizType 业务关联分类 |
| 78 | 77 | * @param price 增加金额 |
| 79 | 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 | 81 | |
| 82 | 82 | @Override |
| 83 | 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 | 85 | // 1. 判断支付交易拓展单是否存 |
| 86 | 86 | PayOrderExtensionDO orderExtension = orderService.getOrderExtensionByNo(outTradeNo); |
| 87 | 87 | if (orderExtension == null) { |
| 88 | 88 | throw exception(PAY_ORDER_EXTENSION_NOT_FOUND); |
| 89 | 89 | } |
| 90 | - PayWalletDO wallet = getOrCreateWallet(userId, userType); | |
| 90 | + PayWalletDO wallet = walletMapper.selectById(walletId); | |
| 91 | 91 | // 2. 扣减余额 |
| 92 | 92 | return reduceWalletBalance(wallet.getId(), orderExtension.getOrderId(), PAYMENT, price); |
| 93 | 93 | } |
| ... | ... | @@ -198,7 +198,7 @@ public class PayWalletServiceImpl implements PayWalletService { |
| 198 | 198 | break; |
| 199 | 199 | } |
| 200 | 200 | case UPDATE_BALANCE: // 更新余额 |
| 201 | - case BROKERAGE_WITHDRAW: // 分佣提现 | |
| 201 | + case TRANSFER: // 分佣提现 | |
| 202 | 202 | walletMapper.updateWhenAdd(payWallet.getId(), price); |
| 203 | 203 | break; |
| 204 | 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 | 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 | 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 | 7 | import cn.hutool.core.date.TemporalAccessorUtil; |
| 8 | 8 | import cn.hutool.core.util.StrUtil; |
| 9 | 9 | import cn.iocoder.yudao.framework.common.util.io.FileUtils; |
| 10 | +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | |
| 10 | 11 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| 11 | 12 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; |
| 12 | 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 | 21 | import com.github.binarywang.wxpay.bean.notify.*; |
| 21 | 22 | import com.github.binarywang.wxpay.bean.request.*; |
| 22 | 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 | 27 | import com.github.binarywang.wxpay.config.WxPayConfig; |
| 28 | 28 | import com.github.binarywang.wxpay.exception.WxPayException; |
| 29 | 29 | import com.github.binarywang.wxpay.service.WxPayService; |
| ... | ... | @@ -32,8 +32,6 @@ import lombok.extern.slf4j.Slf4j; |
| 32 | 32 | |
| 33 | 33 | import java.time.LocalDateTime; |
| 34 | 34 | import java.time.ZoneId; |
| 35 | -import java.util.Collections; | |
| 36 | -import java.util.List; | |
| 37 | 35 | import java.util.Map; |
| 38 | 36 | import java.util.Objects; |
| 39 | 37 | |
| ... | ... | @@ -463,53 +461,57 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC |
| 463 | 461 | |
| 464 | 462 | @Override |
| 465 | 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 | 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 | 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 | 499 | @Override |
| 491 | 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 | -} |