ποΈ CreatorContent.net
U
User
!
-
!
-
Manage Subscription
Manage Tokens
Storage
Media Library β
Documentation
User Dashboard
Podcasts
Podcasts
Episodes
Transcriptions
Contributors
Studio
Public Profile
Public Profiles
Blog
Event Lists
Surveys
Contact Forms
Subscribers
Notifications & Shoutouts
Development
React Test
Media Library
Help Center
Admin Dashboard
Logout
Back to Documentation
Subscription System
Subscription System
File: subscription-system.markdown
Documentation Index
Loading documentation...
# User Subscription System for Laravel with Stripe and Cashier This document outlines the implementation of a user subscription system for a Laravel website using Laravel Cashier with Stripe. The system includes a separate table for plan features and covers database schema, setup, subscription management, and best practices. ## 1. Overview The subscription system allows users to subscribe to plans, manage their subscriptions, and process payments via Stripe. Laravel Cashier simplifies integration with Stripe, handling subscription creation, updates, cancellations, and webhooks. The system includes a separate `plan_features` table for granular feature management. ## 2. Database Schema The following tables are required to manage subscriptions and plan features. ### 2.1. Users Table Assumes a standard Laravel `users` table exists: - `id` (primary key) - `name` - `email` - `password` - `stripe_id` (added by Cashier for Stripe customer ID) - `created_at`, `updated_at` Run Cashierβs migration to add Stripe-related columns: ```bash php artisan cashier:install ``` ### 2.2. Plans Table Stores subscription plan details. ```php Schema::create('plans', function (Blueprint $table) { $table->id(); $table->string('name'); // e.g., "Basic", "Pro" $table->string('slug')->unique(); // e.g., "basic", "pro" $table->decimal('price', 8, 2); // e.g., 9.99 $table->string('interval'); // e.g., "month", "year" $table->integer('interval_count'); // e.g., 1 (for 1 month, 1 year) $table->string('stripe_plan_id')->unique(); // Stripe plan ID $table->text('description')->nullable(); $table->boolean('is_active')->default(true); $table->timestamps(); }); ``` ### 2.3. Plan Features Table Stores individual features for each plan. ```php Schema::create('plan_features', function (Blueprint $table) { $table->id(); $table->foreignId('plan_id')->constrained()->onDelete('cascade'); $table->string('name'); // e.g., "Unlimited Projects" $table->string('value')->nullable(); // e.g., "true", "100" $table->string('type')->default('feature'); // e.g., "feature", "limit" $table->timestamps(); }); ``` ### 2.4. Subscriptions Table Tracks user subscriptions (created by Cashierβs migration). ```php Schema::create('subscriptions', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->onDelete('cascade'); $table->string('name'); // Subscription name (e.g., "default") $table->string('stripe_id')->unique(); // Stripe subscription ID $table->string('stripe_status'); // e.g., "active", "past_due" $table->string('stripe_price')->nullable(); // Stripe price ID $table->integer('quantity')->nullable(); $table->timestamp('trial_ends_at')->nullable(); $table->timestamp('ends_at')->nullable(); $table->timestamps(); }); ``` ### 2.5. Subscription Payments Table Logs payment history. ```php Schema::create('subscription_payments', function (Blueprint $table) { $table->id(); $table->foreignId('subscription_id')->constrained()->onDelete('cascade'); $table->decimal('amount', 8, 2); $table->string('status'); // e.g., "paid", "failed" $table->string('stripe_charge_id')->nullable(); // Stripe charge ID $table->timestamp('paid_at')->nullable(); $table->timestamps(); }); ``` ## 3. Setup Instructions ### 3.1. Install Laravel Cashier Install Cashier for Stripe: ```bash composer require laravel/cashier ``` ### 3.2. Configure Stripe - Add Stripe keys to `.env`: ```env STRIPE_KEY=your-stripe-public-key STRIPE_SECRET=your-stripe-secret-key STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret ``` - Publish Cashierβs configuration: ```bash php artisan vendor:publish --tag="cashier-migrations" ``` - Run migrations: ```bash php artisan migrate ``` ### 3.3. Configure Models - Add `Billable` trait to the `User` model: ```php use Laravel\Cashier\Billable; class User extends Authenticatable { use Billable; // ... } ``` - Create `Plan` and `PlanFeature` models: ```php // app/Models/Plan.php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Plan extends Model { protected $fillable = [ 'name', 'slug', 'price', 'interval', 'interval_count', 'stripe_plan_id', 'description', 'is_active' ]; public function features() { return $this->hasMany(PlanFeature::class); } } ``` ```php // app/Models/PlanFeature.php namespace App\Models; use Illuminate\Database\Eloquent\Model; class PlanFeature extends Model { protected $fillable = ['plan_id', 'name', 'value', 'type']; public function plan() { return $this->belongsTo(Plan::class); } } ``` ### 3.4. Create Stripe Plans Create plans in Stripe (manually via dashboard or programmatically): ```php use Stripe\Plan as StripePlan; StripePlan::create([ 'amount' => 999, // $9.99 'currency' => 'usd', 'interval' => 'month', 'product' => ['name' => 'Basic Plan'], 'id' => 'basic-monthly', ]); ``` Store the `stripe_plan_id` in the `plans` table. ## 4. Subscription Management ### 4.1. Creating a Subscription - **Frontend**: Use Stripe Elements for secure payment collection. - **Backend**: Create a subscription via Cashier. ```php // app/Http/Controllers/SubscriptionController.php namespace App\Http\Controllers; use App\Models\Plan; use Illuminate\Http\Request; class SubscriptionController extends Controller { public function store(Request $request) { $plan = Plan::findOrFail($request->plan_id); $user = auth()->user(); $user->newSubscription('default', $plan->stripe_plan_id) ->trialDays(14) ->create($request->payment_method); return redirect()->route('dashboard') ->with('success', 'Subscribed successfully!'); } } ``` ### 4.2. Displaying Plans and Features - Show plans and their features in the frontend: ```php // routes/web.php Route::get('/plans', function () { $plans = App\Models\Plan::with('features')->where('is_active', true)->get(); return view('plans.index', compact('plans')); }); ``` ```blade <!-- resources/views/plans/index.blade.php --> @foreach ($plans as $plan) <h2>{{ $plan->name }} - ${{ $plan->price }}/{{ $plan->interval }}</h2> <ul> @foreach ($plan->features as $feature) <li>{{ $feature->name }}: {{ $feature->value }}</li> @endforeach </ul> <form method="POST" action="{{ route('subscriptions.store') }}"> @csrf <input type="hidden" name="plan_id" value="{{ $plan->id }}"> <div id="card-element"></div> <button type="submit">Subscribe</button> </form> @endforeach <script src="https://js.stripe.com/v3/"></script> <script> const stripe = Stripe('{{ env('STRIPE_KEY') }}'); const elements = stripe.elements(); const card = elements.create('card'); card.mount('#card-element'); </script> ``` ### 4.3. Handling Webhooks - Set up a webhook endpoint for Stripe events: ```php // routes/web.php Route::post('/webhook/stripe', [WebhookController::class, 'handleWebhook']); ``` ```php // app/Http/Controllers/WebhookController.php namespace App\Http\Controllers; use Laravel\Cashier\Http\Controllers\WebhookController as CashierWebhookController; class WebhookController extends CashierWebhookController { public function handleInvoicePaymentSucceeded($payload) { $subscription = Subscription::where('stripe_id', $payload['data']['object']['subscription'])->first(); if ($subscription) { SubscriptionPayment::create([ 'subscription_id' => $subscription->id, 'amount' => $payload['data']['object']['amount_paid'] / 100, 'status' => 'paid', 'stripe_charge_id' => $payload['data']['object']['charge'], 'paid_at' => now(), ]); } return response('Webhook handled', 200); } } ``` - Configure the webhook in Stripeβs dashboard with the endpoint: `https://your-site.com/webhook/stripe`. ### 4.4. Managing Subscriptions - **Upgrade/Downgrade**: ```php public function update(Request $request) { $plan = Plan::findOrFail($request->plan_id); $user = auth()->user(); $user->subscription('default')->swap($plan->stripe_plan_id); return redirect()->route('dashboard')->with('success', 'Plan updated!'); } ``` - **Cancel**: ```php public function cancel(Request $request) { auth()->user()->subscription('default')->cancel(); return redirect()->route('dashboard')->with('success', 'Subscription canceled!'); } ``` - **Resume** (if within grace period): ```php public function resume(Request $request) { auth()->user()->subscription('default')->resume(); return redirect()->route('dashboard')->with('success', 'Subscription resumed!'); } ``` ### 4.5. Trial and Subscription Status - Check if a user is subscribed: ```php if (auth()->user()->subscribed('default')) { // User has an active subscription } ``` - Check trial status: ```php if (auth()->user()->onTrial('default')) { // User is on trial } ``` ## 5. Best Practices - **Secure Payments**: Use Stripe Elements for PCI-compliant payment collection. - **Webhook Reliability**: Process webhooks asynchronously using Laravel queues (`php artisan queue:work`). - **Error Handling**: Handle payment failures gracefully and notify users (e.g., via Laravel notifications). ```php use Illuminate\Support\Facades\Notification; Notification::send($user, new PaymentFailed($invoice)); ``` - **Testing**: Use Stripeβs test mode (`test` API keys) and write tests: ```php public function test_user_can_subscribe() { $user = User::factory()->create(); $plan = Plan::factory()->create(['stripe_plan_id' => 'price_123']); $user->newSubscription('default', 'price_123')->create('pm_card_visa'); $this->assertTrue($user->subscribed('default')); } ``` - **User Experience**: - Display a clear plan comparison table with features. - Send email notifications for subscription events (e.g., renewal, payment failure). - Provide a user dashboard to view subscription status, payment history, and manage plans. ## 6. Additional Notes - **Stripe Configuration**: Ensure plans are created in Stripe with matching `stripe_plan_id` values in the `plans` table. - **Database Migrations**: Run migrations after defining the schema to create tables. - **Scalability**: Use Laravel Horizon for queue management if handling many webhooks. - **Compliance**: Ensure GDPR/CCPA compliance for user data. For pricing details on subscription services like SuperGrok, visit https://x.ai/grok. For API-related queries, visit https://x.ai/api.
0
π Page Notes
+ Add New
Add New Note
Type
βΉοΈ Info
π Bug
β¨ Feature Request
π‘ Improvement
β Missing Feature
π¨ Design Changes
Title (optional)
Note Content
π Add Note