同步平台业务模块能力

This commit is contained in:
baiyanyun
2026-07-13 09:13:30 +08:00
parent e612f5e434
commit a1a38f62d8
224 changed files with 22331 additions and 1224 deletions

View File

@@ -0,0 +1,93 @@
package com.xspaceagi.pay.infra.gateway.client;
import com.xspaceagi.pay.domain.gateway.FileEntryGateway;
import com.xspaceagi.pay.infra.gateway.utils.PayGatewayClientUtils;
import com.xspaceagi.pay.sdk.dto.ApiResponse;
import com.xspaceagi.pay.sdk.dto.FileEntryOpenApiUploadRequest;
import com.xspaceagi.pay.sdk.dto.FileEntryUploadResponse;
import com.xspaceagi.pay.sdk.sign.OpenApiFileEntryMultipartSupport;
import com.xspaceagi.pay.sdk.sign.OpenApiFileEntryMultipartSupport.SignedUploadFields;
import com.xspaceagi.pay.sdk.sign.OpenApiFileEntrySign;
import com.xspaceagi.pay.spec.gateway.PayGatewayOutbound;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
@Slf4j
@Component
public class FileEntryGatewayClient implements FileEntryGateway {
private static final String BIZ_MERCHANT_ONBOARDING_IMAGE = "merchant_onboarding_image";
private static final ParameterizedTypeReference<ApiResponse<FileEntryUploadResponse>> UPLOAD_RESPONSE_TYPE =
new ParameterizedTypeReference<ApiResponse<FileEntryUploadResponse>>() {};
private final RestTemplate payGatewayRestTemplate;
public FileEntryGatewayClient(@Qualifier("payGatewayRestTemplate") RestTemplate payGatewayRestTemplate) {
this.payGatewayRestTemplate = payGatewayRestTemplate;
}
@Override
public FileEntryUploadResponse uploadMerchantOnboardingImage(
PayGatewayOutbound outbound,
byte[] fileBytes,
String filename,
String contentType,
String replaceFileKey) {
if (fileBytes == null || fileBytes.length == 0) {
throw new IllegalArgumentException("请选择文件");
}
FileEntryOpenApiUploadRequest meta = new FileEntryOpenApiUploadRequest();
meta.setClientId(outbound.clientId());
meta.setBizType(BIZ_MERCHANT_ONBOARDING_IMAGE);
if (StringUtils.hasText(replaceFileKey)) {
meta.setReplaceFileKey(replaceFileKey.trim());
}
byte[] signedJson = OpenApiFileEntrySign.signUpload(meta, outbound.clientSecret());
SignedUploadFields fields = OpenApiFileEntryMultipartSupport.parseSignedMetadata(signedJson);
String url = outbound.baseUrl() + OpenApiFileEntrySign.PATH_UPLOAD;
log.info("[pay-gateway] POST {} clientId={} bizType={}", url, fields.getClientId(), fields.getBizType());
return PayGatewayClientUtils.postMultipartSigned(
payGatewayRestTemplate,
url,
toMultipartBody(fields, fileBytes, filename, contentType),
UPLOAD_RESPONSE_TYPE,
true);
}
private static MultiValueMap<String, Object> toMultipartBody(
SignedUploadFields fields, byte[] fileBytes, String filename, String contentType) {
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
parts.add("clientId", fields.getClientId());
parts.add("bizType", fields.getBizType());
if (StringUtils.hasText(fields.getReplaceFileKey())) {
parts.add("replaceFileKey", fields.getReplaceFileKey());
}
parts.add("timestamp", String.valueOf(fields.getTimestamp()));
parts.add("nonce", fields.getNonce());
parts.add("signature", fields.getSignature());
String safeName = StringUtils.hasText(filename) ? filename : "upload";
ByteArrayResource resource = new ByteArrayResource(fileBytes) {
@Override
public String getFilename() {
return safeName;
}
};
HttpHeaders fileHeaders = new HttpHeaders();
if (StringUtils.hasText(contentType)) {
fileHeaders.setContentType(MediaType.parseMediaType(contentType));
}
parts.add("file", new HttpEntity<>(resource, fileHeaders));
return parts;
}
}

View File

@@ -0,0 +1,87 @@
package com.xspaceagi.pay.infra.gateway.client;
import com.xspaceagi.pay.domain.gateway.PaymentAppGateway;
import com.xspaceagi.pay.infra.gateway.utils.PayGatewayClientUtils;
import com.xspaceagi.pay.sdk.dto.ApiResponse;
import com.xspaceagi.pay.sdk.dto.AppOrderCreateRequest;
import com.xspaceagi.pay.sdk.dto.AppTransactionCreateRequest;
import com.xspaceagi.pay.sdk.dto.OrderAndTransactionCreateResponse;
import com.xspaceagi.pay.sdk.dto.OrderCreateResponse;
import com.xspaceagi.pay.sdk.dto.PaymentStatusQueryRequest;
import com.xspaceagi.pay.sdk.dto.PaymentStatusQueryResponse;
import com.xspaceagi.pay.sdk.enums.PayChannel;
import com.xspaceagi.pay.sdk.enums.PayClientScene;
import com.xspaceagi.pay.sdk.sign.OpenApiPaymentAppSign;
import com.xspaceagi.pay.spec.gateway.PayGatewayOutbound;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@Slf4j
@Component
public class PaymentAppGatewayClient implements PaymentAppGateway {
private static final ParameterizedTypeReference<ApiResponse<OrderCreateResponse>> CREATE_TYPE =
new ParameterizedTypeReference<ApiResponse<OrderCreateResponse>>() {};
private static final ParameterizedTypeReference<ApiResponse<OrderAndTransactionCreateResponse>> TX_TYPE =
new ParameterizedTypeReference<ApiResponse<OrderAndTransactionCreateResponse>>() {};
private static final ParameterizedTypeReference<ApiResponse<PaymentStatusQueryResponse>> STATUS_TYPE =
new ParameterizedTypeReference<ApiResponse<PaymentStatusQueryResponse>>() {};
private final RestTemplate payGatewayRestTemplate;
public PaymentAppGatewayClient(@Qualifier("payGatewayRestTemplate") RestTemplate payGatewayRestTemplate) {
this.payGatewayRestTemplate = payGatewayRestTemplate;
}
@Override
public OrderCreateResponse createOrder(
PayGatewayOutbound outbound, String bizOrderNo, long orderAmount, String subject, String ext) {
AppOrderCreateRequest request = new AppOrderCreateRequest();
request.setClientId(outbound.clientId());
request.setBizOrderNo(bizOrderNo);
request.setOrderAmount(orderAmount);
request.setSubject(subject);
request.setExt(ext);
byte[] body = OpenApiPaymentAppSign.signCreateOrder(request, outbound.clientSecret());
String url = outbound.baseUrl() + OpenApiPaymentAppSign.PATH_CREATE_ORDER;
log.info("[pay-gateway] POST {}", url);
return PayGatewayClientUtils.postSigned(payGatewayRestTemplate, url, body, CREATE_TYPE, true);
}
@Override
public OrderAndTransactionCreateResponse createTransaction(
PayGatewayOutbound outbound,
String gatewayPaymentOrderNo,
PayChannel payChannel,
String clientIp,
PayClientScene payClientScene) {
AppTransactionCreateRequest request = new AppTransactionCreateRequest();
request.setClientId(outbound.clientId());
request.setGatewayPaymentOrderNo(gatewayPaymentOrderNo);
request.setPayChannel(payChannel);
request.setClientIp(clientIp);
request.setPayClientScene(payClientScene);
byte[] body = OpenApiPaymentAppSign.signCreateTransaction(request, outbound.clientSecret());
String url = outbound.baseUrl() + OpenApiPaymentAppSign.PATH_CREATE_TRANSACTION;
log.info("[pay-gateway] POST {} payChannel={}", url, payChannel);
return PayGatewayClientUtils.postSigned(payGatewayRestTemplate, url, body, TX_TYPE, true);
}
@Override
public PaymentStatusQueryResponse queryOrderStatus(
PayGatewayOutbound outbound, String gatewayPaymentOrderNo, boolean syncFromChannel) {
PaymentStatusQueryRequest request = new PaymentStatusQueryRequest();
request.setClientId(outbound.clientId());
request.setGatewayPaymentOrderNo(gatewayPaymentOrderNo);
request.setSyncFromChannel(syncFromChannel);
byte[] body = OpenApiPaymentAppSign.signQueryStatus(request, outbound.clientSecret());
String url = outbound.baseUrl() + OpenApiPaymentAppSign.PATH_STATUS;
log.info("[pay-gateway] POST {} syncFromChannel={}", url, syncFromChannel);
return PayGatewayClientUtils.postSigned(payGatewayRestTemplate, url, body, STATUS_TYPE, true);
}
}

View File

@@ -0,0 +1,86 @@
package com.xspaceagi.pay.infra.gateway.client;
import com.xspaceagi.pay.domain.gateway.PaymentH5Gateway;
import com.xspaceagi.pay.infra.gateway.utils.PayGatewayClientUtils;
import com.xspaceagi.pay.sdk.dto.ApiResponse;
import com.xspaceagi.pay.sdk.dto.H5OrderCreateRequest;
import com.xspaceagi.pay.sdk.dto.H5TransactionCreateRequest;
import com.xspaceagi.pay.sdk.dto.OrderAndTransactionCreateResponse;
import com.xspaceagi.pay.sdk.dto.OrderCreateResponse;
import com.xspaceagi.pay.sdk.dto.PaymentStatusQueryRequest;
import com.xspaceagi.pay.sdk.dto.PaymentStatusQueryResponse;
import com.xspaceagi.pay.sdk.enums.PayChannel;
import com.xspaceagi.pay.sdk.sign.OpenApiPaymentH5Sign;
import com.xspaceagi.pay.spec.gateway.PayGatewayOutbound;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@Slf4j
@Component
public class PaymentH5GatewayClient implements PaymentH5Gateway {
private static final ParameterizedTypeReference<ApiResponse<OrderCreateResponse>> CREATE_TYPE =
new ParameterizedTypeReference<ApiResponse<OrderCreateResponse>>() {};
private static final ParameterizedTypeReference<ApiResponse<OrderAndTransactionCreateResponse>> TX_TYPE =
new ParameterizedTypeReference<ApiResponse<OrderAndTransactionCreateResponse>>() {};
private static final ParameterizedTypeReference<ApiResponse<PaymentStatusQueryResponse>> STATUS_TYPE =
new ParameterizedTypeReference<ApiResponse<PaymentStatusQueryResponse>>() {};
private final RestTemplate payGatewayRestTemplate;
public PaymentH5GatewayClient(@Qualifier("payGatewayRestTemplate") RestTemplate payGatewayRestTemplate) {
this.payGatewayRestTemplate = payGatewayRestTemplate;
}
@Override
public OrderCreateResponse createOrder(
PayGatewayOutbound outbound, String bizOrderNo, long orderAmount, String subject, String ext) {
H5OrderCreateRequest request = new H5OrderCreateRequest();
request.setClientId(outbound.clientId());
request.setBizOrderNo(bizOrderNo);
request.setOrderAmount(orderAmount);
request.setSubject(subject);
request.setExt(ext);
byte[] body = OpenApiPaymentH5Sign.signCreateOrder(request, outbound.clientSecret());
String url = outbound.baseUrl() + OpenApiPaymentH5Sign.PATH_CREATE_ORDER;
log.info("[pay-gateway] POST {}", url);
return PayGatewayClientUtils.postSigned(payGatewayRestTemplate, url, body, CREATE_TYPE, true);
}
@Override
public OrderAndTransactionCreateResponse createTransaction(
PayGatewayOutbound outbound,
String gatewayPaymentOrderNo,
PayChannel payChannel,
String clientIp,
String frontNotifyUrl) {
H5TransactionCreateRequest request = new H5TransactionCreateRequest();
request.setClientId(outbound.clientId());
request.setGatewayPaymentOrderNo(gatewayPaymentOrderNo);
request.setPayChannel(payChannel);
request.setClientIp(clientIp);
request.setFrontNotifyUrl(frontNotifyUrl);
byte[] body = OpenApiPaymentH5Sign.signCreateTransaction(request, outbound.clientSecret());
String url = outbound.baseUrl() + OpenApiPaymentH5Sign.PATH_CREATE_TRANSACTION;
log.info("[pay-gateway] POST {} payChannel={}", url, payChannel);
return PayGatewayClientUtils.postSigned(payGatewayRestTemplate, url, body, TX_TYPE, true);
}
@Override
public PaymentStatusQueryResponse queryOrderStatus(
PayGatewayOutbound outbound, String gatewayPaymentOrderNo, boolean syncFromChannel) {
PaymentStatusQueryRequest request = new PaymentStatusQueryRequest();
request.setClientId(outbound.clientId());
request.setGatewayPaymentOrderNo(gatewayPaymentOrderNo);
request.setSyncFromChannel(syncFromChannel);
byte[] body = OpenApiPaymentH5Sign.signQueryStatus(request, outbound.clientSecret());
String url = outbound.baseUrl() + OpenApiPaymentH5Sign.PATH_STATUS;
log.info("[pay-gateway] POST {} syncFromChannel={}", url, syncFromChannel);
return PayGatewayClientUtils.postSigned(payGatewayRestTemplate, url, body, STATUS_TYPE, true);
}
}

View File

@@ -0,0 +1,92 @@
package com.xspaceagi.pay.infra.gateway.client;
import com.xspaceagi.pay.domain.gateway.PaymentMiniPayGateway;
import com.xspaceagi.pay.infra.gateway.utils.PayGatewayClientUtils;
import com.xspaceagi.pay.sdk.dto.ApiResponse;
import com.xspaceagi.pay.sdk.dto.MiniPayOrderCreateRequest;
import com.xspaceagi.pay.sdk.dto.MiniPayTransactionCreateRequest;
import com.xspaceagi.pay.sdk.dto.OrderAndTransactionCreateResponse;
import com.xspaceagi.pay.sdk.dto.OrderCreateResponse;
import com.xspaceagi.pay.sdk.dto.PaymentStatusQueryRequest;
import com.xspaceagi.pay.sdk.dto.PaymentStatusQueryResponse;
import com.xspaceagi.pay.sdk.enums.PayChannel;
import com.xspaceagi.pay.sdk.enums.PayClientScene;
import com.xspaceagi.pay.sdk.sign.OpenApiPaymentMiniPaySign;
import com.xspaceagi.pay.spec.gateway.PayGatewayOutbound;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@Slf4j
@Component
public class PaymentMiniPayGatewayClient implements PaymentMiniPayGateway {
private static final ParameterizedTypeReference<ApiResponse<OrderCreateResponse>> CREATE_TYPE =
new ParameterizedTypeReference<ApiResponse<OrderCreateResponse>>() {};
private static final ParameterizedTypeReference<ApiResponse<OrderAndTransactionCreateResponse>> TX_TYPE =
new ParameterizedTypeReference<ApiResponse<OrderAndTransactionCreateResponse>>() {};
private static final ParameterizedTypeReference<ApiResponse<PaymentStatusQueryResponse>> STATUS_TYPE =
new ParameterizedTypeReference<ApiResponse<PaymentStatusQueryResponse>>() {};
private final RestTemplate payGatewayRestTemplate;
public PaymentMiniPayGatewayClient(@Qualifier("payGatewayRestTemplate") RestTemplate payGatewayRestTemplate) {
this.payGatewayRestTemplate = payGatewayRestTemplate;
}
@Override
public OrderCreateResponse createOrder(
PayGatewayOutbound outbound, String bizOrderNo, long orderAmount, String subject, String ext) {
MiniPayOrderCreateRequest request = new MiniPayOrderCreateRequest();
request.setClientId(outbound.clientId());
request.setBizOrderNo(bizOrderNo);
request.setOrderAmount(orderAmount);
request.setSubject(subject);
byte[] body = OpenApiPaymentMiniPaySign.signCreateOrder(request, outbound.clientSecret());
String url = outbound.baseUrl() + OpenApiPaymentMiniPaySign.PATH_CREATE_ORDER;
log.info("[pay-gateway] POST {}", url);
return PayGatewayClientUtils.postSigned(payGatewayRestTemplate, url, body, CREATE_TYPE, true);
}
@Override
public OrderAndTransactionCreateResponse createTransaction(
PayGatewayOutbound outbound,
String gatewayPaymentOrderNo,
PayChannel payChannel,
String subAppid,
String openId,
String buyerId,
String clientIp,
PayClientScene payClientScene) {
MiniPayTransactionCreateRequest request = new MiniPayTransactionCreateRequest();
request.setClientId(outbound.clientId());
request.setGatewayPaymentOrderNo(gatewayPaymentOrderNo);
request.setPayChannel(payChannel);
request.setSubAppid(subAppid);
request.setOpenId(openId);
request.setBuyerId(buyerId);
request.setClientIp(clientIp);
request.setPayClientScene(payClientScene);
byte[] body = OpenApiPaymentMiniPaySign.signCreateTransaction(request, outbound.clientSecret());
String url = outbound.baseUrl() + OpenApiPaymentMiniPaySign.PATH_CREATE_TRANSACTION;
log.info("[pay-gateway] POST {} payChannel={}", url, payChannel);
return PayGatewayClientUtils.postSigned(payGatewayRestTemplate, url, body, TX_TYPE, true);
}
@Override
public PaymentStatusQueryResponse queryOrderStatus(
PayGatewayOutbound outbound, String gatewayPaymentOrderNo, boolean syncFromChannel) {
PaymentStatusQueryRequest request = new PaymentStatusQueryRequest();
request.setClientId(outbound.clientId());
request.setGatewayPaymentOrderNo(gatewayPaymentOrderNo);
request.setSyncFromChannel(syncFromChannel);
byte[] body = OpenApiPaymentMiniPaySign.signQueryStatus(request, outbound.clientSecret());
String url = outbound.baseUrl() + OpenApiPaymentMiniPaySign.PATH_STATUS;
log.info("[pay-gateway] POST {} syncFromChannel={}", url, syncFromChannel);
return PayGatewayClientUtils.postSigned(payGatewayRestTemplate, url, body, STATUS_TYPE, true);
}
}

View File

@@ -5,10 +5,10 @@ import com.xspaceagi.pay.infra.gateway.utils.PayGatewayClientUtils;
import com.xspaceagi.pay.sdk.dto.ApiResponse;
import com.xspaceagi.pay.sdk.dto.CashierSessionCreateRequest;
import com.xspaceagi.pay.sdk.dto.CashierSessionCreateResponse;
import com.xspaceagi.pay.sdk.dto.PaymentOrderCreateResponse;
import com.xspaceagi.pay.sdk.dto.OrderCreateResponse;
import com.xspaceagi.pay.sdk.dto.PaymentStatusQueryRequest;
import com.xspaceagi.pay.sdk.dto.ScanOrderCreateRequest;
import com.xspaceagi.pay.sdk.dto.ScanOrderStatusQueryResponse;
import com.xspaceagi.pay.sdk.dto.PaymentStatusQueryResponse;
import com.xspaceagi.pay.sdk.sign.OpenApiCashierSign;
import com.xspaceagi.pay.sdk.sign.OpenApiPaymentScanSign;
import com.xspaceagi.pay.spec.gateway.PayGatewayOutbound;
@@ -22,11 +22,11 @@ import org.springframework.web.client.RestTemplate;
@Component
public class PaymentScanGatewayClient implements PaymentScanGateway {
private static final ParameterizedTypeReference<ApiResponse<PaymentOrderCreateResponse>> CREATE_TYPE =
new ParameterizedTypeReference<ApiResponse<PaymentOrderCreateResponse>>() {};
private static final ParameterizedTypeReference<ApiResponse<OrderCreateResponse>> CREATE_TYPE =
new ParameterizedTypeReference<ApiResponse<OrderCreateResponse>>() {};
private static final ParameterizedTypeReference<ApiResponse<ScanOrderStatusQueryResponse>> STATUS_TYPE =
new ParameterizedTypeReference<ApiResponse<ScanOrderStatusQueryResponse>>() {};
private static final ParameterizedTypeReference<ApiResponse<PaymentStatusQueryResponse>> STATUS_TYPE =
new ParameterizedTypeReference<ApiResponse<PaymentStatusQueryResponse>>() {};
private static final ParameterizedTypeReference<ApiResponse<CashierSessionCreateResponse>> CASHIER_SESSION_TYPE =
new ParameterizedTypeReference<ApiResponse<CashierSessionCreateResponse>>() {};
@@ -38,7 +38,7 @@ public class PaymentScanGatewayClient implements PaymentScanGateway {
}
@Override
public PaymentOrderCreateResponse createOrder(
public OrderCreateResponse createOrder(
PayGatewayOutbound outbound, String bizOrderNo, long orderAmount, String subject, String ext) {
ScanOrderCreateRequest request = new ScanOrderCreateRequest();
request.setClientId(outbound.clientId());
@@ -53,7 +53,7 @@ public class PaymentScanGatewayClient implements PaymentScanGateway {
}
@Override
public ScanOrderStatusQueryResponse queryOrderStatus(
public PaymentStatusQueryResponse queryOrderStatus(
PayGatewayOutbound outbound, String gatewayPaymentOrderNo, boolean syncFromChannel) {
PaymentStatusQueryRequest request = new PaymentStatusQueryRequest();
request.setClientId(outbound.clientId());

View File

@@ -13,6 +13,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
@@ -67,6 +68,31 @@ public final class PayGatewayClientUtils {
}
}
public static <T> T postMultipartSigned(
RestTemplate restTemplate,
String url,
MultiValueMap<String, Object> parts,
ParameterizedTypeReference<ApiResponse<T>> typeRef,
boolean requireNonNullData) {
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(parts, headers);
ResponseEntity<ApiResponse<T>> response = restTemplate.exchange(url, HttpMethod.POST, entity, typeRef);
return unwrap(response.getBody(), url, requireNonNullData);
} catch (HttpStatusCodeException e) {
log.warn(
"[pay-gateway] POST multipart failed url={} status={} body={}",
url,
e.getStatusCode(),
e.getResponseBodyAsString());
throw toHttpErrorException(e);
} catch (ResourceAccessException e) {
log.warn("[pay-gateway] POST multipart network error url={} msg={}", url, e.getMessage(), e);
throw new PayGatewayClientException();
}
}
private static <T> T unwrap(ApiResponse<T> resp, String url, boolean requireNonNullData) {
if (resp == null) {
log.warn("[pay-gateway] empty response url={}", url);