支付模块重构 - 详细设计
本文档详细描述各层的实现设计
1. 基础设施层详细设计
1.1 线程池设计
/**
* 线程池配置说明
*
* 1. paymentExecutor - 主支付线程池
* - 核心线程: 10 (根据CPU核数调整)
* - 最大线程: 50 (应对突发流量)
* - 队列容量: 1000 (缓冲待处理任务)
* - 拒绝策略: CallerRunsPolicy (调用者执行,降级保底)
* - 用途: 异步查询订单详情、更新订单状态等
*
* 2. notifyExecutor - 通知处理线程池
* - 核心线程: 5
* - 最大线程: 20
* - 队列容量: 500
* - 用途: 处理来自各平台的回调通知
*/
1.2 WebClient配置详解
@Configuration
public class WebClientConfig {
@Bean
public WebClient paymentWebClient() {
// 连接池配置
ConnectionProvider provider = ConnectionProvider.builder("payment-pool")
.maxConnections(500) // 最大连接数
.maxIdleTime(Duration.ofSeconds(20))
.maxLifeTime(Duration.ofSeconds(60))
.pendingAcquireTimeout(Duration.ofSeconds(60))
.evictInBackground(Duration.ofSeconds(120))
.build();
// HTTP客户端配置
HttpClient httpClient = HttpClient.create(provider)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000) // 连接超时
.responseTimeout(Duration.ofSeconds(30)) // 响应超时
.doOnConnected(conn -> conn
.addHandlerLast(new ReadTimeoutHandler(30, TimeUnit.SECONDS))
.addHandlerLast(new WriteTimeoutHandler(10, TimeUnit.SECONDS))
);
// 构建WebClient
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.codecs(configurer -> configurer
.defaultCodecs()
.maxInMemorySize(16 * 1024 * 1024)) // 16MB缓冲
.filter(logRequest()) // 请求日志
.filter(logResponse()) // 响应日志
.build();
}
private ExchangeFilterFunction logRequest() {
return ExchangeFilterFunction.ofRequestProcessor(request -> {
log.debug("HTTP请求: {} {}", request.method(), request.url());
return Mono.just(request);
});
}
private ExchangeFilterFunction logResponse() {
return ExchangeFilterFunction.ofResponseProcessor(response -> {
log.debug("HTTP响应: {}", response.statusCode());
return Mono.just(response);
});
}
}
1.3 分布式锁使用规范
/**
* 分布式锁使用规范
*
* 1. 锁粒度: 按订单号加锁,避免锁范围过大
* 2. 超时时间: 根据业务耗时设置,通常30秒
* 3. 看门狗: Redisson自动续期,防止业务未完成锁过期
* 4. 释放: 必须在finally中释放,避免死锁
*
* 使用示例:
*/
public void processWithLock(String orderId) {
String lockKey = "order:" + orderId;
boolean locked = distributedLock.tryLock(lockKey, 30, TimeUnit.SECONDS);
if (!locked) {
throw new PaymentException("系统繁忙,请稍后重试");
}
try {
// 业务逻辑
doBusinessLogic();
} finally {
distributedLock.unlock(lockKey);
}
}
1.4 幂等性设计
/**
* 幂等性设计
*
* 场景1: 订单创建幂等
* - Key: idempotent:create:{userId}:{productId}:{timestamp/10min}
* - 过期: 10分钟(防止短时间重复提交)
*
* 场景2: 订单验证幂等
* - Key: idempotent:verify:{orderId}
* - 过期: 24小时
*
* 场景3: 回调通知幂等
* - Key: idempotent:notify:{platform}:{transactionId}
* - 过期: 7天
*/
public interface IdempotentChecker {
// 检查+标记原子操作(使用Redis SETNX)
boolean checkAndMark(String type, String key, long expireSeconds);
// 仅检查
boolean isDuplicate(String type, String key);
// 仅标记
void markProcessed(String type, String key, long expireSeconds);
}
2. 网关层详细设计
2.1 微信支付网关
@Slf4j
@Component
public class WechatPaymentGateway extends AbstractPaymentGateway {
@Autowired
private WechatPayConfig config;
@Autowired
private WebClient webClient;
@Override
public PaymentPlatform getPlatform() {
return PaymentPlatform.WECHAT;
}
@Override
protected Object buildChannelCreateRequest(CreateOrderRequest request) {
// 构建微信APP支付请求
Map<String, Object> params = new HashMap<>();
params.put("appid", config.getAppId());
params.put("mchid", config.getMchId());
params.put("description", request.getProductName());
params.put("out_trade_no", request.getOrderId());
params.put("notify_url", config.getNotifyUrl());
Map<String, Object> amount = new HashMap<>();
amount.put("total", request.getAmount()); // 分
amount.put("currency", "CNY");
params.put("amount", amount);
return params;
}
@Override
protected Object doCreateOrder(Object channelRequest) {
// 调用微信下单API
RSAAutoCertificateConfig wechatConfig = buildWechatConfig();
AppService appService = new AppService.Builder().config(wechatConfig).build();
PrepayRequest prepayRequest = convertToPrepayRequest(channelRequest);
PrepayResponse response = appService.prepay(prepayRequest);
return response;
}
@Override
protected CreateOrderResponse adaptCreateOrderResponse(Object response, CreateOrderRequest request) {
PrepayResponse prepayResponse = (PrepayResponse) response;
// 生成APP支付参数
String prepayId = prepayResponse.getPrepayId();
String nonceStr = generateNonceStr();
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String sign = generateAppPaySign(prepayId, nonceStr, timestamp);
return CreateOrderResponse.builder()
.orderId(request.getOrderId())
.platform(PaymentPlatform.WECHAT)
.success(true)
.prepayId(prepayId)
.wechatParams(CreateOrderResponse.WechatAppPayParams.builder()
.appId(config.getAppId())
.partnerId(config.getMchId())
.prepayId(prepayId)
.packageValue("Sign=WXPay")
.nonceStr(nonceStr)
.timestamp(timestamp)
.sign(sign)
.build())
.build();
}
@Override
protected Object doVerifyOrder(VerifyOrderRequest request) {
// 查询微信订单
RSAAutoCertificateConfig wechatConfig = buildWechatConfig();
AppService appService = new AppService.Builder().config(wechatConfig).build();
QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
queryRequest.setMchid(config.getMchId());
queryRequest.setOutTradeNo(request.getOrderId());
return appService.queryOrderByOutTradeNo(queryRequest);
}
@Override
protected PaymentResult parseVerifyResult(Object response) {
Transaction transaction = (Transaction) response;
PaymentStatus status;
switch (transaction.getTradeState()) {
case SUCCESS:
status = PaymentStatus.SUCCESS;
break;
case CLOSED:
case PAYERROR:
status = PaymentStatus.FAILED;
break;
default:
status = PaymentStatus.PENDING;
}
return PaymentResult.builder()
.success(status == PaymentStatus.SUCCESS)
.status(status)
.transactionId(transaction.getTransactionId())
.amount(String.valueOf(transaction.getAmount().getPayerTotal() / 100.0))
.currency("CNY")
.payTime(parseWechatTime(transaction.getSuccessTime()))
.build();
}
@Override
public boolean verifyNotifySignature(String notifyBody, Map<String, String> headers) {
// 微信通知验签
String signature = headers.get("Wechatpay-Signature");
String timestamp = headers.get("Wechatpay-Timestamp");
String nonce = headers.get("Wechatpay-Nonce");
String serial = headers.get("Wechatpay-Serial");
// 使用微信SDK验证签名
return WechatSignatureUtil.verify(notifyBody, signature, timestamp, nonce, serial, config);
}
@Override
public NotifyMessage parseNotifyMessage(String notifyBody, Map<String, String> headers) {
// 解密通知内容
String decrypted = WechatSignatureUtil.decryptNotify(notifyBody, config.getApiV3Key());
JSONObject json = JSON.parseObject(decrypted);
return NotifyMessage.builder()
.platform(PaymentPlatform.WECHAT)
.orderId(json.getString("out_trade_no"))
.transactionId(json.getString("transaction_id"))
.status(parseTradeState(json.getString("trade_state")))
.amount(json.getJSONObject("amount").getString("payer_total"))
.currency("CNY")
.notifyTime(new Date())
.rawData(decrypted)
.build();
}
}
2.2 苹果支付网关
@Slf4j
@Component
public class ApplePaymentGateway extends AbstractPaymentGateway {
@Autowired
private ApplePayConfig config;
@Autowired
private AppleStoreKitClient storeKitClient;
@Autowired
private WebClient webClient;
@Override
public PaymentPlatform getPlatform() {
return PaymentPlatform.APPLE;
}
@Override
protected Object buildChannelCreateRequest(CreateOrderRequest request) {
// 苹果IAP不需要服务端创建订单,直接返回订单号
return request.getOrderId();
}
@Override
protected Object doCreateOrder(Object channelRequest) {
// 苹果IAP由客户端发起,服务端只记录
return channelRequest;
}
@Override
protected CreateOrderResponse adaptCreateOrderResponse(Object response, CreateOrderRequest request) {
return CreateOrderResponse.success(request.getOrderId(), PaymentPlatform.APPLE);
}
@Override
@Retryable(value = PaymentGatewayException.class, maxAttempts = 3,
backoff = @Backoff(delay = 2000, multiplier = 2))
protected Object doVerifyOrder(VerifyOrderRequest request) {
String receipt = request.getReceipt();
// 1. 先请求生产环境
JSONObject response = verifyReceiptWithApple(receipt, false);
int status = response.getIntValue("status");
// 2. 如果返回21007,说明是沙盒环境收据,重新请求沙盒
if (status == 21007) {
log.info("收据来自沙盒环境,切换到沙盒验证");
response = verifyReceiptWithApple(receipt, true);
}
return response;
}
private JSONObject verifyReceiptWithApple(String receipt, boolean sandbox) {
String url = sandbox ? config.getSandboxVerifyUrl() : config.getProductionVerifyUrl();
JSONObject requestBody = new JSONObject();
requestBody.put("receipt-data", receipt);
requestBody.put("password", config.getSharedSecret());
String responseBody = webClient.post()
.uri(url)
.bodyValue(requestBody.toJSONString())
.retrieve()
.bodyToMono(String.class)
.block();
return JSON.parseObject(responseBody);
}
@Override
protected PaymentResult parseVerifyResult(Object response) {
JSONObject json = (JSONObject) response;
int status = json.getIntValue("status");
if (status != 0) {
return PaymentResult.builder()
.success(false)
.status(PaymentStatus.FAILED)
.errorCode(String.valueOf(status))
.errorMsg(getAppleErrorMessage(status))
.build();
}
JSONObject receipt = json.getJSONObject("receipt");
return PaymentResult.builder()
.success(true)
.status(PaymentStatus.SUCCESS)
.transactionId(receipt.getString("transaction_id"))
.originalTransactionId(receipt.getString("original_transaction_id"))
.productId(receipt.getString("product_id"))
.currency(receipt.getString("currency"))
.amount(receipt.getString("price"))
.payTime(parseAppleTime(receipt.getLong("purchase_date_ms")))
.rawData(json.toJSONString())
.build();
}
@Override
public boolean verifyNotifySignature(String notifyBody, Map<String, String> headers) {
// 苹果Server-to-Server通知验签
try {
JSONObject notification = JSON.parseObject(notifyBody);
String signedPayload = notification.getString("signedPayload");
// 验证JWS签名
return AppleJWSVerifier.verify(signedPayload, config);
} catch (Exception e) {
log.error("苹果通知验签失败", e);
return false;
}
}
@Override
public NotifyMessage parseNotifyMessage(String notifyBody, Map<String, String> headers) {
JSONObject notification = JSON.parseObject(notifyBody);
String signedPayload = notification.getString("signedPayload");
// 解析JWS负载
String[] parts = signedPayload.split("\\.");
String payload = new String(Base64.getUrlDecoder().decode(parts[1]));
JSONObject payloadJson = JSON.parseObject(payload);
// 解析signedTransactionInfo
String signedTransactionInfo = payloadJson.getJSONObject("data")
.getString("signedTransactionInfo");
String[] txParts = signedTransactionInfo.split("\\.");
String txPayload = new String(Base64.getUrlDecoder().decode(txParts[1]));
JSONObject txJson = JSON.parseObject(txPayload);
return NotifyMessage.builder()
.platform(PaymentPlatform.APPLE)
.notifyType(payloadJson.getString("notificationType"))
.transactionId(txJson.getString("transactionId"))
.originalTransactionId(txJson.getString("originalTransactionId"))
.productId(txJson.getString("productId"))
.status(mapAppleNotifyStatus(payloadJson.getString("notificationType")))
.notifyTime(new Date())
.rawData(notifyBody)
.build();
}
/**
* 查询苹果订单详情(用于补全订单信息)
*/
public JSONObject queryTransactionInfo(String transactionId) {
return storeKitClient.getTransactionInfo(transactionId);
}
private String getAppleErrorMessage(int status) {
switch (status) {
case 21000: return "App Store无法读取你提供的JSON对象";
case 21002: return "receipt-data属性格式错误或丢失";
case 21003: return "收据无法被验证";
case 21004: return "共享密钥不匹配";
case 21005: return "收据服务器暂时不可用";
case 21006: return "收据有效但订阅已过期";
case 21007: return "收据来自测试环境";
case 21008: return "收据来自生产环境";
case 21009: return "内部数据访问错误";
case 21010: return "用户账户找不到或已删除";
default: return "未知错误: " + status;
}
}
}
2.3 谷歌支付网关
@Slf4j
@Component
public class GooglePaymentGateway extends AbstractPaymentGateway {
@Autowired
private GooglePayConfig config;
@Autowired
private AndroidPublisher androidPublisher;
@Override
public PaymentPlatform getPlatform() {
return PaymentPlatform.GOOGLE;
}
@Override
protected Object buildChannelCreateRequest(CreateOrderRequest request) {
return request.getOrderId();
}
@Override
protected Object doCreateOrder(Object channelRequest) {
return channelRequest;
}
@Override
protected CreateOrderResponse adaptCreateOrderResponse(Object response, CreateOrderRequest request) {
return CreateOrderResponse.success(request.getOrderId(), PaymentPlatform.GOOGLE);
}
@Override
protected Object doVerifyOrder(VerifyOrderRequest request) {
String purchaseToken = request.getReceipt();
String productId = request.getProductId();
// 判断是订阅还是一次性购买
ProductType productType = ProductType.fromCode(
request.getExtraParams().get("productType").toString());
if (productType.isSubscription()) {
return verifySubscription(purchaseToken);
} else {
return verifyPurchase(productId, purchaseToken);
}
}
/**
* 验证订阅
*/
private SubscriptionPurchaseV2 verifySubscription(String purchaseToken) {
try {
return androidPublisher.purchases()
.subscriptionsv2()
.get(config.getPackageName(), purchaseToken)
.execute();
} catch (Exception e) {
throw new PaymentGatewayException(PaymentPlatform.GOOGLE,
"验证订阅失败: " + e.getMessage(), e);
}
}
/**
* 验证一次性购买
*/
private ProductPurchase verifyPurchase(String productId, String purchaseToken) {
try {
ProductPurchase purchase = androidPublisher.purchases()
.products()
.get(config.getPackageName(), productId, purchaseToken)
.execute();
// 确认购买,防止退款
if (purchase.getAcknowledgementState() == 0) {
acknowledgePurchase(productId, purchaseToken);
}
return purchase;
} catch (Exception e) {
throw new PaymentGatewayException(PaymentPlatform.GOOGLE,
"验证购买失败: " + e.getMessage(), e);
}
}
/**
* 确认购买
*/
private void acknowledgePurchase(String productId, String purchaseToken) {
try {
ProductPurchasesAcknowledgeRequest request = new ProductPurchasesAcknowledgeRequest();
request.setDeveloperPayload("acknowledged");
androidPublisher.purchases()
.products()
.acknowledge(config.getPackageName(), productId, purchaseToken, request)
.execute();
log.info("谷歌购买确认成功: productId={}", productId);
} catch (Exception e) {
log.error("谷歌购买确认失败", e);
}
}
/**
* 确认订阅
*/
public void acknowledgeSubscription(String subscriptionId, String purchaseToken) {
try {
SubscriptionPurchasesAcknowledgeRequest request =
new SubscriptionPurchasesAcknowledgeRequest();
request.setDeveloperPayload("acknowledged");
androidPublisher.purchases()
.subscriptions()
.acknowledge(config.getPackageName(), subscriptionId, purchaseToken, request)
.execute();
log.info("谷歌订阅确认成功: subscriptionId={}", subscriptionId);
} catch (Exception e) {
log.error("谷歌订阅确认失败", e);
}
}
@Override
protected PaymentResult parseVerifyResult(Object response) {
if (response instanceof SubscriptionPurchaseV2) {
return parseSubscriptionResult((SubscriptionPurchaseV2) response);
} else {
return parsePurchaseResult((ProductPurchase) response);
}
}
private PaymentResult parseSubscriptionResult(SubscriptionPurchaseV2 subscription) {
String state = subscription.getSubscriptionState();
PaymentStatus status = "SUBSCRIPTION_STATE_ACTIVE".equals(state)
? PaymentStatus.SUCCESS : PaymentStatus.FAILED;
// 解析价格信息
Object recurringPrice = subscription.getLineItems().get(0)
.getAutoRenewingPlan().get("recurringPrice");
String currency = extractCurrency(recurringPrice);
String amount = extractAmount(recurringPrice);
return PaymentResult.builder()
.success(status == PaymentStatus.SUCCESS)
.status(status)
.transactionId(subscription.getLatestOrderId())
.currency(currency)
.amount(amount)
.expiryTime(parseGoogleTime(
subscription.getLineItems().get(0).getExpiryTime()))
.autoRenewing(subscription.getLineItems().get(0)
.getAutoRenewingPlan().getAutoRenewEnabled())
.rawData(JSON.toJSONString(subscription))
.build();
}
private PaymentResult parsePurchaseResult(ProductPurchase purchase) {
int purchaseState = purchase.getPurchaseState();
PaymentStatus status;
switch (purchaseState) {
case 0: status = PaymentStatus.SUCCESS; break; // 已购买
case 1: status = PaymentStatus.CANCELLED; break; // 已取消
case 2: status = PaymentStatus.PENDING; break; // 待处理
default: status = PaymentStatus.FAILED;
}
return PaymentResult.builder()
.success(status == PaymentStatus.SUCCESS)
.status(status)
.transactionId(purchase.getOrderId())
.payTime(new Date(purchase.getPurchaseTimeMillis()))
.rawData(JSON.toJSONString(purchase))
.build();
}
@Override
public boolean verifyNotifySignature(String notifyBody, Map<String, String> headers) {
// 谷歌实时开发者通知通过Pub/Sub推送,需验证JWT
// 实际实现中需要验证Google的JWT签名
return true;
}
@Override
public NotifyMessage parseNotifyMessage(String notifyBody, Map<String, String> headers) {
// 解析Google实时开发者通知
JSONObject json = JSON.parseObject(notifyBody);
JSONObject message = json.getJSONObject("message");
String data = new String(Base64.getDecoder().decode(message.getString("data")));
JSONObject notification = JSON.parseObject(data);
String notificationType = notification.containsKey("subscriptionNotification")
? "subscription" : "purchase";
return NotifyMessage.builder()
.platform(PaymentPlatform.GOOGLE)
.notifyType(notificationType)
.packageName(notification.getString("packageName"))
.purchaseToken(notification.getJSONObject(notificationType + "Notification")
.getString("purchaseToken"))
.notifyTime(new Date())
.rawData(data)
.build();
}
}
3. 门面层详细设计
3.1 Controller到Facade的适配
/**
* Controller层适配示例 - 保持原有接口不变
*/
@RestController
@RequestMapping("/pay/apply/iap") // 保持原有路径
public class ApplePayController {
@Autowired
private PaymentFacade paymentFacade;
/**
* 创建订单 - 保持原有接口签名
*/
@PostMapping("/creatOrder") // 保持原有路径(包括拼写错误)
@ResponseBody
public AjaxAppResult creatOrder(@RequestBody CreateOrderParam createOrderParam) {
log.info("创建苹果订单请求: {}", JSON.toJSONString(createOrderParam));
// 转换为统一请求模型
CreateOrderRequest request = CreateOrderRequest.builder()
.userId(createOrderParam.getUserId())
.productId(createOrderParam.getProductId())
.isAbroad(createOrderParam.getIsAboardFlag())
.build();
// 调用门面
CreateOrderResponse response = paymentFacade.createOrder(
PaymentPlatform.APPLE, request);
// 转换为原有响应格式
Map<String, Object> resMap = new HashMap<>();
resMap.put("orderId", response.getOrderId());
log.info("创建苹果订单响应: {}", JSON.toJSONString(resMap));
return AjaxAppResult.success(resMap);
}
/**
* 验证订单 - 保持原有接口签名
*/
@PostMapping("/verifyOrder")
@ResponseBody
public AjaxAppResult verifyOrder(@RequestBody VerifyOrderParam verifyOrderParam) {
log.info("验证苹果订单请求: {}", JSON.toJSONString(verifyOrderParam));
// 转换为统一请求模型
VerifyOrderRequest request = VerifyOrderRequest.builder()
.userId(verifyOrderParam.getUserId())
.orderId(verifyOrderParam.getOrderId())
.productId(verifyOrderParam.getProductId())
.receipt(verifyOrderParam.getReceipt())
.transactionId(verifyOrderParam.getTransactionId())
.originalTransactionId(verifyOrderParam.getOriginalTransactionId())
.build();
// 调用门面
VerifyOrderResponse response = paymentFacade.verifyOrder(
PaymentPlatform.APPLE, request);
// 转换为原有响应格式
Map<String, Object> resMap = new HashMap<>();
resMap.put("status", response.getStatus() == PaymentStatus.SUCCESS ? 0 : -1);
log.info("验证苹果订单响应: {}", JSON.toJSONString(resMap));
return AjaxAppResult.success(resMap);
}
}
3.2 PaymentFacade完整实现
@Slf4j
@Service
@RequiredArgsConstructor
public class PaymentFacadeImpl implements PaymentFacade {
private final PaymentGatewayFactory gatewayFactory;
private final OrderService orderService;
private final PermissionService permissionService;
private final ProductService productService;
private final IdempotentChecker idempotentChecker;
private final DistributedLock distributedLock;
private final PaymentEventPublisher eventPublisher;
private final PaymentAsyncExecutor asyncExecutor;
@Override
@Transactional(rollbackFor = Exception.class)
public CreateOrderResponse createOrder(PaymentPlatform platform, CreateOrderRequest request) {
String traceId = generateTraceId();
log.info("[{}][创建订单] 开始, platform={}, userId={}, productId={}",
traceId, platform, request.getUserId(), request.getProductId());
try {
// 1. 参数校验
validateCreateRequest(request);
// 2. 查询产品信息
AppProuct product = productService.getProduct(request.getProductId());
if (product == null) {
throw new PaymentException("74", "产品不存在");
}
// 3. 业务规则校验(如重复订阅检查)
checkSubscriptionRule(request.getUserId(), product);
// 4. 生成订单号
String orderId = PaymentIdGenerator.generate();
request.setOrderId(orderId);
// 5. 创建本地订单
PaymentOrder order = orderService.createOrder(request, product, platform);
log.info("[{}][创建订单] 本地订单已创建, orderId={}", traceId, orderId);
// 6. 调用支付网关
PaymentGateway gateway = gatewayFactory.getGateway(platform);
CreateOrderResponse response = gateway.createOrder(request);
response.setOrderId(orderId);
// 7. 更新订单状态
if (response.isSuccess()) {
orderService.updateOrderStatus(orderId, PaymentStatus.PENDING);
}
log.info("[{}][创建订单] 完成, orderId={}, success={}",
traceId, orderId, response.isSuccess());
return response;
} catch (PaymentException e) {
log.error("[{}][创建订单] 业务异常: {}", traceId, e.getMessage());
throw e;
} catch (Exception e) {
log.error("[{}][创建订单] 系统异常", traceId, e);
throw new PaymentException("系统繁忙,请稍后重试");
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public VerifyOrderResponse verifyOrder(PaymentPlatform platform, VerifyOrderRequest request) {
String traceId = generateTraceId();
String orderId = request.getOrderId();
String lockKey = "verify:" + orderId;
log.info("[{}][验证订单] 开始, platform={}, orderId={}", traceId, platform, orderId);
// 1. 分布式锁
boolean locked = distributedLock.tryLock(lockKey, 30, TimeUnit.SECONDS);
if (!locked) {
throw new PaymentException("订单正在处理中");
}
try {
// 2. 幂等性检查
if (idempotentChecker.isDuplicate("verify", orderId)) {
log.warn("[{}][验证订单] 重复验证, orderId={}", traceId, orderId);
throw new DuplicateOrderException("订单已验证,无需重复操作");
}
// 3. 查询本地订单
PaymentOrder order = orderService.getOrder(orderId);
if (order == null) {
throw new OrderNotFoundException("订单不存在");
}
// 4. 状态检查
if (order.getStatus() == PaymentStatus.SUCCESS) {
throw new DuplicateOrderException("订单已支付成功");
}
// 5. 调用网关验证
PaymentGateway gateway = gatewayFactory.getGateway(platform);
VerifyOrderResponse response = gateway.verifyOrder(request);
// 6. 处理验证结果
if (response.isSuccess() && response.getStatus() == PaymentStatus.SUCCESS) {
handlePaymentSuccess(traceId, order, response, platform);
}
log.info("[{}][验证订单] 完成, orderId={}, status={}",
traceId, orderId, response.getStatus());
return response;
} finally {
distributedLock.unlock(lockKey);
}
}
/**
* 处理支付成功
*/
private void handlePaymentSuccess(String traceId, PaymentOrder order,
VerifyOrderResponse response, PaymentPlatform platform) {
String orderId = order.getOrderId();
// 1. 更新订单
orderService.updateOrderWithPaymentResult(orderId, response);
log.info("[{}][验证订单] 订单已更新, orderId={}", traceId, orderId);
// 2. 下发权限
boolean granted = permissionService.grantPermission(
order.getUserId(), order.getProductId(), platform);
response.setPermissionGranted(granted);
log.info("[{}][验证订单] 权限下发, orderId={}, granted={}", traceId, orderId, granted);
// 3. 标记幂等
idempotentChecker.markProcessed("verify", orderId, 86400);
// 4. 发布事件
eventPublisher.publishSuccess(PaymentSuccessEvent.builder()
.orderId(orderId)
.userId(order.getUserId())
.productId(order.getProductId())
.platform(platform)
.amount(response.getPayAmount())
.currency(response.getCurrency())
.build());
// 5. 异步补全订单信息(如金额、币种)
asyncExecutor.execute(() -> {
try {
supplementOrderInfo(orderId, platform, response);
} catch (Exception e) {
log.error("[{}] 异步补全订单信息失败", traceId, e);
}
});
}
/**
* 异步补全订单信息
*/
private void supplementOrderInfo(String orderId, PaymentPlatform platform,
VerifyOrderResponse response) {
// 各平台可能需要额外查询订单详情来获取金额、币种等信息
// 这里异步执行,不影响主流程
}
private void checkSubscriptionRule(String userId, AppProuct product) {
// 检查是否重复订阅
if (ProductType.fromCode(product.getProductType()).isSubscription()) {
AppMember member = permissionService.getMemberInfo(userId);
if (member != null && "是".equals(member.getIsContinueSubscripeFlag())) {
throw new PaymentException("93", "您已处于续费状态,无需再次订阅");
}
}
}
private String generateTraceId() {
return UUID.randomUUID().toString().replace("-", "").substring(0, 16);
}
}
4. 责任链处理器详细设计
4.1 NotifyContext上下文
@Data
@Builder
public class NotifyContext {
/** 支付平台 */
private PaymentPlatform platform;
/** 原始通知体 */
private String rawBody;
/** 请求头 */
private Map<String, String> headers;
/** 解析后的通知消息 */
private NotifyMessage message;
/** 本地订单 */
private PaymentOrder order;
/** 处理结果 */
private NotifyHandleResult result;
/** 是否继续处理 */
private boolean continueChain = true;
/** 扩展属性 */
private Map<String, Object> attributes = new HashMap<>();
public void stopChain() {
this.continueChain = false;
}
public void setSuccess() {
this.result = NotifyHandleResult.success();
}
public void setFail(String message) {
this.result = NotifyHandleResult.fail(message);
this.continueChain = false;
}
}
4.2 处理器实现
/**
* 签名验证处理器
*/
@Slf4j
@Component
public class SignatureVerifyHandler implements NotifyHandler {
@Autowired
private PaymentGatewayFactory gatewayFactory;
@Override
public int getOrder() {
return 100; // 最先执行
}
@Override
public boolean handle(NotifyContext context) {
log.debug("执行签名验证, platform={}", context.getPlatform());
PaymentGateway gateway = gatewayFactory.getGateway(context.getPlatform());
boolean valid = gateway.verifyNotifySignature(context.getRawBody(), context.getHeaders());
if (!valid) {
log.warn("签名验证失败, platform={}", context.getPlatform());
context.setFail("签名验证失败");
return false;
}
log.debug("签名验证通过");
return true;
}
}
/**
* 幂等性处理器
*/
@Slf4j
@Component
public class IdempotentHandler implements NotifyHandler {
@Autowired
private IdempotentChecker idempotentChecker;
@Autowired
private DistributedLock distributedLock;
@Override
public int getOrder() {
return 200;
}
@Override
public boolean handle(NotifyContext context) {
NotifyMessage message = context.getMessage();
String idempotentKey = context.getPlatform() + ":" + message.getTransactionId();
// 分布式锁
String lockKey = "notify:" + idempotentKey;
boolean locked = distributedLock.tryLock(lockKey, 10, TimeUnit.SECONDS);
if (!locked) {
log.warn("获取通知处理锁失败, key={}", lockKey);
context.setFail("系统繁忙");
return false;
}
// 存储锁key以便后续释放
context.getAttributes().put("lockKey", lockKey);
// 幂等检查
if (idempotentChecker.isDuplicate("notify", idempotentKey)) {
log.info("重复通知已处理, key={}", idempotentKey);
context.setSuccess(); // 返回成功,避免平台重试
return false;
}
return true;
}
}
/**
* 业务处理器
*/
@Slf4j
@Component
public class BusinessHandler implements NotifyHandler {
@Autowired
private NotifyFacade notifyFacade;
@Autowired
private IdempotentChecker idempotentChecker;
@Autowired
private DistributedLock distributedLock;
@Override
public int getOrder() {
return 900; // 最后执行
}
@Override
public boolean handle(NotifyContext context) {
try {
// 执行业务逻辑
notifyFacade.processNotify(context);
// 标记幂等
String idempotentKey = context.getPlatform() + ":" +
context.getMessage().getTransactionId();
idempotentChecker.markProcessed("notify", idempotentKey, 7 * 86400);
context.setSuccess();
return true;
} catch (Exception e) {
log.error("业务处理失败", e);
context.setFail(e.getMessage());
return false;
} finally {
// 释放锁
String lockKey = (String) context.getAttributes().get("lockKey");
if (lockKey != null) {
distributedLock.unlock(lockKey);
}
}
}
}
5. 事件机制设计
5.1 支付成功事件
@Data
@Builder
public class PaymentSuccessEvent {
private String orderId;
private String userId;
private String productId;
private PaymentPlatform platform;
private String amount;
private String currency;
private Date payTime;
private String transactionId;
}
5.2 事件监听器
@Slf4j
@Component
public class SubscriptionProcessor {
@Autowired
private PermissionService permissionService;
@EventListener
public void onPaymentSuccess(PaymentSuccessEvent event) {
ProductType productType = productService.getProductType(event.getProductId());
if (productType.isSubscription()) {
log.info("处理订阅支付成功事件, orderId={}, userId={}",
event.getOrderId(), event.getUserId());
// 订阅类特殊处理
// 如:更新会员过期时间、发送订阅成功通知等
}
}
}
@Slf4j
@Component
public class PurchaseProcessor {
@EventListener
public void onPaymentSuccess(PaymentSuccessEvent event) {
ProductType productType = productService.getProductType(event.getProductId());
if (!productType.isSubscription()) {
log.info("处理一次性购买成功事件, orderId={}, userId={}",
event.getOrderId(), event.getUserId());
// 一次性购买特殊处理
}
}
}
以上为各层详细设计,实际编码时按此设计实现即可。