fix: route Aslan merchant recharge to freight wallet

This commit is contained in:
zhx 2026-07-14 18:41:32 +08:00
parent bd5f8305df
commit cbb12ea13a
5 changed files with 224 additions and 13 deletions

View File

@ -47,12 +47,15 @@ public class PayWebH5OptionsQryExe {
private final SysCountryCodeClient sysCountryCodeClient;
private final UserProfileAndCountryQueryExe userProfileAndCountryQueryExe;
private final PayApplicationCommodityService payApplicationCommodityService;
private final PayWebH5WalletTypeResolver walletTypeResolver;
public PayWebH5ContextCO context(PayWebUserCmd cmd) {
defaultType(cmd);
UserProfileAndCountryCO source = userProfileAndCountryQueryExe.h5Context(cmd);
WebSiteUseProfileCO userProfile = source.getUserProfile();
ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_RECORD_INFO, userProfile);
PayWebH5WalletTypeResolver.WalletType walletType = walletTypeResolver.resolve(
userProfile.getId());
return new PayWebH5ContextCO()
.setAuthMode("display_user_id")
@ -65,9 +68,9 @@ public class PayWebH5OptionsQryExe {
.setSysOrigin(userProfile.getSysOrigin())
.setCountryName(userProfile.getCountryName())
.setCountryCode(userProfile.getCountryCode())
.setFreightAgent(userProfile.getFreightAgent())
.setSuperFreightAgent(userProfile.getSuperFreightAgent())
.setAudienceType(resolveAudienceType(userProfile)))
.setFreightAgent(walletType.isFreightAgent())
.setSuperFreightAgent(walletType.isSuperFreightAgent())
.setAudienceType(walletType.getAudienceType()))
.setCountryList(source.getCountryList())
.setRegionId(source.getRegionId());
}
@ -169,16 +172,6 @@ public class PayWebH5OptionsQryExe {
return amount.multiply(BigDecimal.valueOf(scale)).setScale(0, RoundingMode.DOWN).longValue();
}
private String resolveAudienceType(WebSiteUseProfileCO userProfile) {
if (Boolean.TRUE.equals(userProfile.getSuperFreightAgent())) {
return "super_freight_agent";
}
if (Boolean.TRUE.equals(userProfile.getFreightAgent())) {
return "coin_seller";
}
return "normal";
}
private void defaultType(PayWebUserCmd cmd) {
if (Objects.isNull(cmd.getType())) {
cmd.setType(PayApplicationCommodityType.GOLD);

View File

@ -59,6 +59,7 @@ public class PayWebH5PlaceOrderCmdExe {
private final InAppPurchaseDetailsService inAppPurchaseDetailsService;
private final PayApplicationCommodityService payApplicationCommodityService;
private final V5PayOrderStateService v5PayOrderStateService;
private final PayWebH5WalletTypeResolver walletTypeResolver;
public PayWebH5OrderCO execute(PayWebH5PlaceOrderCmd cmd) {
String providerCode = Objects.toString(cmd.getProviderCode(), "").trim()
@ -122,6 +123,9 @@ public class PayWebH5PlaceOrderCmdExe {
Boolean.TRUE.equals(commodity.getShelf()));
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
Objects.isNull(cmd.getType()) || Objects.equals(cmd.getType().name(), commodity.getType()));
// 以服务端钱包身份约束商品类型防止旧 H5 或伪造请求把币商订单送入个人钱包
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
walletTypeResolver.matchesCommodityType(cmd.getUserId(), commodity.getType()));
PayCountry payCountry = payCountryService.getById(cmd.getPayCountryId());
ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_RECORD_INFO, payCountry);

View File

@ -0,0 +1,67 @@
package com.red.circle.order.app.command.pay.web;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.order.inner.model.enums.PayApplicationCommodityType;
import com.red.circle.wallet.inner.endpoint.freight.FreightGoldClient;
import com.red.circle.wallet.inner.model.dto.UserFreightBalanceDTO;
import java.util.Objects;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/**
* Aslan H5 充值用户钱包类型解析器
*
* @author tf
*/
@Component
@RequiredArgsConstructor
public class PayWebH5WalletTypeResolver {
private final FreightGoldClient freightGoldClient;
/**
* 以币商钱包记录判定入账钱包避免用户资料未填充币商标记时错充到个人金币钱包
*/
public WalletType resolve(Long userId) {
UserFreightBalanceDTO balance = ResponseAssert.requiredSuccess(
freightGoldClient.getByUserId(userId));
if (Objects.isNull(balance) || !Boolean.FALSE.equals(balance.getClose())) {
return WalletType.NORMAL;
}
return Boolean.TRUE.equals(balance.getSuperDealer())
? WalletType.SUPER_FREIGHT
: WalletType.FREIGHT;
}
/**
* 以当前有效钱包身份校验订单商品防止币商订单生成个人金币快照
*/
public boolean matchesCommodityType(Long userId, String commodityType) {
return resolve(userId).getCommodityType().eq(commodityType);
}
/**
* H5 币商和超级币商统一购买货运金币支付回调才会进入币商钱包分支
*/
@Getter
public enum WalletType {
NORMAL("normal", false, false, PayApplicationCommodityType.GOLD),
FREIGHT("coin_seller", true, false, PayApplicationCommodityType.FREIGHT_GOLD),
SUPER_FREIGHT("super_freight_agent", true, true,
PayApplicationCommodityType.FREIGHT_GOLD);
private final String audienceType;
private final boolean freightAgent;
private final boolean superFreightAgent;
private final PayApplicationCommodityType commodityType;
WalletType(String audienceType, boolean freightAgent, boolean superFreightAgent,
PayApplicationCommodityType commodityType) {
this.audienceType = audienceType;
this.freightAgent = freightAgent;
this.superFreightAgent = superFreightAgent;
this.commodityType = commodityType;
}
}
}

View File

@ -0,0 +1,45 @@
package com.red.circle.order.app.command.pay.web;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5ContextCO;
import com.red.circle.order.app.dto.clientobject.pay.UserProfileAndCountryCO;
import com.red.circle.order.app.dto.clientobject.pay.WebSiteUseProfileCO;
import com.red.circle.order.app.dto.cmd.PayWebUserCmd;
import com.red.circle.order.infra.database.rds.service.pay.PayApplicationCommodityService;
import com.red.circle.order.infra.database.rds.service.pay.PayApplicationService;
import com.red.circle.order.infra.database.rds.service.pay.PayCountryService;
import com.red.circle.other.inner.endpoint.sys.SysCountryCodeClient;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
public class PayWebH5OptionsQryExeTest {
@Test
public void contextMustUseWalletResolverWhenUserProfileHasNoFreightFlags() {
UserProfileAndCountryQueryExe userQuery = Mockito.mock(UserProfileAndCountryQueryExe.class);
PayWebH5WalletTypeResolver walletTypeResolver = Mockito.mock(
PayWebH5WalletTypeResolver.class);
PayWebH5OptionsQryExe executor = new PayWebH5OptionsQryExe(
Mockito.mock(PayCountryService.class),
Mockito.mock(PayApplicationService.class),
Mockito.mock(SysCountryCodeClient.class),
userQuery,
Mockito.mock(PayApplicationCommodityService.class),
walletTypeResolver);
WebSiteUseProfileCO userProfile = new WebSiteUseProfileCO()
.setId(10001L)
.setAccount("7777")
.setFreightAgent(null)
.setSuperFreightAgent(null);
Mockito.when(userQuery.h5Context(Mockito.any()))
.thenReturn(new UserProfileAndCountryCO().setUserProfile(userProfile));
Mockito.when(walletTypeResolver.resolve(10001L))
.thenReturn(PayWebH5WalletTypeResolver.WalletType.SUPER_FREIGHT);
PayWebH5ContextCO context = executor.context(new PayWebUserCmd());
Assert.assertEquals("super_freight_agent", context.getAccount().getAudienceType());
Assert.assertTrue(context.getAccount().getFreightAgent());
Assert.assertTrue(context.getAccount().getSuperFreightAgent());
}
}

View File

@ -0,0 +1,102 @@
package com.red.circle.order.app.command.pay.web;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.order.inner.model.enums.PayApplicationCommodityType;
import com.red.circle.wallet.inner.endpoint.freight.FreightGoldClient;
import com.red.circle.wallet.inner.model.dto.UserFreightBalanceDTO;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class PayWebH5WalletTypeResolverTest {
private static final Long USER_ID = 10001L;
private FreightGoldClient freightGoldClient;
private PayWebH5WalletTypeResolver resolver;
@Before
public void setUp() {
freightGoldClient = Mockito.mock(FreightGoldClient.class);
resolver = new PayWebH5WalletTypeResolver(freightGoldClient);
}
@Test
public void activeFreightWalletMustUseFreightGold() {
mockBalance(new UserFreightBalanceDTO()
.setClose(false)
.setSuperDealer(false));
PayWebH5WalletTypeResolver.WalletType walletType = resolver.resolve(USER_ID);
Assert.assertEquals("coin_seller", walletType.getAudienceType());
Assert.assertTrue(walletType.isFreightAgent());
Assert.assertFalse(walletType.isSuperFreightAgent());
Assert.assertEquals(PayApplicationCommodityType.FREIGHT_GOLD,
walletType.getCommodityType());
}
@Test
public void activeSuperFreightWalletMustUseFreightGold() {
mockBalance(new UserFreightBalanceDTO()
.setClose(false)
.setSuperDealer(true));
PayWebH5WalletTypeResolver.WalletType walletType = resolver.resolve(USER_ID);
Assert.assertEquals("super_freight_agent", walletType.getAudienceType());
Assert.assertTrue(walletType.isFreightAgent());
Assert.assertTrue(walletType.isSuperFreightAgent());
Assert.assertEquals(PayApplicationCommodityType.FREIGHT_GOLD,
walletType.getCommodityType());
}
@Test
public void activeFreightWalletMustRejectPersonalGoldCommodity() {
mockBalance(new UserFreightBalanceDTO()
.setClose(false)
.setSuperDealer(false));
Assert.assertTrue(resolver.matchesCommodityType(USER_ID, "FREIGHT_GOLD"));
Assert.assertFalse(resolver.matchesCommodityType(USER_ID, "GOLD"));
}
@Test
public void missingFreightWalletMustUsePersonalGold() {
mockBalance(null);
PayWebH5WalletTypeResolver.WalletType walletType = resolver.resolve(USER_ID);
Assert.assertEquals("normal", walletType.getAudienceType());
Assert.assertFalse(walletType.isFreightAgent());
Assert.assertEquals(PayApplicationCommodityType.GOLD, walletType.getCommodityType());
}
@Test
public void missingFreightWalletMustRejectFreightGoldCommodity() {
mockBalance(null);
Assert.assertTrue(resolver.matchesCommodityType(USER_ID, "GOLD"));
Assert.assertFalse(resolver.matchesCommodityType(USER_ID, "FREIGHT_GOLD"));
}
@Test
public void closedFreightWalletMustUsePersonalGold() {
mockBalance(new UserFreightBalanceDTO()
.setClose(true)
.setSuperDealer(true));
PayWebH5WalletTypeResolver.WalletType walletType = resolver.resolve(USER_ID);
Assert.assertEquals("normal", walletType.getAudienceType());
Assert.assertFalse(walletType.isFreightAgent());
Assert.assertFalse(walletType.isSuperFreightAgent());
Assert.assertEquals(PayApplicationCommodityType.GOLD, walletType.getCommodityType());
}
private void mockBalance(UserFreightBalanceDTO balance) {
Mockito.when(freightGoldClient.getByUserId(USER_ID))
.thenReturn(ResultResponse.success(balance));
}
}