recharge接入实时汇率
This commit is contained in:
parent
860ebd5a21
commit
6b78840f5e
@ -26,9 +26,11 @@ 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 com.red.circle.other.inner.endpoint.user.region.UserRegionClient;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
@ -36,6 +38,7 @@ import org.springframework.stereotype.Component;
|
||||
*
|
||||
* @author pengliang on 2021/7/9
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class PayWebPlaceAnOrderCmdExe {
|
||||
@ -82,9 +85,11 @@ public class PayWebPlaceAnOrderCmdExe {
|
||||
details.setSysOrigin(application.getAppCode());
|
||||
details.setAcceptUserId(cmd.getUserId());
|
||||
details.setCreateUserId(cmd.getUserId());
|
||||
// 获取实时汇率,失败时使用配置的汇率作为备选
|
||||
BigDecimal exchangeRate = getRealTimeExchangeRate(payCountry);
|
||||
details.setAmount(PayAmountArithmeticUtils.multiplyUpNotPoint(
|
||||
commodity.getAmountUsd(),
|
||||
payCountry.getUsdExchangeRate()
|
||||
exchangeRate
|
||||
)
|
||||
);
|
||||
details.setAmountUsd(commodity.getAmountUsd());
|
||||
@ -120,6 +125,33 @@ public class PayWebPlaceAnOrderCmdExe {
|
||||
return details;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实时汇率,失败时返回配置的汇率
|
||||
* @param payCountry 支付国家配置
|
||||
* @return 汇率
|
||||
*/
|
||||
private BigDecimal getRealTimeExchangeRate(PayCountry payCountry) {
|
||||
BigDecimal realTimeRate = null;
|
||||
try {
|
||||
// 调用实时汇率查询 - 这里假设目标币种是payCountry对应的币种
|
||||
// 您可以根据实际需要调整币种参数
|
||||
realTimeRate = payPlaceAnOrderService.getRealTimeExchangeRate("USD", payCountry.getCurrency());
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to get real-time exchange rate for country {}, will use configured rate as fallback",
|
||||
payCountry.getId(), e);
|
||||
}
|
||||
|
||||
// 如果实时汇率获取失败,使用配置的汇率作为备选
|
||||
BigDecimal exchangeRate = realTimeRate != null ? realTimeRate : payCountry.getUsdExchangeRate();
|
||||
|
||||
// 记录使用的汇率来源
|
||||
log.info("Using {} exchange rate: {} for country {} amount calculation",
|
||||
realTimeRate != null ? "real-time" : "configured",
|
||||
exchangeRate, payCountry.getId());
|
||||
|
||||
return exchangeRate;
|
||||
}
|
||||
|
||||
private void rechargeRecord(PayPlaceAnOrderDetailsV2 details) {
|
||||
if (Objects.equals(details.getNewVersion(), Boolean.TRUE)) {
|
||||
userFreightRechargeRecordGateway.inrNowMonthAmount(
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
package com.red.circle.order.app.command.pay.web.strategy;
|
||||
|
||||
import com.google.api.client.util.Maps;
|
||||
import com.red.circle.component.pay.paymax.PayMaxUtils;
|
||||
import com.red.circle.framework.core.request.RequestClientEnum;
|
||||
import com.red.circle.framework.web.props.EnvProperties;
|
||||
import com.red.circle.order.app.common.PayerMaxProperties;
|
||||
import com.red.circle.order.app.common.QuoteQueryParam;
|
||||
import com.red.circle.order.app.common.QuoteQueryResponse;
|
||||
import com.red.circle.order.app.dto.clientobject.pay.PlaceAnOrderResponseCO;
|
||||
import com.red.circle.order.infra.database.mongo.order.InAppPurchaseDetailsService;
|
||||
import com.red.circle.order.infra.database.mongo.order.entity.InAppPurchaseDetails;
|
||||
@ -11,13 +15,19 @@ import com.red.circle.order.infra.database.mongo.order.entity.assist.InAppPurcha
|
||||
import com.red.circle.order.infra.database.mongo.order.entity.assist.InAppPurchaseStatusStep;
|
||||
import com.red.circle.order.inner.model.enums.inapp.InAppPurchaseStatus;
|
||||
import com.red.circle.tool.core.date.TimestampUtils;
|
||||
import com.red.circle.tool.core.http.RcHttpClient;
|
||||
import com.red.circle.tool.core.json.JacksonUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author pengliang on 2022/11/2
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PayPlaceAnOrderService {
|
||||
@ -25,6 +35,8 @@ public class PayPlaceAnOrderService {
|
||||
private final EnvProperties envProperties;
|
||||
private final InAppPurchaseDetailsService inAppPurchaseDetailsService;
|
||||
private final FactoryPlaceAnOrderStrategyV2 factoryPlaceAnOrderStrategyV2;
|
||||
private final PayerMaxProperties payerMaxProperties;
|
||||
private final RcHttpClient rcHttpClient = RcHttpClient.builder().build();
|
||||
|
||||
public PlaceAnOrderResponseCO doRequest(PayPlaceAnOrderDetailsV2 param) {
|
||||
// 第三方:请求下单
|
||||
@ -85,4 +97,59 @@ public class PayPlaceAnOrderService {
|
||||
return envProperties.isProd() ? PayEnv.PROD.name() : PayEnv.TEST.name();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询实时汇率
|
||||
* @param baseCurrency 基础币种(如USD)
|
||||
* @param targetCurrency 目标币种(如CNY)
|
||||
* @return 汇率值
|
||||
*/
|
||||
public BigDecimal getRealTimeExchangeRate(String baseCurrency, String targetCurrency) {
|
||||
try {
|
||||
QuoteQueryParam param = new QuoteQueryParam();
|
||||
QuoteQueryParam.QuoteQueryData data = new QuoteQueryParam.QuoteQueryData();
|
||||
|
||||
// 币种对格式:基础币种+目标币种,如USDCNY
|
||||
String ccyPair = baseCurrency + targetCurrency;
|
||||
data.setCcyPairList(Arrays.asList(ccyPair));
|
||||
data.setPriceTypeList(Arrays.asList("OFFER")); // 使用卖出价
|
||||
|
||||
param.setAppId(payerMaxProperties.getAppId());
|
||||
param.setMerchantNo(payerMaxProperties.getMerchantId());
|
||||
param.setData(data);
|
||||
|
||||
String url = payerMaxProperties.getGatewayBaseUrl() + "/aggregate-pay/api/gateway/quoteQuery";
|
||||
String jsonParam = JacksonUtils.toJson(param);
|
||||
String signature = PayMaxUtils.signRsa(jsonParam, payerMaxProperties.getRsaPrivateKey());
|
||||
|
||||
if (!envProperties.isProd()) {
|
||||
log.info("Exchange Rate Query Request: {}\nParam:{}\nSign:{}", url, jsonParam, signature);
|
||||
}
|
||||
|
||||
QuoteQueryResponse response = rcHttpClient.post()
|
||||
.uri(url)
|
||||
.header("sign", signature)
|
||||
.contentType("application/json")
|
||||
.bodyValue(jsonParam)
|
||||
.retrieve()
|
||||
.bodyToEntity(QuoteQueryResponse.class)
|
||||
.block();
|
||||
|
||||
if (response != null && response.isSuccess() && response.getData() != null
|
||||
&& response.getData().getRateList() != null && !response.getData().getRateList().isEmpty()) {
|
||||
// 使用offerRate(卖出价)作为汇率
|
||||
BigDecimal rate = response.getData().getRateList().get(0).getOfferRate();
|
||||
log.info("Successfully got real-time exchange rate from {} to {}: {}", baseCurrency, targetCurrency, rate);
|
||||
return rate;
|
||||
} else {
|
||||
log.error("Failed to get real-time exchange rate, response: {}",
|
||||
response != null ? JacksonUtils.toJson(response) : "null");
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error occurred while querying real-time exchange rate from {} to {}",
|
||||
baseCurrency, targetCurrency, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,71 @@
|
||||
package com.red.circle.order.app.common;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* PayerMax配置属性
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "red-circle.pay.payer-max")
|
||||
public class PayerMaxProperties {
|
||||
|
||||
private String secretKey;
|
||||
private String merchantId;
|
||||
private String gatewayBaseUrl;
|
||||
private String rsaPrivateKey;
|
||||
private String platformRsaPublicKey;
|
||||
private String appId;
|
||||
|
||||
public String getSecretKey() {
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
public void setSecretKey(String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
public String getMerchantId() {
|
||||
return merchantId;
|
||||
}
|
||||
|
||||
public void setMerchantId(String merchantId) {
|
||||
this.merchantId = merchantId;
|
||||
}
|
||||
|
||||
public String getGatewayBaseUrl() {
|
||||
return gatewayBaseUrl;
|
||||
}
|
||||
|
||||
public void setGatewayBaseUrl(String gatewayBaseUrl) {
|
||||
this.gatewayBaseUrl = gatewayBaseUrl;
|
||||
}
|
||||
|
||||
public String getRsaPrivateKey() {
|
||||
return rsaPrivateKey;
|
||||
}
|
||||
|
||||
public void setRsaPrivateKey(String rsaPrivateKey) {
|
||||
this.rsaPrivateKey = rsaPrivateKey;
|
||||
}
|
||||
|
||||
public String getPlatformRsaPublicKey() {
|
||||
return platformRsaPublicKey;
|
||||
}
|
||||
|
||||
public void setPlatformRsaPublicKey(String platformRsaPublicKey) {
|
||||
this.platformRsaPublicKey = platformRsaPublicKey;
|
||||
}
|
||||
|
||||
public String getAppId() {
|
||||
return appId;
|
||||
}
|
||||
|
||||
public void setAppId(String appId) {
|
||||
this.appId = appId;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package com.red.circle.order.app.common;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* PayerMax汇率查询请求参数
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
@Data
|
||||
public class QuoteQueryParam {
|
||||
|
||||
/**
|
||||
* 接口版本 当前值为:1.2
|
||||
*/
|
||||
@JsonProperty("version")
|
||||
private String version = "1.2";
|
||||
|
||||
/**
|
||||
* 密钥版本 当前值为:1
|
||||
*/
|
||||
@JsonProperty("keyVersion")
|
||||
private String keyVersion = "1";
|
||||
|
||||
/**
|
||||
* 请求时间,符合rfc3339规范,格式:yyyy-MM-dd'T'HH:mm:ss.SSSXXX
|
||||
*/
|
||||
@JsonProperty("requestTime")
|
||||
private String requestTime;
|
||||
|
||||
/**
|
||||
* 商户应用Id
|
||||
*/
|
||||
@JsonProperty("appId")
|
||||
private String appId;
|
||||
|
||||
/**
|
||||
* 商户号
|
||||
*/
|
||||
@JsonProperty("merchantNo")
|
||||
private String merchantNo;
|
||||
|
||||
/**
|
||||
* 数据部分
|
||||
*/
|
||||
@JsonProperty("data")
|
||||
private QuoteQueryData data;
|
||||
|
||||
@Data
|
||||
public static class QuoteQueryData {
|
||||
/**
|
||||
* 币种对列表,基于美金标价法拼接,USD在前,例如:USDIDR
|
||||
*/
|
||||
@JsonProperty("ccyPairList")
|
||||
private List<String> ccyPairList;
|
||||
|
||||
/**
|
||||
* 价格类型列表,买入价BID,卖出价OFFER,默认查询全部
|
||||
*/
|
||||
@JsonProperty("priceTypeList")
|
||||
private List<String> priceTypeList;
|
||||
}
|
||||
|
||||
public QuoteQueryParam() {
|
||||
// 设置当前时间,格式:yyyy-MM-dd'T'HH:mm:ss.SSSXXX(示例:2022-06-10T20:30:53.732+08:00)
|
||||
this.requestTime = OffsetDateTime.now(ZoneId.of("+08:00"))
|
||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
package com.red.circle.order.app.common;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* PayerMax汇率查询响应
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
@Data
|
||||
public class QuoteQueryResponse {
|
||||
|
||||
/**
|
||||
* 返回码,'APPLY_SUCCESS'代表成功
|
||||
*/
|
||||
@JsonProperty("code")
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 返回描述
|
||||
*/
|
||||
@JsonProperty("msg")
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 响应数据
|
||||
*/
|
||||
@JsonProperty("data")
|
||||
private ResponseData data;
|
||||
|
||||
/**
|
||||
* 判断是否成功
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
return "APPLY_SUCCESS".equals(code);
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ResponseData {
|
||||
/**
|
||||
* 汇率列表
|
||||
*/
|
||||
@JsonProperty("rateList")
|
||||
private List<RateInfo> rateList;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class RateInfo {
|
||||
/**
|
||||
* 币种对
|
||||
*/
|
||||
@JsonProperty("ccyPair")
|
||||
private String ccyPair;
|
||||
|
||||
/**
|
||||
* 买入价,默认8位小数
|
||||
*/
|
||||
@JsonProperty("bidRate")
|
||||
private BigDecimal bidRate;
|
||||
|
||||
/**
|
||||
* 卖出价,默认8位小数
|
||||
*/
|
||||
@JsonProperty("offerRate")
|
||||
private BigDecimal offerRate;
|
||||
|
||||
/**
|
||||
* 汇率时间,格式yyyy-MM-dd'T'HH:mm:ss.SSSXXX
|
||||
*/
|
||||
@JsonProperty("rateTime")
|
||||
private String rateTime;
|
||||
|
||||
/**
|
||||
* 版本
|
||||
*/
|
||||
@JsonProperty("version")
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 过期时间,格式yyyy-MM-dd'T'HH:mm:ss.SSSXXX
|
||||
*/
|
||||
@JsonProperty("expiryTime")
|
||||
private String expiryTime;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user