支付重构方案-各层详细设计-20251127


支付模块重构 - 详细设计

本文档详细描述各层的实现设计


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());

            // 一次性购买特殊处理
        }
    }
}

以上为各层详细设计,实际编码时按此设计实现即可。



扫描二维码,在手机上阅读
收藏

支付重构方案-实施检查清单-20251127

支付重构方案-核心接口设计-20251127

评 论
请登录后再评论