Service Subscription Module

Description

The Service Subscription Module allows users to select and subscribe to various service packages, including both free and paid plans with different features. The module integrates with Stripe to handle payment transactions, manage subscription cycles, and other payment-related tasks.

Each service package can have multiple package plans with different prices and billing cycles. Users select a specific package plan to subscribe to, and the system will create a corresponding subscription in the database and on Stripe.

Main Features

  1. Free Plan Subscription

    • Allows users to subscribe to a free service package with resource limitations
    • No payment information required, automatically confirmed upon registration
    • Use case: New users want to try the system with basic features
  2. Paid Plan Subscription

    • Supports multiple service packages with different prices and privileges
    • Integrates with Stripe for secure payment processing
    • Automatically redirects users to the Stripe payment page
    • Use case: Businesses need advanced features such as increased product or member limits
  3. Stripe Webhook Handling

    • Receives and processes events from Stripe (successful payment, failed payment, etc.)
    • Updates subscription status in the system based on Stripe information
    • Records payment and event history
    • Use case: When users complete payment, the system needs to automatically activate the service
  4. Subscription Management

    • Allows users to view their current package information
    • Supports upgrading/downgrading between service packages
    • Handles changes in billing cycles
    • Use case: Customers want to upgrade to a higher plan to increase usage limits
  5. Cancel Subscription

    • Allows users to cancel their service subscription
    • Supports immediate cancellation or cancellation at the end of the billing cycle
    • Records cancellation reasons for analysis
    • Use case: Customers no longer need the service
  6. Subscription History Tracking

    • Stores history of subscription and payment changes
    • Displays details of events such as creation, upgrade, downgrade, cancellation
    • Use case: Administrators need to check payment or package change history
  7. Automatic Renewal

    • Supports automatic renewal of subscriptions upon expiration
    • Handles recurring payments via Stripe
    • Use case: Ensures uninterrupted service for customers
  8. Resource Limit Management

    • Each package has its own limits on the number of members, products, and product groups
    • Controls access to features based on the subscription package
    • Use case: Service tiering according to customer needs and budget
  9. Failed Payment Handling

    • Notifies users when payment fails
    • Supports retrying payment
    • Provides a grace period before service cancellation
    • Use case: Customer's credit card expires or has insufficient funds
  10. Reporting and Statistics

    • Provides information on the number of subscriptions per package
    • Tracks revenue and conversion rates
    • Use case: Analyze business performance and usage trends

This module ensures a smooth user experience during the subscription process, manages subscription status in the database, and efficiently handles webhook events from Stripe.

Overview Process Flow Diagram

---
config:
  theme: base
  layout: dagre
  flowchart:
    curve: linear
    htmlLabels: true
  themeVariables:
    edgeLabelBackground: "transparent"
---
flowchart TD
    %% Main components
    Client[User]
    
    %% API Controller Layer
    subgraph ApiControllerLayer["API Controller Layer"]
        SubscriptionController[SubscriptionController]
        WebhookController[WebhookController]
        Middleware{Middleware}
    end
    
    %% API Service Layer
    subgraph ApiServiceLayer["API Service Layer"]
        SubscriptionService(SubscriptionService)
    end
    
    %% Business Logic Services
    subgraph BusinessServices["Business Logic Services"]
        StripeService(StripeService)
    end
    
    %% Database Layer
    subgraph DatabaseLayer["Database Layer"]
        SubscriptionDB[(subscriptions)]
        UsersDB[(users)]
        HistoryDB[(subscription_histories)]
        PackageDB[(packages)]
        PackagePlanDB[(package_plans)]
        PackagePlanToProviderDB[(package_plan_to_providers)]
    end
    
    %% External Services
    subgraph ExternalServices["External Services"]
        StripeAPI((Stripe API))
    end
    
    %% Flow Steps
    Client --- Step1[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>1</span>
            <p style='margin-top: 8px'>Select service package</p>
        </div>
    ]
    Step1 --> SubscriptionController
    
    SubscriptionController --- Step2[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>2</span>
            <p style='margin-top: 8px'>Authenticate user & data</p>
        </div>
    ]
    Step2 --> Middleware
    Middleware --> Step2Output[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>2'</span>
            <p style='margin-top: 8px'>Authentication result</p>
        </div>
    ]
    Step2Output --> SubscriptionController
    
    SubscriptionController --- Step3[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>3</span>
            <p style='margin-top: 8px'>Get package & package_plan info</p>
        </div>
    ]
    Step3 --> PackagePlanDB
    PackagePlanDB --> PackageDB
    PackagePlanDB --> PackagePlanToProviderDB
    
    SubscriptionController --- Step3b[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>3'</span>
            <p style='margin-top: 8px'>Pass data to Service</p>
        </div>
    ]
    Step3b --> SubscriptionService
    
    SubscriptionService --- Step4A[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #99cc66 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>4A</span>
            <p style='margin-top: 8px'>Grant free plan</p>
        </div>
    ]
    Step4A --> SubscriptionDB
    Step4A --> HistoryDB
    Step4A --> UsersDB
    
    SubscriptionService --- Step4B[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #cc66cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>4B</span>
            <p style='margin-top: 8px'>Handle paid plan</p>
        </div>
    ]
    Step4B --> StripeService
    
    StripeService --- Step5[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #cc66cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>5</span>
            <p style='margin-top: 8px'>Create payment session</p>
        </div>
    ]
    Step5 --> StripeAPI
    
    StripeAPI --- Step6[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #cc66cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>6</span>
            <p style='margin-top: 8px'>Send webhook events</p>
        </div>
    ]
    Step6 --> WebhookController
    
    WebhookController --- Step7A[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>7A</span>
            <p style='margin-top: 8px'>Validate webhook</p>
        </div>
    ]
    Step7A --> StripeService
    
    WebhookController --- Step7B[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #cc66cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>7B</span>
            <p style='margin-top: 8px'>Process webhook event</p>
        </div>
    ]
    Step7B --> SubscriptionService
    
    SubscriptionService --- Step8[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>8</span>
            <p style='margin-top: 8px'>Update subscription status</p>
        </div>
    ]
    Step8 --> SubscriptionDB
    Step8 --> HistoryDB
    
    Client --- Step9[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>9</span>
            <p style='margin-top: 8px'>Manage subscription</p>
        </div>
    ]
    Step9 --> SubscriptionController
    
    %% Styling
    style Client fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style ApiControllerLayer fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style ApiServiceLayer fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style BusinessServices fill:#f5f0ff,stroke:#9966cc,stroke-width:2px
    style DatabaseLayer fill:#ffe6cc,stroke:#ff9900,stroke-width:2px
    style ExternalServices fill:#fcd9d9,stroke:#cc3333,stroke-width:2px
    style Step1 fill:transparent,stroke:transparent,stroke-width:1px
    style Step2 fill:transparent,stroke:transparent,stroke-width:1px
    style Step2Output fill:transparent,stroke:transparent,stroke-width:1px
    style Step3 fill:transparent,stroke:transparent,stroke-width:1px
    style Step3b fill:transparent,stroke:transparent,stroke-width:1px
    style Step4A fill:transparent,stroke:transparent,stroke-width:1px
    style Step4B fill:transparent,stroke:transparent,stroke-width:1px
    style Step5 fill:transparent,stroke:transparent,stroke-width:1px
    style Step6 fill:transparent,stroke:transparent,stroke-width:1px
    style Step7A fill:transparent,stroke:transparent,stroke-width:1px
    style Step7B fill:transparent,stroke:transparent,stroke-width:1px
    style Step8 fill:transparent,stroke:transparent,stroke-width:1px
    style Step9 fill:transparent,stroke:transparent,stroke-width:1px

Feature List Table

Name Link Description
Service Subscription Service Subscription Process of registering a new service package, including both free and paid plans
Subscription Management Subscription Management Update, upgrade, downgrade, and cancel service package subscriptions
Subscription History Subscription History Track and display subscription change history
Stripe Webhook Handling Webhook Handling Handle webhook events from Stripe to update subscription status

Related Database

erDiagram
    users {
        bigint id PK "Primary key"
        string name "User name"
        string email "User email (unique)"
        string uid "User UID (unique)"
        string payment_provider_customer_id "Customer ID from Stripe (nullable)"
        integer status "User status"
        string remember_token "Remember login token (nullable)"
        timestamp created_at "Created at"
        timestamp updated_at "Updated at"
        timestamp deleted_at "Soft delete at (nullable)"
    }
    
    packages {
        bigint id PK "Primary key"
        string name "Package name"
        string slug "Package slug (unique)"
        text description "Package description (nullable)"
        text image "Image path (nullable)"
        integer schedule_id "Schedule ID"
        integer schedule_priority "Schedule priority"
        integer max_member "Max members (nullable)"
        integer max_product_group "Max product groups (nullable)"
        integer max_product "Max products (nullable)"
        integer max_category "Max categories (nullable)" 
        integer max_search_query "Max search queries (nullable)"
        integer max_viewpoint "Max viewpoints (nullable)"
        string data_visible "Data visibility"
        tinyint api_available "API available"
        tinyint status "Package status"
        timestamp created_at "Created at"
        timestamp updated_at "Updated at"
    }
    
    package_plans {
        bigint id PK "Primary key"
        string name "Plan name"
        string slug "Plan slug (unique)"
        bigint package_id FK "Linked to packages table"
        double amount "Amount"
        string currency "Currency"
        string type "Plan type (recurring, one_time)"
        string billing_plan "Billing cycle"
        tinyint status "Plan status"
        timestamp created_at "Created at"
        timestamp updated_at "Updated at"
    }
    
    package_plan_to_providers {
        bigint id PK "Primary key"
        bigint package_plan_id FK "Linked to package_plans table"
        bigint provider_id FK "Linked to payment_providers table"
        string provider_price_id "Price ID from provider (Stripe)"
        tinyint status "Status"
        timestamp created_at "Created at"
        timestamp updated_at "Updated at"
    }
    
    subscriptions {
        bigint id PK "Primary key"
        string slug "Subscription slug (nullable, unique)"
        bigint package_id FK "Linked to packages table"
        bigint package_plan_id FK "Linked to package_plans table"
        bigint group_id FK "Linked to groups table"
        bigint user_id FK "Linked to users table"
        string email "User email"
        string payment_provider_customer_id "Customer ID from payment provider"
        string payment_provider_subscription_id "Subscription ID from provider (nullable, unique)"
        boolean auto_renew "Auto-renew"
        string status "Subscription status"
        string scheduled_plan_id "Scheduled plan ID (nullable)"
        timestamp scheduled_plan_change_at "Scheduled plan change at (nullable)"
        timestamp canceled_at "Canceled at (nullable)"
        string canceled_reason "Cancel reason (nullable)"
        timestamp first_register_at "First registered at (nullable)"
        timestamp deadline_at "Deadline (nullable)"
        timestamp grace_period_end_at "Grace period end at (nullable)"
        timestamp created_at "Created at"
        timestamp updated_at "Updated at"
        timestamp deleted_at "Soft delete at (nullable)"
    }
    
    subscription_histories {
        bigint id PK "Primary key"
        bigint subscription_id FK "Linked to subscriptions table"
        bigint package_id FK "Linked to packages table"
        bigint package_plan_id FK "Linked to package_plans table"
        bigint group_id FK "Linked to groups table"
        bigint user_id FK "Linked to users table"
        string old_plan_id "Old plan ID (nullable)"
        string payment_intent_id "Stripe payment intent ID (nullable, unique)"
        string invoice_id "Stripe invoice ID (nullable, unique)"
        string billing_plan "Billing cycle"
        integer payment_attempt "Payment attempt count (nullable)"
        json payment_method_details "Payment method details (nullable)"
        string type "Subscription type"
        decimal amount "Amount"
        string currency "Currency"
        integer schedule_id "Schedule ID"
        integer schedule_priority "Schedule priority"
        integer product_group_usage "Used product groups"
        integer product_usage "Used products"
        integer category_usage "Used categories"
        integer search_query_usage "Used search queries"
        integer max_member "Max members"
        integer max_product_group "Max product groups"
        integer max_product "Max products"
        integer max_category "Max categories"
        integer max_search_query "Max search queries"
        integer max_viewpoint "Max viewpoints"
        string data_visible "Data visibility"
        tinyint api_available "API available"
        tinyint status "Status"
        string payment_status "Payment status"
        timestamp started_at "Started at (nullable)"
        timestamp expires_at "Expires at (nullable)"
        timestamp paid_at "Paid at (nullable)"
        timestamp created_at "Created at"
        timestamp updated_at "Updated at"
    }
    
    stripe_webhook_events {
        bigint id PK "Primary key"
        string stripe_event_id "Stripe event ID"
        string request_id "Request ID (nullable)"
        string event_type "Event type"
        enum status "Status (pending, processing, completed, failed)"
        text error "Error (nullable)"
        timestamp processed_at "Processed at (nullable)"
        timestamp created_at "Created at"
        timestamp updated_at "Updated at"
    }

    users ||--o{ subscriptions : has
    packages ||--o{ package_plans : has
    package_plans ||--o{ package_plan_to_providers : has
    packages ||--o{ subscriptions : associated_with
    package_plans ||--o{ subscriptions : associated_with
    package_plan_to_providers ||--o{ subscriptions : used_for
    subscriptions ||--o{ subscription_histories : has

Related API Endpoints

Method Endpoint Controller Description
GET /api/general/subscription SubscriptionController@index Get the current user's subscription list
POST /api/general/subscription SubscriptionController@store Register a new service package
GET /api/general/subscription/active SubscriptionController@getActiveSubscription Get current subscription information
PUT /api/general/subscription/{id} SubscriptionController@update Update/change subscription package
POST /api/general/subscription/free-plan SubscriptionController@registerFreePlan Register for a free plan
GET /api/admin/group/packages PackageController@index Get available service packages list
GET /api/general/package-plan PackagePlanController@index Get service package plan list
GET /api/general/checkout CheckoutController@index Get payment information
POST /api/general/checkout CheckoutController@store Create a new payment session
POST /api/general/stripe/checkout/session StripeController@createCheckoutSession Create Stripe payment session
POST /api/general/stripe/portal/session StripeController@createPortalSession Create Stripe management session
POST /api/admin/stripe/webhook WebhookController@handleWebhook Handle webhook events from Stripe