From e3941a6ef82fb4a734599dc6602c4a9b7d5ee5de Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 13:02:14 +0000 Subject: [PATCH] Fix Cashier Paddle v2 subscription workflow and dashboard permissions - Updated the dashboard route to allow access for subscribed users. - Added explicit Paddle initialization in the pricing page for Cashier Paddle v2 compatibility. - Added validation for Paddle price IDs in the SubscriptionController. - Added feature tests for dashboard access and checkout validation. Co-authored-by: claudemyburgh <6057076+claudemyburgh@users.noreply.github.com> --- .../Controllers/SubscriptionController.php | 4 +-- resources/js/pages/pricing.tsx | 15 ++++++++++- routes/web.php | 11 ++++++-- tests/Feature/DashboardTest.php | 21 +++++++++++++++ tests/Feature/SubscriptionTest.php | 27 +++++++++++++++++++ 5 files changed, 73 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/SubscriptionController.php b/app/Http/Controllers/SubscriptionController.php index 51ba417..450842b 100644 --- a/app/Http/Controllers/SubscriptionController.php +++ b/app/Http/Controllers/SubscriptionController.php @@ -21,8 +21,8 @@ public function checkout(Request $request) { $priceId = $request->input('price_id'); - if (! $priceId) { - return back()->with('error', 'Please select a plan.'); + if (! $priceId || ! str_starts_with($priceId, 'pri_')) { + return back()->with('error', 'Please select a valid plan.'); } $checkout = $request->user()->checkout($priceId) diff --git a/resources/js/pages/pricing.tsx b/resources/js/pages/pricing.tsx index 86ef5b4..5b7856a 100644 --- a/resources/js/pages/pricing.tsx +++ b/resources/js/pages/pricing.tsx @@ -26,7 +26,20 @@ export default function Pricing() { const { auth, checkout: checkoutData } = usePage<{ auth: Auth; checkout?: Record }>().props; useEffect(() => { - if (checkoutData) { + if (window.Paddle) { + window.Paddle.Initialize({ + token: import.meta.env.VITE_PADDLE_CLIENT_SIDE_TOKEN, + eventCallback: (event: any) => { + if (event.name === 'checkout.closed') { + router.reload({ only: ['auth', 'checkout'] }); + } + }, + }); + } + }, []); + + useEffect(() => { + if (checkoutData && window.Paddle) { window.Paddle.Checkout.open(checkoutData); } }, [checkoutData]); diff --git a/routes/web.php b/routes/web.php index e69d6b3..32df73b 100644 --- a/routes/web.php +++ b/routes/web.php @@ -8,6 +8,7 @@ use App\Http\Controllers\RegistryController; use App\Http\Controllers\SubscriptionController; use App\Http\Controllers\ThemesController; +use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; Route::get('/', HomePageController::class)->name('home'); @@ -32,8 +33,14 @@ Route::post('/r/upload', [RegistryController::class, 'upload']); Route::post('/r/upload-raw', [RegistryController::class, 'uploadRaw']); -Route::middleware(['auth', 'verified', 'role:super-admin|admin'])->group(function () { - Route::inertia('dashboard', 'dashboard')->name('dashboard'); +Route::middleware(['auth', 'verified'])->group(function () { + Route::get('dashboard', function (Request $request) { + if ($request->user()->hasAnyRole(['super-admin', 'admin']) || $request->user()->subscribed()) { + return Inertia\Inertia::render('dashboard'); + } + + abort(403); + })->name('dashboard'); }); require __DIR__.'/settings.php'; diff --git a/tests/Feature/DashboardTest.php b/tests/Feature/DashboardTest.php index 8a168cc..24a52bb 100644 --- a/tests/Feature/DashboardTest.php +++ b/tests/Feature/DashboardTest.php @@ -41,3 +41,24 @@ $response = $this->get(route('dashboard')); $response->assertForbidden(); }); + +test('authenticated subscribed users can visit the dashboard', function () { + $this->seed(RolesAndPermissionsSeeder::class); + $user = User::factory()->create(); + $user->assignRole('guest'); + + // Mock the subscribed method + $user->subscriptions()->create([ + 'type' => 'default', + 'paddle_id' => 'sub_123', + 'status' => 'active', + 'trial_ends_at' => null, + 'paused_at' => null, + 'ends_at' => null, + ]); + + $this->actingAs($user); + + $response = $this->get(route('dashboard')); + $response->assertOk(); +}); diff --git a/tests/Feature/SubscriptionTest.php b/tests/Feature/SubscriptionTest.php index a867d6a..a958309 100644 --- a/tests/Feature/SubscriptionTest.php +++ b/tests/Feature/SubscriptionTest.php @@ -32,4 +32,31 @@ public function test_subscription_edit_page_is_accessible_to_authenticated_user( $response->assertStatus(200); } + + public function test_checkout_requires_valid_price_id() + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->post('/settings/subscription/checkout', [ + 'price_id' => 'invalid_id', + ]); + + $response->assertSessionHas('error', 'Please select a valid plan.'); + } + + public function test_checkout_returns_paddle_options() + { + $this->markTestSkipped('Paddle API interaction requires valid API keys or more extensive mocking.'); + + config(['cashier.api_key' => 'test_api_key']); + config(['cashier.seller_id' => 'test_seller_id']); + + $user = User::factory()->create(); + + $response = $this->actingAs($user)->post('/settings/subscription/checkout', [ + 'price_id' => 'pri_123', + ]); + + $response->assertSessionHas('checkout'); + } }