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
-
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
-
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
-
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
-
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
-
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
-
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
-
Automatic Renewal
- Supports automatic renewal of subscriptions upon expiration
- Handles recurring payments via Stripe
- Use case: Ensures uninterrupted service for customers
-
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
-
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
-
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 |