added cashier subscription, subscription plans
This commit is contained in:
100
app/Filament/Resources/PlanResource.php
Normal file
100
app/Filament/Resources/PlanResource.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Resources\PlanResource\Pages;
|
||||
use App\Filament\Resources\PlanResource\RelationManagers;
|
||||
use App\Models\Plan;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Components\KeyValue;
|
||||
use Filament\Forms\Components\RichEditor;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Forms\Set;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Columns\BooleanColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
use Illuminate\Support\Str;
|
||||
use phpDocumentor\Reflection\Types\Boolean;
|
||||
|
||||
class PlanResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Plan::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
||||
|
||||
protected static ?string $navigationGroup = 'Web Master';
|
||||
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Section::make('Plan Information')
|
||||
->description('Add a new plan')
|
||||
->schema([
|
||||
TextInput::make('name')->label('Page Name')
|
||||
->required(),
|
||||
TextInput::make('description'),
|
||||
TextInput::make('product_id')->required(),
|
||||
TextInput::make('pricing_id')->required(),
|
||||
TextInput::make('price')->numeric()->required(),
|
||||
Select::make('monthly_billing')->options([
|
||||
1 => 'Monthly',
|
||||
0 => 'Yearly',
|
||||
])->default(1)->required(),
|
||||
KeyValue::make('details')
|
||||
->label('Plan Details (Optional)')
|
||||
->keyPlaceholder('Name')
|
||||
->valuePlaceholder('Content')
|
||||
->reorderable(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('name')->label('Name'),
|
||||
TextColumn::make('product_id')->label('Product'),
|
||||
TextColumn::make('pricing_id')->label('Pricing'),
|
||||
TextColumn::make('price')->label('Price'),
|
||||
BooleanColumn::make('monthly_billing')->label('Monthly Billing'),
|
||||
])
|
||||
->filters([
|
||||
//
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\EditAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListPlans::route('/'),
|
||||
'create' => Pages\CreatePlan::route('/create'),
|
||||
'edit' => Pages\EditPlan::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
21
app/Filament/Resources/PlanResource/Pages/CreatePlan.php
Normal file
21
app/Filament/Resources/PlanResource/Pages/CreatePlan.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\PlanResource\Pages;
|
||||
|
||||
use App\Filament\Resources\PlanResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreatePlan extends CreateRecord
|
||||
{
|
||||
protected static string $resource = PlanResource::class;
|
||||
|
||||
protected function getCreatedNotification(): ?Notification
|
||||
{
|
||||
return Notification::make()
|
||||
->success()
|
||||
->title('Plan created')
|
||||
->body('Plan created successfully');
|
||||
}
|
||||
}
|
||||
32
app/Filament/Resources/PlanResource/Pages/EditPlan.php
Normal file
32
app/Filament/Resources/PlanResource/Pages/EditPlan.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\PlanResource\Pages;
|
||||
|
||||
use App\Filament\Resources\PlanResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditPlan extends EditRecord
|
||||
{
|
||||
protected static string $resource = PlanResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
protected function getRedirectUrl(): ?string
|
||||
{
|
||||
return $this->getResource()::getUrl('index');
|
||||
}
|
||||
|
||||
protected function getSavedNotification(): ?Notification
|
||||
{
|
||||
return Notification::make()
|
||||
->success()
|
||||
->title('Plan updated')
|
||||
->body('Plan updated successfully');
|
||||
}
|
||||
}
|
||||
19
app/Filament/Resources/PlanResource/Pages/ListPlans.php
Normal file
19
app/Filament/Resources/PlanResource/Pages/ListPlans.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\PlanResource\Pages;
|
||||
|
||||
use App\Filament\Resources\PlanResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListPlans extends ListRecords
|
||||
{
|
||||
protected static string $resource = PlanResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
37
app/Listeners/StripeEventListener.php
Normal file
37
app/Listeners/StripeEventListener.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Laravel\Cashier\Events\WebhookReceived;
|
||||
use Livewire\Livewire;
|
||||
|
||||
class StripeEventListener
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*/
|
||||
public function handle(WebhookReceived $event): void
|
||||
{
|
||||
if ($event->payload['type'] === 'invoice.payment_succeeded') {
|
||||
session()->flash('alert', ['type' => 'success', 'message' => 'Payment completed successfully.']);
|
||||
Log::info('Payment succeeded');
|
||||
}
|
||||
|
||||
if ($event->payload['type'] === 'customer.subscription.deleted') {
|
||||
session()->flash('alert', ['type' => 'error', 'message' => 'Subscription canceled.']);
|
||||
Log::info('Subscription canceled');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,70 @@
|
||||
|
||||
namespace App\Livewire\Dashboard;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Livewire\Component;
|
||||
|
||||
class Dashboard extends Component
|
||||
{
|
||||
public $message;
|
||||
public $subscription;
|
||||
public $plans;
|
||||
|
||||
public function paymentStatus(Request $request)
|
||||
{
|
||||
$status = $request->route('status');
|
||||
$currentUrl = $request->fullUrl();
|
||||
if ($status == 'success') {
|
||||
return redirect()->route('dashboard')->with('status', 'success');
|
||||
} elseif ($status == 'cancel') {
|
||||
return redirect()->route('dashboard')->with('status', 'cancel');
|
||||
}
|
||||
}
|
||||
|
||||
public function mount(Request $request)
|
||||
{
|
||||
try {
|
||||
$status = $request->session()->get('status');
|
||||
if (isset($status)) {
|
||||
if ($status == 'success') {
|
||||
$this->message = ['type' => 'success', 'message' => 'Order completed successfully.'];
|
||||
} else {
|
||||
$this->message = ['type' => 'error', 'message' => 'Order cancelled.'];
|
||||
}
|
||||
$request->session()->forget('status');
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
|
||||
}
|
||||
|
||||
if (auth()->user()->subscribedToProduct(config('app.plans')[0]['product_id'])) {
|
||||
try {
|
||||
$result = auth()->user()->subscriptions()->where(['stripe_status' => 'active'])->orderByDesc('updated_at')->first();
|
||||
$userPriceID = $result['items'][0]['stripe_price'];
|
||||
$subscriptionEnd = $result['ends_at'];
|
||||
|
||||
$planName = null; // Default value if not found
|
||||
|
||||
foreach (config('app.plans') as $plan) {
|
||||
if ($plan['pricing_id'] === $userPriceID) {
|
||||
$planName = $plan['name'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->subscription['name'] = $planName;
|
||||
$this->subscription['ends_at'] = $subscriptionEnd;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
|
||||
\Log::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.dashboard.dashboard')->layout('components.layouts.dashboard');
|
||||
return view('livewire.dashboard.dashboard')->layout('components.layouts.dashboard')->with('message', $this->message);
|
||||
}
|
||||
}
|
||||
|
||||
25
app/Livewire/Dashboard/Pricing.php
Normal file
25
app/Livewire/Dashboard/Pricing.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Dashboard;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class Pricing extends Component
|
||||
{
|
||||
public $plans;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->plans = config('app.plans');
|
||||
}
|
||||
|
||||
public function choosePlan($pricing_id): void
|
||||
{
|
||||
$this->redirect(route('checkout', $pricing_id));
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.dashboard.pricing');
|
||||
}
|
||||
}
|
||||
23
app/Models/Plan.php
Normal file
23
app/Models/Plan.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Plan extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'product_id',
|
||||
'pricing_id',
|
||||
'price',
|
||||
'monthly_billing',
|
||||
'details'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'details' => 'json',
|
||||
'monthly_billing' => 'boolean',
|
||||
];
|
||||
}
|
||||
@@ -10,11 +10,12 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Cashier\Billable;
|
||||
|
||||
class User extends Authenticatable implements FilamentUser, MustVerifyEmail
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasFactory, Notifiable;
|
||||
use HasFactory, Notifiable, Billable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
|
||||
@@ -4,8 +4,10 @@ namespace App\Providers;
|
||||
|
||||
use App\Models\Blog;
|
||||
use App\Models\Menu;
|
||||
use App\Models\Plan;
|
||||
use DB;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Laravel\Cashier\Cashier;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -33,8 +35,15 @@ class AppServiceProvider extends ServiceProvider
|
||||
return Blog::where('is_published', 1)->get();
|
||||
});
|
||||
|
||||
$plans = cache()->remember('app_plans', now()->addHours(6), function () {
|
||||
return Plan::all();
|
||||
});
|
||||
|
||||
config(['app.settings' => (array) $settings]);
|
||||
config(['app.menus' => $menus]);
|
||||
config(['app.blogs' => $blogs]);
|
||||
config(['app.plans' => $plans]);
|
||||
|
||||
Cashier::calculateTaxes();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
$middleware->web(append: [
|
||||
\App\Http\Middleware\Locale::class,
|
||||
]);
|
||||
$middleware->validateCsrfTokens(except: [
|
||||
'stripe/*',
|
||||
]);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions) {
|
||||
//
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"ext-imap": "*",
|
||||
"ddeboer/imap": "^1.14",
|
||||
"filament/filament": "3.3",
|
||||
"laravel/cashier": "^15.6",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/tinker": "^2.10.1",
|
||||
"livewire/flux": "^2.1",
|
||||
"livewire/livewire": "^3.6",
|
||||
"ext-imap": "*"
|
||||
"livewire/livewire": "^3.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"barryvdh/laravel-debugbar": "^3.15",
|
||||
|
||||
323
composer.lock
generated
323
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "b4dcaf149eb8c0291c1de5ccd42c8f94",
|
||||
"content-hash": "f1d41505247807e937b78e4e92b1571e",
|
||||
"packages": [
|
||||
{
|
||||
"name": "anourvalar/eloquent-serialize",
|
||||
@@ -2100,6 +2100,94 @@
|
||||
},
|
||||
"time": "2025-04-01T14:41:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/cashier",
|
||||
"version": "v15.6.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/cashier-stripe.git",
|
||||
"reference": "8fe60cc71161ef06b6a1b23cffe886abf2a49b29"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/cashier-stripe/zipball/8fe60cc71161ef06b6a1b23cffe886abf2a49b29",
|
||||
"reference": "8fe60cc71161ef06b6a1b23cffe886abf2a49b29",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"illuminate/console": "^10.0|^11.0|^12.0",
|
||||
"illuminate/contracts": "^10.0|^11.0|^12.0",
|
||||
"illuminate/database": "^10.0|^11.0|^12.0",
|
||||
"illuminate/http": "^10.0|^11.0|^12.0",
|
||||
"illuminate/log": "^10.0|^11.0|^12.0",
|
||||
"illuminate/notifications": "^10.0|^11.0|^12.0",
|
||||
"illuminate/pagination": "^10.0|^11.0|^12.0",
|
||||
"illuminate/routing": "^10.0|^11.0|^12.0",
|
||||
"illuminate/support": "^10.0|^11.0|^12.0",
|
||||
"illuminate/view": "^10.0|^11.0|^12.0",
|
||||
"moneyphp/money": "^4.0",
|
||||
"nesbot/carbon": "^2.0|^3.0",
|
||||
"php": "^8.1",
|
||||
"stripe/stripe-php": "^16.2",
|
||||
"symfony/console": "^6.0|^7.0",
|
||||
"symfony/http-kernel": "^6.0|^7.0",
|
||||
"symfony/polyfill-intl-icu": "^1.22.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"dompdf/dompdf": "^2.0",
|
||||
"mockery/mockery": "^1.0",
|
||||
"orchestra/testbench": "^8.18|^9.0|^10.0",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpunit/phpunit": "^10.4|^11.5"
|
||||
},
|
||||
"suggest": {
|
||||
"dompdf/dompdf": "Required when generating and downloading invoice PDF's using Dompdf (^1.0.1|^2.0).",
|
||||
"ext-intl": "Allows for more locales besides the default \"en\" when formatting money values."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Cashier\\CashierServiceProvider"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "15.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Cashier\\": "src/",
|
||||
"Laravel\\Cashier\\Database\\Factories\\": "database/factories/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
},
|
||||
{
|
||||
"name": "Dries Vints",
|
||||
"email": "dries@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.",
|
||||
"keywords": [
|
||||
"billing",
|
||||
"laravel",
|
||||
"stripe"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/cashier/issues",
|
||||
"source": "https://github.com/laravel/cashier"
|
||||
},
|
||||
"time": "2025-04-22T13:59:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v12.10.2",
|
||||
@@ -3349,6 +3437,96 @@
|
||||
},
|
||||
"time": "2024-03-31T07:05:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "moneyphp/money",
|
||||
"version": "v4.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/moneyphp/money.git",
|
||||
"reference": "af048f0206d3b39b8fad9de6a230cedf765365fa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/moneyphp/money/zipball/af048f0206d3b39b8fad9de6a230cedf765365fa",
|
||||
"reference": "af048f0206d3b39b8fad9de6a230cedf765365fa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-bcmath": "*",
|
||||
"ext-filter": "*",
|
||||
"ext-json": "*",
|
||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cache/taggable-cache": "^1.1.0",
|
||||
"doctrine/coding-standard": "^12.0",
|
||||
"doctrine/instantiator": "^1.5.0 || ^2.0",
|
||||
"ext-gmp": "*",
|
||||
"ext-intl": "*",
|
||||
"florianv/exchanger": "^2.8.1",
|
||||
"florianv/swap": "^4.3.0",
|
||||
"moneyphp/crypto-currencies": "^1.1.0",
|
||||
"moneyphp/iso-currencies": "^3.4",
|
||||
"php-http/message": "^1.16.0",
|
||||
"php-http/mock-client": "^1.6.0",
|
||||
"phpbench/phpbench": "^1.2.5",
|
||||
"phpstan/extension-installer": "^1.4",
|
||||
"phpstan/phpstan": "^2.1.9",
|
||||
"phpstan/phpstan-phpunit": "^2.0",
|
||||
"phpunit/phpunit": "^10.5.9",
|
||||
"psr/cache": "^1.0.1 || ^2.0 || ^3.0",
|
||||
"ticketswap/phpstan-error-formatter": "^1.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gmp": "Calculate without integer limits",
|
||||
"ext-intl": "Format Money objects with intl",
|
||||
"florianv/exchanger": "Exchange rates library for PHP",
|
||||
"florianv/swap": "Exchange rates library for PHP",
|
||||
"psr/cache-implementation": "Used for Currency caching"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Money\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mathias Verraes",
|
||||
"email": "mathias@verraes.net",
|
||||
"homepage": "http://verraes.net"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Frederik Bosch",
|
||||
"email": "f.bosch@genkgo.nl"
|
||||
}
|
||||
],
|
||||
"description": "PHP implementation of Fowler's Money pattern",
|
||||
"homepage": "http://moneyphp.org",
|
||||
"keywords": [
|
||||
"Value Object",
|
||||
"money",
|
||||
"vo"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/moneyphp/money/issues",
|
||||
"source": "https://github.com/moneyphp/money/tree/v4.7.0"
|
||||
},
|
||||
"time": "2025-04-03T08:26:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "3.9.0",
|
||||
@@ -5028,6 +5206,65 @@
|
||||
],
|
||||
"time": "2025-04-11T15:27:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "stripe/stripe-php",
|
||||
"version": "v16.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/stripe/stripe-php.git",
|
||||
"reference": "d6de0a536f00b5c5c74f36b8f4d0d93b035499ff"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/d6de0a536f00b5c5c74f36b8f4d0d93b035499ff",
|
||||
"reference": "d6de0a536f00b5c5c74f36b8f4d0d93b035499ff",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=5.6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "3.5.0",
|
||||
"phpstan/phpstan": "^1.2",
|
||||
"phpunit/phpunit": "^5.7 || ^9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Stripe\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Stripe and contributors",
|
||||
"homepage": "https://github.com/stripe/stripe-php/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Stripe PHP Library",
|
||||
"homepage": "https://stripe.com/",
|
||||
"keywords": [
|
||||
"api",
|
||||
"payment processing",
|
||||
"stripe"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/stripe/stripe-php/issues",
|
||||
"source": "https://github.com/stripe/stripe-php/tree/v16.6.0"
|
||||
},
|
||||
"time": "2025-02-24T22:35:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/clock",
|
||||
"version": "v7.2.0",
|
||||
@@ -6204,6 +6441,90 @@
|
||||
],
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-icu",
|
||||
"version": "v1.32.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-icu.git",
|
||||
"reference": "763d2a91fea5681509ca01acbc1c5e450d127811"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/763d2a91fea5681509ca01acbc1c5e450d127811",
|
||||
"reference": "763d2a91fea5681509ca01acbc1c5e450d127811",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "For best performance and support of other locales than \"en\""
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Icu\\": ""
|
||||
},
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
],
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for intl's ICU-related data and classes",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"icu",
|
||||
"intl",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.32.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-21T18:38:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-idn",
|
||||
"version": "v1.31.0",
|
||||
|
||||
127
config/cashier.php
Normal file
127
config/cashier.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
use Laravel\Cashier\Console\WebhookCommand;
|
||||
use Laravel\Cashier\Invoices\DompdfInvoiceRenderer;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Stripe Keys
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The Stripe publishable key and secret key give you access to Stripe's
|
||||
| API. The "publishable" key is typically used when interacting with
|
||||
| Stripe.js while the "secret" key accesses private API endpoints.
|
||||
|
|
||||
*/
|
||||
|
||||
'key' => env('STRIPE_KEY'),
|
||||
|
||||
'secret' => env('STRIPE_SECRET'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cashier Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the base URI path where Cashier's views, such as the payment
|
||||
| verification screen, will be available from. You're free to tweak
|
||||
| this path according to your preferences and application design.
|
||||
|
|
||||
*/
|
||||
|
||||
'path' => env('CASHIER_PATH', 'stripe'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Stripe Webhooks
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Your Stripe webhook secret is used to prevent unauthorized requests to
|
||||
| your Stripe webhook handling controllers. The tolerance setting will
|
||||
| check the drift between the current time and the signed request's.
|
||||
|
|
||||
*/
|
||||
|
||||
'webhook' => [
|
||||
'secret' => env('STRIPE_WEBHOOK_SECRET'),
|
||||
'tolerance' => env('STRIPE_WEBHOOK_TOLERANCE', 300),
|
||||
'events' => WebhookCommand::DEFAULT_EVENTS,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Currency
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the default currency that will be used when generating charges
|
||||
| from your application. Of course, you are welcome to use any of the
|
||||
| various world currencies that are currently supported via Stripe.
|
||||
|
|
||||
*/
|
||||
|
||||
'currency' => env('CASHIER_CURRENCY', 'usd'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Currency Locale
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the default locale in which your money values are formatted in
|
||||
| for display. To utilize other locales besides the default en locale
|
||||
| verify you have the "intl" PHP extension installed on the system.
|
||||
|
|
||||
*/
|
||||
|
||||
'currency_locale' => env('CASHIER_CURRENCY_LOCALE', 'en'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Payment Confirmation Notification
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If this setting is enabled, Cashier will automatically notify customers
|
||||
| whose payments require additional verification. You should listen to
|
||||
| Stripe's webhooks in order for this feature to function correctly.
|
||||
|
|
||||
*/
|
||||
|
||||
'payment_notification' => env('CASHIER_PAYMENT_NOTIFICATION'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Invoice Settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following options determine how Cashier invoices are converted from
|
||||
| HTML into PDFs. You're free to change the options based on the needs
|
||||
| of your application or your preferences regarding invoice styling.
|
||||
|
|
||||
*/
|
||||
|
||||
'invoices' => [
|
||||
'renderer' => env('CASHIER_INVOICE_RENDERER', DompdfInvoiceRenderer::class),
|
||||
|
||||
'options' => [
|
||||
// Supported: 'letter', 'legal', 'A4'
|
||||
'paper' => env('CASHIER_PAPER', 'letter'),
|
||||
|
||||
'remote_enabled' => env('CASHIER_REMOTE_ENABLED', false),
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Stripe Logger
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This setting defines which logging channel will be used by the Stripe
|
||||
| library to write log messages. You are free to specify any of your
|
||||
| logging channels listed inside the "logging" configuration file.
|
||||
|
|
||||
*/
|
||||
|
||||
'logger' => env('CASHIER_LOGGER'),
|
||||
|
||||
];
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('stripe_id')->nullable()->index();
|
||||
$table->string('pm_type')->nullable();
|
||||
$table->string('pm_last_four', 4)->nullable();
|
||||
$table->timestamp('trial_ends_at')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropIndex([
|
||||
'stripe_id',
|
||||
]);
|
||||
|
||||
$table->dropColumn([
|
||||
'stripe_id',
|
||||
'pm_type',
|
||||
'pm_last_four',
|
||||
'trial_ends_at',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('subscriptions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id');
|
||||
$table->string('type');
|
||||
$table->string('stripe_id')->unique();
|
||||
$table->string('stripe_status');
|
||||
$table->string('stripe_price')->nullable();
|
||||
$table->integer('quantity')->nullable();
|
||||
$table->timestamp('trial_ends_at')->nullable();
|
||||
$table->timestamp('ends_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['user_id', 'stripe_status']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('subscriptions');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('subscription_items', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('subscription_id');
|
||||
$table->string('stripe_id')->unique();
|
||||
$table->string('stripe_product');
|
||||
$table->string('stripe_price');
|
||||
$table->integer('quantity')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['subscription_id', 'stripe_price']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('subscription_items');
|
||||
}
|
||||
};
|
||||
34
database/migrations/2025_05_02_215351_create_plans_table.php
Normal file
34
database/migrations/2025_05_02_215351_create_plans_table.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('plans', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->text('description')->nullable();
|
||||
$table->string('product_id')->collation('utf8_bin');
|
||||
$table->string('pricing_id')->collation('utf8_bin');
|
||||
$table->integer('price');
|
||||
$table->boolean('monthly_billing')->default(true);
|
||||
$table->longText('details')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('plans');
|
||||
}
|
||||
};
|
||||
@@ -8,6 +8,14 @@
|
||||
background-color: #f72a25;
|
||||
}
|
||||
|
||||
.app-primary-bg {
|
||||
background-color: #F14743;
|
||||
}
|
||||
|
||||
.app-primary {
|
||||
color: #F14743;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: white;
|
||||
background-color: #4361ee;
|
||||
|
||||
43
resources/views/flux/icon/circle-x.blade.php
Normal file
43
resources/views/flux/icon/circle-x.blade.php
Normal file
@@ -0,0 +1,43 @@
|
||||
{{-- Credit: Lucide (https://lucide.dev) --}}
|
||||
|
||||
@props([
|
||||
'variant' => 'outline',
|
||||
])
|
||||
|
||||
@php
|
||||
if ($variant === 'solid') {
|
||||
throw new \Exception('The "solid" variant is not supported in Lucide.');
|
||||
}
|
||||
|
||||
$classes = Flux::classes('shrink-0')
|
||||
->add(match($variant) {
|
||||
'outline' => '[:where(&)]:size-6',
|
||||
'solid' => '[:where(&)]:size-6',
|
||||
'mini' => '[:where(&)]:size-5',
|
||||
'micro' => '[:where(&)]:size-4',
|
||||
});
|
||||
|
||||
$strokeWidth = match ($variant) {
|
||||
'outline' => 2,
|
||||
'mini' => 2.25,
|
||||
'micro' => 2.5,
|
||||
};
|
||||
@endphp
|
||||
|
||||
<svg
|
||||
{{ $attributes->class($classes) }}
|
||||
data-flux-icon
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="{{ $strokeWidth }}"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
data-slot="icon"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="m15 9-6 6" />
|
||||
<path d="m9 9 6 6" />
|
||||
</svg>
|
||||
@@ -1,19 +1,11 @@
|
||||
@section('title'){{ __('Dashboard') }}@endsection
|
||||
<div class="flex h-full w-full flex-1 flex-col gap-4 rounded-xl">
|
||||
<div class="grid auto-rows-min gap-4 md:grid-cols-3">
|
||||
<div class="grid auto-rows-min gap-4 md:grid-cols-2">
|
||||
|
||||
|
||||
|
||||
<article class="flex items-center gap-4 rounded-lg border border-gray-100 bg-white p-6 dark:border-gray-800 dark:bg-white/[0.03]">
|
||||
<span class="rounded-full bg-blue-100 p-3 text-blue-600 dark:bg-blue-500/20 dark:text-blue-400">
|
||||
<flux:icon.circle-dollar-sign />
|
||||
</span>
|
||||
<div>
|
||||
<p class="text-2xl font-medium text-gray-900 dark:text-white">0</p>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Balance</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="flex items-center gap-4 rounded-lg border border-gray-100 bg-white p-6 dark:border-gray-800 dark:bg-white/[0.03]">
|
||||
<span class="rounded-full bg-blue-100 p-3 text-blue-600 dark:bg-blue-500/20 dark:text-blue-400">
|
||||
<span class="rounded-full bg-[#F04743]/20 p-3 text-[#F04743] dark:bg-[#F04743]/20 dark:text-[#F04743]">
|
||||
<flux:icon.at-sign />
|
||||
</span>
|
||||
<div>
|
||||
@@ -23,7 +15,7 @@
|
||||
</article>
|
||||
|
||||
<article class="flex items-center gap-4 rounded-lg border border-gray-100 bg-white p-6 dark:border-gray-800 dark:bg-white/[0.03]">
|
||||
<span class="rounded-full bg-blue-100 p-3 text-blue-600 dark:bg-blue-500/20 dark:text-blue-400">
|
||||
<span class="rounded-full bg-[#F04743]/20 p-3 text-[#F04743] dark:bg-[#F04743]/20 dark:text-[#F04743]">
|
||||
<flux:icon.mails />
|
||||
</span>
|
||||
<div>
|
||||
@@ -34,7 +26,35 @@
|
||||
|
||||
|
||||
</div>
|
||||
<div class="relative h-full flex-1 overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-700">
|
||||
<x-placeholder-pattern class="absolute inset-0 size-full stroke-gray-900/20 dark:stroke-neutral-100/20" />
|
||||
@if(auth()->user()->subscribedToProduct(config('app.plans')[0]['product_id']))
|
||||
|
||||
<article class="flex items-center gap-4 rounded-lg border border-gray-100 bg-white p-6 dark:border-gray-800 dark:bg-white/[0.03]">
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Your <span class="text-accent-content font-bold">{{ $subscription['name'] }}</span> subscription is active and will be
|
||||
@if($subscription['ends_at']) end at<span class="app-primary"> {{ \Carbon\Carbon::make($subscription['ends_at'])->toFormattedDayDateString() }}.</span>
|
||||
@else auto-renew as per the plan you chose.
|
||||
@endif
|
||||
To manage you subscription <a href="{{ route('billing') }}" target="_blank" class="text-blue-500">Click here</a></p>
|
||||
</div>
|
||||
</article>
|
||||
@else
|
||||
<div class="flex-1 overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-700 dark:bg-white/[0.03]">
|
||||
<livewire:dashboard.pricing />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Check if session flash data exists
|
||||
@if(session()->has('alert'))
|
||||
setTimeout(function() {
|
||||
// Emitting showAlert event with type and message from session
|
||||
Livewire.emit('showAlert', {
|
||||
type: '{{ session('alert')['type'] }}',
|
||||
message: '{{ session('alert')['message'] }}'
|
||||
});
|
||||
}, 2000); // 2000ms = 2 seconds delay
|
||||
@endif
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
46
resources/views/livewire/dashboard/pricing.blade.php
Normal file
46
resources/views/livewire/dashboard/pricing.blade.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<div class="mx-auto max-w-3xl px-4 py-8 sm:px-6 sm:py-12 lg:px-8 ">
|
||||
<div class="w-full mb-8 items-center flex justify-center">
|
||||
<h1 class="text-center text-3xl text-gray-900 dark:text-gray-200">Purchase Subscription</h1>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 sm:items-center md:gap-8">
|
||||
|
||||
@if(isset($plans))
|
||||
@foreach($plans as $plan)
|
||||
<div class="rounded-2xl border dark:border-white/[0.1] border-black/[0.3] p-6 shadow-xs ring-1 ring-white/[0.5] sm:px-8 lg:p-12">
|
||||
<div class="text-center">
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-400">{{ $plan->name }} @if(!$plan->monthly_billing)
|
||||
<flux:badge variant="solid" size="sm" color="emerald">2 Months Free</flux:badge>
|
||||
@endif</h2>
|
||||
|
||||
<p class="mt-2 sm:mt-4">
|
||||
<strong class="text-3xl font-bold text-gray-900 dark:text-gray-200 sm:text-4xl">${{ $plan->price }}</strong>
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-400">/{{ $plan->monthly_billing ? 'month' : 'year' }}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul class="mt-6 space-y-2">
|
||||
|
||||
@if($plan->details)
|
||||
@forelse ($plan->details as $key => $value)
|
||||
@if ($value)
|
||||
<li class="flex items-center gap-1">
|
||||
@if($value == "true")<flux:icon.check-circle />
|
||||
@else <flux:icon.circle-x />
|
||||
@endif
|
||||
<span class="text-gray-700 dark:text-gray-400 "> {{ $key }} </span>
|
||||
</li>
|
||||
@endif
|
||||
@empty
|
||||
@endforelse
|
||||
@endif
|
||||
</ul>
|
||||
|
||||
<flux:button variant="primary" class="w-full mt-6 cursor-pointer" wire:click="choosePlan('{{ $plan->pricing_id }}')">
|
||||
Choose Plan
|
||||
</flux:button>
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -47,6 +47,33 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
||||
Route::get('dashboard/bulk-email-generator', Dashboard::class)->name('dashboard.bulk');
|
||||
Route::get('dashboard/compose-email', Dashboard::class)->name('dashboard.compose');
|
||||
|
||||
// Checkout Routes
|
||||
Route::get('checkout/{plan}', function ($pricing_id) {
|
||||
$plans = config('app.plans');
|
||||
$pricingData = [];
|
||||
foreach ($plans as $plan) {
|
||||
$pricingData[] = $plan['pricing_id'];
|
||||
}
|
||||
|
||||
if (in_array($pricing_id, $pricingData)) {
|
||||
return auth()->user()
|
||||
->newSubscription('default', $pricing_id)
|
||||
->allowPromotionCodes()
|
||||
->checkout([
|
||||
'success_url' => route('checkout.success'),
|
||||
'cancel_url' => route('checkout.cancel'),
|
||||
]);
|
||||
|
||||
}
|
||||
abort(404);
|
||||
})->name('checkout');
|
||||
|
||||
Route::get('dashboard/success', [Dashboard::class, 'paymentStatus'])->name('checkout.success')->defaults('status', 'success');
|
||||
Route::get('dashboard/cancel', [Dashboard::class, 'paymentStatus'])->name('checkout.cancel')->defaults('status', 'cancel');
|
||||
|
||||
Route::get('dashboard/billing', function () {
|
||||
return auth()->user()->redirectToBillingPortal(route('dashboard'));
|
||||
})->name('billing');
|
||||
});
|
||||
|
||||
Route::middleware(['auth'])->group(function () {
|
||||
|
||||
Reference in New Issue
Block a user