# SubscriptionController.java

    package com.yumu.noveltranslator.adapter.in.rest.web;

    import com.yumu.noveltranslator.port.dto.common.Result;
    import com.yumu.noveltranslator.port.dto.subscription.PaymentVerificationResponse;
    import com.yumu.noveltranslator.port.dto.subscription.CheckoutSessionResponse;
    import com.yumu.noveltranslator.port.dto.subscription.CheckoutSessionRequest;
    import com.yumu.noveltranslator.port.dto.subscription.SubscriptionStatusResponse;
    import com.yumu.noveltranslator.port.dto.subscription.PortalSessionResponse;
    import com.yumu.noveltranslator.util.SecurityUtil;
    import com.yumu.noveltranslator.port.in.SubscriptionPort;
    import jakarta.validation.Valid;
    import lombok.RequiredArgsConstructor;
    import org.springframework.web.bind.annotation.*;

    /**
     * 用户端订阅 API
     */
    @RestController
    @RequestMapping("/subscription")
    @RequiredArgsConstructor
    public class SubscriptionController {

        private final SubscriptionPort subscriptionPort;

        /**
         * 验证支付结果（前端回调时主动查询 Stripe session 状态）
         */
        @GetMapping("/verify")
        public Result<PaymentVerificationResponse> verify(@RequestParam String session_id) {
            Long userId = SecurityUtil.getRequiredUserId();
            return Result.ok(subscriptionPort.verifyCheckoutSession(session_id, userId));
        }

        /**
         * 创建支付会话
         */
        @PostMapping("/checkout")
        public Result<CheckoutSessionResponse> checkout(@RequestBody @Valid CheckoutSessionRequest request) {
            Long userId = SecurityUtil.getRequiredUserId();
            return Result.ok(subscriptionPort.createCheckoutSession(userId, request));
        }

        /**
         * 获取订阅状态
         */
        @GetMapping("/status")
        public Result<SubscriptionStatusResponse> status() {
            Long userId = SecurityUtil.getRequiredUserId();
            return Result.ok(subscriptionPort.getSubscriptionStatus(userId));
        }

        /**
         * 取消订阅
         */
        @PostMapping("/cancel")
        public Result<SubscriptionStatusResponse> cancel() {
            Long userId = SecurityUtil.getRequiredUserId();
            return Result.ok(subscriptionPort.cancelSubscription(userId));
        }

        /**
         * 跳转账单管理
         */
        @PostMapping("/portal")
        public Result<PortalSessionResponse> portal() {
            Long userId = SecurityUtil.getRequiredUserId();
            return Result.ok(subscriptionPort.createPortalSession(userId));
        }
    }