feat: add h5 mifapay recharge endpoints

This commit is contained in:
zhx 2026-06-23 20:50:34 +08:00
parent 73adb48c1c
commit a92739a2bc
15 changed files with 768 additions and 2 deletions

2
.gitignore vendored
View File

@ -94,3 +94,5 @@ target/
devops-monitor-id_rsa.key
/.claude/
build.md

View File

@ -5,8 +5,13 @@ import com.red.circle.common.business.dto.cmd.IdStringCmd;
import com.red.circle.order.app.dto.clientobject.pay.ApplicationCommodityCardV2CO;
import com.red.circle.order.app.dto.clientobject.pay.PayApplicationCO;
import com.red.circle.order.app.dto.clientobject.pay.PayCountryCO;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5ContextCO;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5OptionsCO;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5OrderCO;
import com.red.circle.order.app.dto.clientobject.pay.PlaceAnOrderResponseCO;
import com.red.circle.order.app.dto.clientobject.pay.UserProfileAndCountryCO;
import com.red.circle.order.app.dto.cmd.PayWebH5OptionsCmd;
import com.red.circle.order.app.dto.cmd.PayWebH5PlaceOrderCmd;
import com.red.circle.order.app.dto.cmd.PayPlaceAnOrderCmd;
import com.red.circle.order.app.dto.cmd.PayPlaceAnOrderReceiptCmd;
import com.red.circle.order.app.dto.cmd.PayWebApplicationCommodityCmd;
@ -91,6 +96,58 @@ public class WebPayRestController {
return webPayService.getChannelCommodityV2(cmd);
}
/**
* H5 充值账号上下文.
*
* @eo.name H5 充值账号上下文.
* @eo.url /h5/context
* @eo.method get
* @eo.request-type formdata
*/
@GetMapping("/h5/context")
public PayWebH5ContextCO h5Context(@Validated PayWebUserCmd cmd) {
return webPayService.getH5Context(cmd);
}
/**
* H5 充值商品列表.
*
* @eo.name H5 充值商品列表.
* @eo.url /h5/options
* @eo.method get
* @eo.request-type formdata
*/
@GetMapping("/h5/options")
public PayWebH5OptionsCO h5Options(@Validated PayWebH5OptionsCmd cmd) {
return webPayService.getH5Options(cmd);
}
/**
* H5 MiFaPay 下单.
*
* @eo.name H5 MiFaPay 下单.
* @eo.url /h5/orders
* @eo.method post
* @eo.request-type json
*/
@PostMapping("/h5/orders")
public PayWebH5OrderCO h5Order(@RequestBody @Validated PayWebH5PlaceOrderCmd cmd) {
return webPayService.createH5Order(cmd);
}
/**
* H5 订单状态.
*
* @eo.name H5 订单状态.
* @eo.url /h5/orders/{orderId}
* @eo.method get
* @eo.request-type formdata
*/
@GetMapping("/h5/orders/{orderId}")
public PayWebH5OrderCO h5OrderStatus(@PathVariable("orderId") String orderId) {
return webPayService.getH5Order(orderId);
}
/**
* 下单.
*

View File

@ -0,0 +1,158 @@
package com.red.circle.order.app.command.pay.web;
import com.red.circle.common.business.core.amount.PayAmountArithmeticUtils;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.order.app.dto.clientobject.pay.PayCountryCO;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5AccountCO;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5ContextCO;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5OptionsCO;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5ProductCO;
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.PayWebH5OptionsCmd;
import com.red.circle.order.app.dto.cmd.PayWebUserCmd;
import com.red.circle.order.infra.database.rds.entity.pay.PayApplication;
import com.red.circle.order.infra.database.rds.entity.pay.PayApplicationCommodity;
import com.red.circle.order.infra.database.rds.entity.pay.PayCountry;
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.order.inner.model.enums.PayApplicationCommodityType;
import com.red.circle.other.inner.endpoint.sys.SysCountryCodeClient;
import com.red.circle.other.inner.model.dto.sys.SysCountryCodeDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.text.StringUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/**
* Aslan H5 充值上下文和商品查询
*
* <p>商品价格必须从后台商品表实时读取H5 本地 JSON 只决定 MiFaPay 国家和方式不能参与金额计算</p>
*
* @author tf
*/
@Component
@RequiredArgsConstructor
public class PayWebH5OptionsQryExe {
private final PayCountryService payCountryService;
private final PayApplicationService payApplicationService;
private final SysCountryCodeClient sysCountryCodeClient;
private final UserProfileAndCountryQueryExe userProfileAndCountryQueryExe;
private final PayApplicationCommodityService payApplicationCommodityService;
public PayWebH5ContextCO context(PayWebUserCmd cmd) {
defaultType(cmd);
UserProfileAndCountryCO source = userProfileAndCountryQueryExe.execute(cmd);
WebSiteUseProfileCO userProfile = source.getUserProfile();
ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_RECORD_INFO, userProfile);
return new PayWebH5ContextCO()
.setAuthMode("display_user_id")
.setAccount(new PayWebH5AccountCO()
.setUserId(userProfile.getId())
.setDisplayUserId(userProfile.getId())
.setUsername(userProfile.getUserNickname())
.setAvatar(userProfile.getUserAvatar())
.setAccount(userProfile.getAccount())
.setSysOrigin(userProfile.getSysOrigin())
.setFreightAgent(userProfile.getFreightAgent())
.setSuperFreightAgent(userProfile.getSuperFreightAgent())
.setAudienceType(resolveAudienceType(userProfile)))
.setCountryList(source.getCountryList())
.setRegionId(source.getRegionId());
}
public PayWebH5OptionsCO options(PayWebH5OptionsCmd cmd) {
defaultType(cmd);
PayApplication application = payApplicationService.getById(cmd.getApplicationId());
ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_RECORD_INFO, application);
PayCountry payCountry = Objects.isNull(cmd.getPayCountryId())
? payCountryService.getShelfSortMax()
: payCountryService.getById(cmd.getPayCountryId());
ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_RECORD_INFO, payCountry);
SysCountryCodeDTO country = ResponseAssert.requiredSuccess(
sysCountryCodeClient.getById(payCountry.getCountryId())
);
ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_RECORD_INFO, country);
List<PayApplicationCommodity> commodities = payApplicationCommodityService
.listCommodityByApplicationCountry(
cmd.getApplicationId(),
payCountry.getId(),
cmd.getType(),
null
);
if (CollectionUtils.isEmpty(commodities) && StringUtils.isNotBlank(cmd.getRegionId())) {
// Aslan 后台有按区域维护商品的历史配置国家商品为空时只降级到同区域商品不放宽应用或类型条件
commodities = payApplicationCommodityService.listCommodityByApplicationCountry(
cmd.getApplicationId(),
null,
cmd.getType(),
cmd.getRegionId()
);
}
BigDecimal exchangeRate = payCountry.getUsdExchangeRate();
return new PayWebH5OptionsCO()
.setPayCountryId(Objects.toString(payCountry.getId()))
.setCountryCode(country.getAlphaTwo())
.setCountryName(country.getCountryName())
.setCurrencyCode(payCountry.getCurrency())
.setUsdToCurrencyRate(exchangeRate)
.setProducts(commodities.stream()
.map(commodity -> toProduct(commodity, payCountry))
.collect(Collectors.toList()));
}
private PayWebH5ProductCO toProduct(PayApplicationCommodity commodity, PayCountry payCountry) {
BigDecimal amount = PayAmountArithmeticUtils.multiplyUpNotPoint(
commodity.getAmountUsd(),
payCountry.getUsdExchangeRate()
);
return new PayWebH5ProductCO()
.setProductId(Objects.toString(commodity.getId()))
.setCoinAmount(commodity.getObtainGoldsQuantity().stripTrailingZeros().toPlainString())
.setAmountUsd(commodity.getAmountUsd())
.setAmountUsdtMicro(minor(commodity.getAmountUsd(), 1_000_000L))
.setAmountMinor(minor(commodity.getAmountUsd(), 100L))
.setProviderAmountMinor(minor(amount, 100L))
.setCurrencyCode(payCountry.getCurrency())
.setType(commodity.getType());
}
private Long minor(BigDecimal amount, Long scale) {
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);
}
}
private void defaultType(PayWebH5OptionsCmd cmd) {
if (Objects.isNull(cmd.getType())) {
cmd.setType(PayApplicationCommodityType.GOLD);
}
}
}

View File

@ -0,0 +1,199 @@
package com.red.circle.order.app.command.pay.web;
import com.red.circle.common.business.core.amount.PayAmountArithmeticUtils;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.framework.core.response.ResponseErrorCode;
import com.red.circle.order.app.command.pay.web.strategy.PayPlaceAnOrderDetailsV2;
import com.red.circle.order.app.command.pay.web.strategy.PayPlaceAnOrderService;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5OrderCO;
import com.red.circle.order.app.dto.clientobject.pay.PlaceAnOrderResponseCO;
import com.red.circle.order.app.dto.cmd.PayWebH5PlaceOrderCmd;
import com.red.circle.order.infra.database.mongo.order.InAppPurchaseDetailsService;
import com.red.circle.order.infra.database.mongo.order.entity.InAppPurchaseDetails;
import com.red.circle.order.infra.database.mongo.order.entity.assist.InAppPurchaseProduct;
import com.red.circle.order.infra.database.rds.entity.pay.PayApplication;
import com.red.circle.order.infra.database.rds.entity.pay.PayApplicationCommodity;
import com.red.circle.order.infra.database.rds.entity.pay.PayCountry;
import com.red.circle.order.infra.database.rds.entity.pay.PayCountryChannelDetails;
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.order.infra.util.IpUtils;
import com.red.circle.order.inner.model.enums.PayApplicationCommodityType;
import com.red.circle.order.inner.model.enums.inapp.InAppPurchaseReceiptType;
import com.red.circle.order.inner.model.enums.inapp.InAppPurchaseStatus;
import com.red.circle.other.inner.endpoint.sys.SysCountryCodeClient;
import com.red.circle.other.inner.model.dto.sys.SysCountryCodeDTO;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/**
* Aslan H5 MiFaPay 下单和查单
*
* <p>前端只提交商品 id 和支付方式标识金额金币数商品类型币种和汇率全部从后台表重新生成</p>
*
* @author tf
*/
@Component
@RequiredArgsConstructor
public class PayWebH5PlaceOrderCmdExe {
private static final String PROVIDER_MIFAPAY = "mifapay";
private static final String FACTORY_MIFAPAY = "MIFA_PAY";
private final PayCountryService payCountryService;
private final PayApplicationService payApplicationService;
private final SysCountryCodeClient sysCountryCodeClient;
private final PayPlaceAnOrderService payPlaceAnOrderService;
private final InAppPurchaseDetailsService inAppPurchaseDetailsService;
private final PayApplicationCommodityService payApplicationCommodityService;
public PayWebH5OrderCO execute(PayWebH5PlaceOrderCmd cmd) {
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
PROVIDER_MIFAPAY.equalsIgnoreCase(Objects.toString(cmd.getProviderCode(), "")));
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
Objects.nonNull(cmd.getApplicationId())
&& Objects.nonNull(cmd.getGoodsId())
&& Objects.nonNull(cmd.getPayCountryId())
&& Objects.nonNull(cmd.getUserId())
&& StringUtils.isNotBlank(cmd.getPayWay())
&& StringUtils.isNotBlank(cmd.getPayType()));
PayPlaceAnOrderDetailsV2 details = buildDetails(cmd);
PlaceAnOrderResponseCO response = payPlaceAnOrderService.doRequest(details);
return new PayWebH5OrderCO()
.setOrderId(details.getOrderId())
.setProviderCode(PROVIDER_MIFAPAY)
.setPayUrl(response.getRequestUrl())
.setStatus("redirected")
.setProviderAmountMinor(minor(details.getAmount(), 100L))
.setUsdMinorAmount(minor(details.getAmountUsd(), 100L))
.setCurrencyCode(details.getCurrency())
.setFailureReason("");
}
public PayWebH5OrderCO getOrder(String orderId) {
InAppPurchaseDetails order = inAppPurchaseDetailsService.getById(orderId);
ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_RECORD_INFO, order);
return new PayWebH5OrderCO()
.setOrderId(order.getId())
.setProviderCode(PROVIDER_MIFAPAY)
.setStatus(toH5Status(order.getStatus()))
.setProviderAmountMinor(minor(order.getAmount(), 100L))
.setUsdMinorAmount(minor(order.getAmountUsd(), 100L))
.setCurrencyCode(order.getCurrency())
.setFailureReason(order.getReason());
}
private PayPlaceAnOrderDetailsV2 buildDetails(PayWebH5PlaceOrderCmd cmd) {
PayApplication application = payApplicationService.getById(cmd.getApplicationId());
ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_RECORD_INFO, application);
PayApplicationCommodity commodity = payApplicationCommodityService
.getAppCommodity(cmd.getGoodsId(), cmd.getApplicationId());
ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_MAPPING_INFO, commodity);
ResponseAssert.isTrue(CommonErrorCode.QUANTITY_MUST_GT_ZERO,
ArithmeticUtils.gtZero(commodity.getObtainGoldsQuantity()));
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
Boolean.TRUE.equals(commodity.getShelf()));
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
Objects.isNull(cmd.getType()) || Objects.equals(cmd.getType().name(), commodity.getType()));
PayCountry payCountry = payCountryService.getById(cmd.getPayCountryId());
ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_RECORD_INFO, payCountry);
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
Boolean.TRUE.equals(payCountry.getShelf()));
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
Objects.isNull(commodity.getPayCountryId())
|| Objects.equals(commodity.getPayCountryId(), payCountry.getId()));
SysCountryCodeDTO sysCountryCode = ResponseAssert.requiredSuccess(
sysCountryCodeClient.getById(payCountry.getCountryId())
);
ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_RECORD_INFO, sysCountryCode);
BigDecimal amount = PayAmountArithmeticUtils.multiplyUpNotPoint(
commodity.getAmountUsd(),
payCountry.getUsdExchangeRate()
);
PayPlaceAnOrderDetailsV2 details = new PayPlaceAnOrderDetailsV2();
details.setOrderId(IdWorkerUtils.getIdStr());
details.setReceiptType(InAppPurchaseReceiptType.PAYMENT);
details.setApplicationId(Objects.toString(application.getId()));
details.setAppCommodityType(commodity.getType());
details.setSysOrigin(application.getAppCode());
details.setAcceptUserId(cmd.getUserId());
details.setCreateUserId(cmd.getUserId());
details.setAmount(amount);
details.setAmountUsd(commodity.getAmountUsd());
details.setProducts(List.of(new InAppPurchaseProduct()
.setId(commodity.getId())
.setName(commodity.getType())
.setCode(commodity.getType())
.setDescribe(productDescriptor(commodity))
.setContent(commodity.getObtainGoldsQuantity().stripTrailingZeros().toPlainString())
.setAmountUsd(commodity.getAmountUsd())
.setQuantity(1)));
details.setProductDescriptor(productDescriptor(commodity));
details.setChannelDetails(mifaPayChannel(cmd, payCountry));
details.setCountry(sysCountryCode);
details.setPayCountry(payCountry);
details.setRechargeUrl(StringUtils.isBlankOrElse(cmd.getReturnUrl(), ""));
details.setSuccessRedirectUrl(StringUtils.isBlankOrElse(cmd.getReturnUrl(), ""));
details.setCancelRedirectUrl(StringUtils.isBlankOrElse(cmd.getReturnUrl(), ""));
details.setNewVersion(Boolean.TRUE);
details.setRequestIp(IpUtils.getIpAddr());
return details;
}
private PayCountryChannelDetails mifaPayChannel(PayWebH5PlaceOrderCmd cmd, PayCountry payCountry) {
String channelCode = StringUtils.isNotBlank(cmd.getChannelCode())
? cmd.getChannelCode()
: cmd.getPayWay() + "_" + cmd.getPayType();
// 这里刻意不查后台渠道明细表H5 支付国家和方式来自 app JSON本对象只为复用现有 MiFaPay 策略和订单快照
return new PayCountryChannelDetails()
.setId(0L)
.setPayCountryId(payCountry.getId())
.setChannelCode(channelCode)
.setFactoryCode(FACTORY_MIFAPAY)
.setFactoryChannel(cmd.getPayType())
.setFactoryCurrencyPoint(2)
.setShelf(Boolean.TRUE);
}
private String productDescriptor(PayApplicationCommodity commodity) {
return PayApplicationCommodityType.GOLD.eq(commodity.getType())
? commodity.getType()
: commodity.getAmountUsd().stripTrailingZeros().toPlainString()
+ "USD = "
+ StringUtils.isBlankOrElse(commodity.getContent(), "0")
+ commodity.getType();
}
private String toH5Status(InAppPurchaseStatus status) {
if (Objects.equals(status, InAppPurchaseStatus.SUCCESS)) {
return "credited";
}
if (Objects.equals(status, InAppPurchaseStatus.FAIL)
|| Objects.equals(status, InAppPurchaseStatus.CANCEL)
|| Objects.equals(status, InAppPurchaseStatus.REFUND)) {
return "failed";
}
return "pending";
}
private Long minor(BigDecimal amount, Long scale) {
if (Objects.isNull(amount)) {
return 0L;
}
return amount.multiply(BigDecimal.valueOf(scale)).setScale(0, RoundingMode.DOWN).longValue();
}
}

View File

@ -31,7 +31,7 @@ public class MiFaPayService {
private final MiFaPayProperties miFaPayProperties;
private final RcHttpClient rcHttpClient = RcHttpClient.builder().build();
public MiFaPayService(@Value("${spring.profiles.active:dev}") String active,
public MiFaPayService(@Value("${spring.profiles.active:prod}") String active,
MiFaPayProperties miFaPayProperties) {
this.active = active;
this.miFaPayProperties = miFaPayProperties;

View File

@ -3,12 +3,19 @@ package com.red.circle.order.app.service;
import com.red.circle.common.business.dto.cmd.IdLongCmd;
import com.red.circle.order.app.command.pay.PayApplicationQryExe;
import com.red.circle.order.app.command.pay.web.PayWebApplicationCommodityV2QueryExe;
import com.red.circle.order.app.command.pay.web.PayWebH5OptionsQryExe;
import com.red.circle.order.app.command.pay.web.PayWebH5PlaceOrderCmdExe;
import com.red.circle.order.app.command.pay.web.PayWebOpenPayCountryQueryExe;
import com.red.circle.order.app.command.pay.web.UserProfileAndCountryQueryExe;
import com.red.circle.order.app.dto.clientobject.pay.ApplicationCommodityCardV2CO;
import com.red.circle.order.app.dto.clientobject.pay.PayApplicationCO;
import com.red.circle.order.app.dto.clientobject.pay.PayCountryCO;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5ContextCO;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5OptionsCO;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5OrderCO;
import com.red.circle.order.app.dto.clientobject.pay.UserProfileAndCountryCO;
import com.red.circle.order.app.dto.cmd.PayWebH5OptionsCmd;
import com.red.circle.order.app.dto.cmd.PayWebH5PlaceOrderCmd;
import com.red.circle.order.app.dto.cmd.PayWebApplicationCommodityCmd;
import com.red.circle.order.app.dto.cmd.PayWebUserCmd;
import java.util.List;
@ -28,6 +35,8 @@ public class WebPayServiceImpl implements WebPayService {
private final PayWebOpenPayCountryQueryExe payWebOpenPayCountryQueryExe;
private final UserProfileAndCountryQueryExe userProfileAndCountryQueryExe;
private final PayWebApplicationCommodityV2QueryExe payWebApplicationCommodityV2QueryExe;
private final PayWebH5OptionsQryExe payWebH5OptionsQryExe;
private final PayWebH5PlaceOrderCmdExe payWebH5PlaceOrderCmdExe;
@Override
public PayApplicationCO getApplication(IdLongCmd cmd) {
@ -50,4 +59,24 @@ public class WebPayServiceImpl implements WebPayService {
return userProfileAndCountryQueryExe.execute(cmd);
}
@Override
public PayWebH5ContextCO getH5Context(PayWebUserCmd cmd) {
return payWebH5OptionsQryExe.context(cmd);
}
@Override
public PayWebH5OptionsCO getH5Options(PayWebH5OptionsCmd cmd) {
return payWebH5OptionsQryExe.options(cmd);
}
@Override
public PayWebH5OrderCO createH5Order(PayWebH5PlaceOrderCmd cmd) {
return payWebH5PlaceOrderCmdExe.execute(cmd);
}
@Override
public PayWebH5OrderCO getH5Order(String orderId) {
return payWebH5PlaceOrderCmdExe.getOrder(orderId);
}
}

View File

@ -0,0 +1,48 @@
package com.red.circle.order.app.dto.clientobject.pay;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.red.circle.framework.dto.ClientObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* H5 充值页账号快照只暴露确认充值对象需要的公开字段
*
* @author tf
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class PayWebH5AccountCO extends ClientObject {
private static final long serialVersionUID = 1L;
@JsonSerialize(using = ToStringSerializer.class)
@JsonProperty("user_id")
private Long userId;
@JsonSerialize(using = ToStringSerializer.class)
@JsonProperty("display_user_id")
private Long displayUserId;
private String username;
private String avatar;
private String account;
@JsonProperty("sys_origin")
private String sysOrigin;
@JsonProperty("audience_type")
private String audienceType;
@JsonProperty("freight_agent")
private Boolean freightAgent;
@JsonProperty("super_freight_agent")
private Boolean superFreightAgent;
}

View File

@ -0,0 +1,32 @@
package com.red.circle.order.app.dto.clientobject.pay;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.red.circle.framework.dto.ClientObject;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* H5 充值页上下文账号和国家都来自 Aslan 服务端前端只拿它做展示和后续商品查询条件
*
* @author tf
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class PayWebH5ContextCO extends ClientObject {
private static final long serialVersionUID = 1L;
@JsonProperty("auth_mode")
private String authMode;
private PayWebH5AccountCO account;
@JsonProperty("country_list")
private List<PayCountryCO> countryList;
@JsonProperty("region_id")
private String regionId;
}

View File

@ -0,0 +1,39 @@
package com.red.circle.order.app.dto.clientobject.pay;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.red.circle.framework.dto.ClientObject;
import java.math.BigDecimal;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* H5 商品列表和当前支付国家汇率快照支付方式由本地 JSON 合并
*
* @author tf
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class PayWebH5OptionsCO extends ClientObject {
private static final long serialVersionUID = 1L;
private List<PayWebH5ProductCO> products;
@JsonProperty("pay_country_id")
private String payCountryId;
@JsonProperty("country_code")
private String countryCode;
@JsonProperty("country_name")
private String countryName;
@JsonProperty("currency_code")
private String currencyCode;
@JsonProperty("usd_to_currency_rate")
private BigDecimal usdToCurrencyRate;
}

View File

@ -0,0 +1,43 @@
package com.red.circle.order.app.dto.clientobject.pay;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.red.circle.framework.dto.ClientObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* H5 订单状态快照终态由服务端订单状态决定前端只负责跳转和轮询展示
*
* @author tf
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class PayWebH5OrderCO extends ClientObject {
private static final long serialVersionUID = 1L;
@JsonProperty("order_id")
private String orderId;
@JsonProperty("provider_code")
private String providerCode;
@JsonProperty("pay_url")
private String payUrl;
private String status;
@JsonProperty("provider_amount_minor")
private Long providerAmountMinor;
@JsonProperty("usd_minor_amount")
private Long usdMinorAmount;
@JsonProperty("currency_code")
private String currencyCode;
@JsonProperty("failure_reason")
private String failureReason;
}

View File

@ -0,0 +1,44 @@
package com.red.circle.order.app.dto.clientobject.pay;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.red.circle.framework.dto.ClientObject;
import java.math.BigDecimal;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* H5 商品卡片快照金额由后端按后台商品表和支付国家汇率计算前端不参与定价
*
* @author tf
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class PayWebH5ProductCO extends ClientObject {
private static final long serialVersionUID = 1L;
@JsonProperty("product_id")
private String productId;
@JsonProperty("coin_amount")
private String coinAmount;
@JsonProperty("amount_usdt_micro")
private Long amountUsdtMicro;
@JsonProperty("amount_minor")
private Long amountMinor;
@JsonProperty("provider_amount_minor")
private Long providerAmountMinor;
@JsonProperty("currency_code")
private String currencyCode;
@JsonProperty("amount_usd")
private BigDecimal amountUsd;
private String type;
}

View File

@ -49,6 +49,16 @@ public class WebSiteUseProfileCO extends ClientObject {
*/
private String sysOrigin;
/**
* 是否金币代理.
*/
private Boolean freightAgent;
/**
* 是否超级金币代理.
*/
private Boolean superFreightAgent;
public static WebSiteUseProfileCO toWebSiteUseProfileCO(UserProfileDTO userProfile) {
if (userProfile == null) {
return null;
@ -58,6 +68,8 @@ public class WebSiteUseProfileCO extends ClientObject {
.setUserAvatar(userProfile.getUserAvatar())
.setUserNickname(userProfile.getUserNickname())
.setAccount(userProfile.getAccount())
.setSysOrigin(userProfile.getOriginSys());
.setSysOrigin(userProfile.getOriginSys())
.setFreightAgent(userProfile.getIsFreightAgent())
.setSuperFreightAgent(userProfile.getIsSuperFreightAgent());
}
}

View File

@ -0,0 +1,32 @@
package com.red.circle.order.app.dto.cmd;
import com.red.circle.framework.dto.Command;
import com.red.circle.order.inner.model.enums.PayApplicationCommodityType;
import java.io.Serial;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* H5 商品查询命令商品类型和国家来自已确认账号上下文
*
* @author tf
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class PayWebH5OptionsCmd extends Command {
@Serial
private static final long serialVersionUID = 1L;
private Long applicationId;
private Long payCountryId;
private Long userId;
private String regionId;
private PayApplicationCommodityType type;
}

View File

@ -0,0 +1,46 @@
package com.red.circle.order.app.dto.cmd;
import com.red.circle.framework.dto.Command;
import com.red.circle.order.inner.model.enums.PayApplicationCommodityType;
import java.io.Serial;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* H5 下单命令只接收商品和支付方式标识金额金币数币种由服务端重新查表计算
*
* @author tf
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class PayWebH5PlaceOrderCmd extends Command {
@Serial
private static final long serialVersionUID = 1L;
private Long applicationId;
private Long goodsId;
private Long payCountryId;
private Long userId;
private String providerCode;
private String payWay;
private String payType;
private String channelCode;
private String returnUrl;
private String commandId;
private String language;
private PayApplicationCommodityType type;
}

View File

@ -4,7 +4,12 @@ import com.red.circle.common.business.dto.cmd.IdLongCmd;
import com.red.circle.order.app.dto.clientobject.pay.ApplicationCommodityCardV2CO;
import com.red.circle.order.app.dto.clientobject.pay.PayApplicationCO;
import com.red.circle.order.app.dto.clientobject.pay.PayCountryCO;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5ContextCO;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5OptionsCO;
import com.red.circle.order.app.dto.clientobject.pay.PayWebH5OrderCO;
import com.red.circle.order.app.dto.clientobject.pay.UserProfileAndCountryCO;
import com.red.circle.order.app.dto.cmd.PayWebH5OptionsCmd;
import com.red.circle.order.app.dto.cmd.PayWebH5PlaceOrderCmd;
import com.red.circle.order.app.dto.cmd.PayWebApplicationCommodityCmd;
import com.red.circle.order.app.dto.cmd.PayWebUserCmd;
import java.util.List;
@ -39,4 +44,24 @@ public interface WebPayService {
*/
UserProfileAndCountryCO getUserProfileAndCountry(PayWebUserCmd cmd);
/**
* H5 充值账号上下文.
*/
PayWebH5ContextCO getH5Context(PayWebUserCmd cmd);
/**
* H5 商品列表.
*/
PayWebH5OptionsCO getH5Options(PayWebH5OptionsCmd cmd);
/**
* H5 下单.
*/
PayWebH5OrderCO createH5Order(PayWebH5PlaceOrderCmd cmd);
/**
* H5 订单状态.
*/
PayWebH5OrderCO getH5Order(String orderId);
}