Download Laravel App
composer create-project --prefer-dist laravel/laravel my-app
C:\xampp\htdocs\laravel>composer create-project --prefer-dist laravel/laravel my-app
Connecting our Database
open .env file root directory.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laraveldb
DB_USERNAME=root
DB_PASSWORD=
Install Auth Scaffold
composer require laravel/ui
C:\xampp\htdocs\laravel\my-app>composer require laravel/ui
generate auth scaffold with bootstrap
php artisan ui bootstrap --auth
C:\xampp\htdocs\laravel\my-app>php artisan ui bootstrap --auth
install npm packages
npm install
C:\xampp\htdocs\laravel\my-app>npm install
built bootstrap CSS
npm run build
C:\xampp\htdocs\laravel\my-app>npm run build
Install Cashier Package
Laravel Cashier (Stripe)
https://laravel.com/docs/9.x/billing
composer require laravel/cashier
C:\xampp\htdocs\laravel\my-app>composer require laravel/cashier
publish cashier migration for creating tables
php artisan vendor:publish --tag="cashier-migrations"
C:\xampp\htdocs\laravel\my-app>php artisan vendor:publish --tag="cashier-migrations"
run the migration
C:\xampp\htdocs\laravel\my-app>php artisan migrate
login stripe account stripe.com test click Developers and get API keys
add API Kyes to .env
STRIPE_KEY=pk_test_secretkeyxxx
STRIPE_SECRET=sk_test_secretkeyxxxx
click Products tab and create plans
BUSINESS PLAN and PREMIUM and copy the API ID
add Cashier to User Model
Laravel\Cashier\Billable
app/Models/User.php
//
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Laravel\Cashier\Billable;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable, Billable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
Model Plan C:\xampp\htdocs\laravel\my-app>php artisan make:migration create_plans_table
database/migrations/create_plan_table.php
//database/migrations/create_plan_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('plans', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug');
$table->string('stripe_plan');
$table->integer('price');
$table->string('description');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('plans');
}
};
run this migration C:\xampp\htdocs\laravel\my-app>php artisan migrate
create model plan
php artisan make:model Plan
C:\xampp\htdocs\laravel\my-app>php artisan make:model Plan
app/Models/Plan.php
//app/Models/Plan.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Plan extends Model
{
use HasFactory;
protected $fillable = [
'name',
'slug',
'stripe_plan',
'price',
'description',
];
public function getRouteKeyName()
{
return 'slug';
}
}
Create Controller C:\xampp\htdocs\laravel\my-app>php artisan make:controller PlanController
app/Http/Controllers/PlanController.php
//app/Http/Controllers/PlanController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Plan;
use Illuminate\Support\Facades\Auth;
use App\Models\User;
class PlanController extends Controller
{
/**
* Write code on Method
*
* @return response()
*/
public function index()
{
$plans = Plan::get();
return view("plans", compact("plans"));
}
/**
* Write code on Method
*
* @return response()
*/
public function show(Plan $plan, Request $request)
{
$intent = auth()->user()->createSetupIntent();
return view("subscription", compact("plan", "intent"));
}
/**
* Write code on Method
*
* @return response()
*/
public function subscription(Request $request)
{
$plan = Plan::find($request->plan);
$subscription = $request->user()->newSubscription($request->plan, $plan->stripe_plan)
->create($request->token);
return view("subscription_success");
}
}
Create Routes
laravelproject\routes\web.php
//laravelproject\routes\web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PlanController;
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Route::middleware("auth")->group(function () {
Route::get('plans', [PlanController::class, 'index']);
Route::get('plans/{plan}', [PlanController::class, 'show'])->name("plans.show");
Route::post('subscription', [PlanController::class, 'subscription'])->name("subscription.create");
});
Create View Blade File resources/views/plans.blade.php
resources/views/subscription.blade.php
resources/views/subscription_success.blade.php
resources/views/plans.blade.php
//resources/views/plans.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<section>
<div class="container py-5">
<header class="text-center mb-5 text-white">
<div class="row">
<div class="col-lg-12 mx-auto">
<h1>Laravel 9 Cashier Stripe Subscription</h1>
<h3>PRICING</h3>
</div>
</div>
</header>
<div class="row text-center align-items-end">
<div class="col-lg-4 mb-5 mb-lg-0">
<div class="bg-white p-5 rounded-lg shadow">
<h1 class="h6 text-uppercase font-weight-bold mb-4">FREE</h1>
<h2 class="h1 font-weight-bold">$0<span class="text-small font-weight-normal ml-2">/ free</span></h2>
<div class="custom-separator my-4 mx-auto bg-primary"></div>
<ul class="list-unstyled my-5 text-small text-left">
<li class="mb-3">
<i class="fa fa-check mr-2 text-primary"></i> Lorem ipsum dolor sit amet</li>
<li class="mb-3">
<i class="fa fa-check mr-2 text-primary"></i> Sed ut perspiciatis</li>
<li class="mb-3">
<i class="fa fa-check mr-2 text-primary"></i> At vero eos et accusamus</li>
<li class="mb-3 text-muted">
<i class="fa fa-times mr-2"></i>
<del>Nam libero tempore</del>
</li>
<li class="mb-3 text-muted">
<i class="fa fa-times mr-2"></i>
<del>Sed ut perspiciatis</del>
</li>
<li class="mb-3 text-muted">
<i class="fa fa-times mr-2"></i>
<del>Sed ut perspiciatis</del>
</li>
</ul>
<a href="#" class="btn btn-primary btn-block shadow rounded-pill">Buy Now</a>
</div>
</div>
@foreach($plans as $plan)
<div class="col-lg-4 mb-5 mb-lg-0">
<div class="bg-white p-5 rounded-lg shadow">
<h1 class="h6 text-uppercase font-weight-bold mb-4">{{ $plan->name }}</h1>
<h2 class="h1 font-weight-bold">${{ $plan->price }}<span class="text-small font-weight-normal ml-2">/ month</span></h2>
<div class="custom-separator my-4 mx-auto bg-primary"></div>
<ul class="list-unstyled my-5 text-small text-left font-weight-normal">
<li class="mb-3">
<i class="fa fa-check mr-2 text-primary"></i> Lorem ipsum dolor sit amet</li>
<li class="mb-3">
<i class="fa fa-check mr-2 text-primary"></i> Sed ut perspiciatis</li>
<li class="mb-3">
<i class="fa fa-check mr-2 text-primary"></i> At vero eos et accusamus</li>
<li class="mb-3">
<i class="fa fa-check mr-2 text-primary"></i> Nam libero tempore</li>
<li class="mb-3">
<i class="fa fa-check mr-2 text-primary"></i> Sed ut perspiciatis</li>
<li class="mb-3 text-muted">
<i class="fa fa-times mr-2"></i>
<del>Sed ut perspiciatis</del>
</li>
</ul>
<a href="{{ route('plans.show', $plan->slug) }}" class="btn btn-primary btn-block shadow rounded-pill">Buy Now</a>
</div>
</div>
@endforeach
</div>
</div>
</section>
</div>
@endsection
resources/views/subscription.blade.php
//resources/views/subscription.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">
You will be charged ${{ number_format($plan->price, 2) }} for {{ $plan->name }} Plan
</div>
<div class="card-body">
<form id="payment-form" action="{{ route('subscription.create') }}" method="POST">
@csrf
<input type="hidden" name="plan" id="plan" value="{{ $plan->id }}">
<div class="row">
<div class="col-xl-4 col-lg-4">
<div class="form-group">
<label for="">Name</label>
<input type="text" name="name" id="card-holder-name" class="form-control" value="" placeholder="Name on the card">
</div>
</div>
</div>
<div class="row">
<div class="col-xl-4 col-lg-4">
<div class="form-group">
<label for="">Card details</label>
<div id="card-element"></div>
</div>
</div>
<div class="col-xl-12 col-lg-12">
<hr>
<button type="submit" class="btn btn-primary" id="card-button" data-secret="{{ $intent->client_secret }}">Purchase</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('{{ env('STRIPE_KEY') }}')
const elements = stripe.elements()
const cardElement = elements.create('card')
cardElement.mount('#card-element')
const form = document.getElementById('payment-form')
const cardBtn = document.getElementById('card-button')
const cardHolderName = document.getElementById('card-holder-name')
form.addEventListener('submit', async (e) => {
e.preventDefault()
cardBtn.disabled = true
const { setupIntent, error } = await stripe.confirmCardSetup(
cardBtn.dataset.secret, {
payment_method: {
card: cardElement,
billing_details: {
name: cardHolderName.value
}
}
}
)
if(error) {
cardBtn.disable = false
} else {
let token = document.createElement('input')
token.setAttribute('type', 'hidden')
token.setAttribute('name', 'token')
token.setAttribute('value', setupIntent.payment_method)
form.appendChild(token)
form.submit();
}
})
</script>
@endsection
resources/views/subscription_success.blade.php
//resources/views/subscription_success.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-body">
<div class="alert alert-success">
Subscription purchase successfully!
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
add css to laravel\my-app\resources\view\layouts\app.blade.php <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
<link href="{{ asset('css/app.css') }}" rel="stylesheet" type="text/css" >
my-app/public/css/app.css
php artisan make:seeder PlanSeeder
C:\xampp\htdocs\laravel\my-app>php artisan make:seeder PlanSeeder
database/seeders/PlanSeeder.php
php artisan db:seed --class=PlanSeeder
C:\xampp\htdocs\laravel\my-app>php artisan db:seed --class=PlanSeeder
check database plan table
Run C:\xampp\htdocs\laravel\my-app>php artisan serve
Starting Laravel development server: http://127.0.0.1:8000
http://localhost:8000/login
After, login
http://localhost:8000/plans
//my-app/public/css/app.css
body {
background: #00B4DB;
background: -webkit-linear-gradient(to right, #0083B0, #00B4DB);
background: linear-gradient(to right, #0083B0, #00B4DB);
color: #514B64;
min-height: 100vh;
}
.rounded-lg {
border-radius: 1rem !important;
}
.text-small {
font-size: 0.9rem !important;
}
.custom-separator {
width: 5rem;
height: 6px;
border-radius: 1rem;
}
.text-uppercase {
letter-spacing: 0.2em;
}
body {
background: #00B4DB;
background: -webkit-linear-gradient(to right, #0083B0, #00B4DB);
background: linear-gradient(to right, #0083B0, #00B4DB);
color: #514B64;
min-height: 100vh;
}
Create Seeder For Plans php artisan make:seeder PlanSeeder
C:\xampp\htdocs\laravel\my-app>php artisan make:seeder PlanSeeder
database/seeders/PlanSeeder.php
//database/seeders/PlanSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Plan;
class PlanSeeder extends Seeder
{
public function run()
{
$plans = [
[
'name' => 'Business Plan',
'slug' => 'business-plan',
'stripe_plan' => 'price_1LmB1SCXbPPKAWayGsb1DAkq',
'price' => 10,
'description' => 'Business Plan'
],
[
'name' => 'Premium',
'slug' => 'premium',
'stripe_plan' => 'price_1LnwbTCXbPPKAWay3ByKpcfq',
'price' => 20,
'description' => 'Premium'
]
];
foreach ($plans as $plan) {
Plan::create($plan);
}
}
}
run PermissionTableSeeder seeder php artisan db:seed --class=PlanSeeder
C:\xampp\htdocs\laravel\my-app>php artisan db:seed --class=PlanSeeder
check database plan table
Run C:\xampp\htdocs\laravel\my-app>php artisan serve
Starting Laravel development server: http://127.0.0.1:8000
http://localhost:8000/login
After, login
http://localhost:8000/plans
