chore: add payment routes for our UPS
This commit is contained in:
@@ -254,6 +254,105 @@ class PaymentController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle enhanced checkout with provider selection
|
||||||
|
*/
|
||||||
|
public function enhancedCheckout(Request $request, int $plan, string $provider)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$user = $request->user();
|
||||||
|
$planModel = Plan::with(['planProviders', 'trialConfiguration'])->findOrFail($plan);
|
||||||
|
|
||||||
|
// Validate provider support
|
||||||
|
if (! $planModel->supportsProvider($provider)) {
|
||||||
|
session()->flash('error', "Provider '{$provider}' is not supported for this plan.");
|
||||||
|
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create checkout session via orchestrator
|
||||||
|
$result = $this->orchestrator->createCheckoutSession($user, $planModel, $provider, [
|
||||||
|
'success_url' => route('checkout.success'),
|
||||||
|
'cancel_url' => route('checkout.cancel'),
|
||||||
|
'is_trial' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Redirect to provider's checkout page
|
||||||
|
if (isset($result['redirect_url'])) {
|
||||||
|
return redirect($result['redirect_url']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to session-based checkout
|
||||||
|
if (isset($result['session_url'])) {
|
||||||
|
return redirect($result['session_url']);
|
||||||
|
}
|
||||||
|
|
||||||
|
session()->flash('error', 'Unable to create checkout session. Please try again.');
|
||||||
|
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
session()->flash('error', 'Checkout error: '.$e->getMessage());
|
||||||
|
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle trial-specific checkout flow
|
||||||
|
*/
|
||||||
|
public function trialCheckout(Request $request, int $plan, string $provider)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$user = $request->user();
|
||||||
|
$planModel = Plan::with(['trialConfiguration', 'planProviders'])->findOrFail($plan);
|
||||||
|
|
||||||
|
// Validate trial availability
|
||||||
|
if (! $planModel->hasTrial()) {
|
||||||
|
session()->flash('error', 'This plan does not offer trials.');
|
||||||
|
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate provider support
|
||||||
|
if (! $planModel->supportsProvider($provider)) {
|
||||||
|
session()->flash('error', "Provider '{$provider}' is not supported for this plan.");
|
||||||
|
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
$trialConfig = $planModel->getTrialConfig();
|
||||||
|
|
||||||
|
// Create trial checkout session
|
||||||
|
$result = $this->orchestrator->createCheckoutSession($user, $planModel, $provider, [
|
||||||
|
'success_url' => route('checkout.success'),
|
||||||
|
'cancel_url' => route('checkout.cancel'),
|
||||||
|
'is_trial' => true,
|
||||||
|
'trial_duration_days' => $trialConfig?->trial_duration_days ?? 14,
|
||||||
|
'trial_requires_payment_method' => $trialConfig?->trial_requires_payment_method ?? true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Redirect to provider's checkout page
|
||||||
|
if (isset($result['redirect_url'])) {
|
||||||
|
return redirect($result['redirect_url']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to session-based checkout
|
||||||
|
if (isset($result['session_url'])) {
|
||||||
|
return redirect($result['session_url']);
|
||||||
|
}
|
||||||
|
|
||||||
|
session()->flash('error', 'Unable to create trial checkout session. Please try again.');
|
||||||
|
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
session()->flash('error', 'Trial checkout error: '.$e->getMessage());
|
||||||
|
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle successful payment redirect
|
* Handle successful payment redirect
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -15,13 +15,19 @@ Route::prefix('payment')->name('payment.')->group(function () {
|
|||||||
Route::get('/success', [PaymentController::class, 'success'])->name('success');
|
Route::get('/success', [PaymentController::class, 'success'])->name('success');
|
||||||
Route::get('/cancel', [PaymentController::class, 'cancel'])->name('cancel');
|
Route::get('/cancel', [PaymentController::class, 'cancel'])->name('cancel');
|
||||||
|
|
||||||
// Payment processing endpoints
|
// UNIFIED: Payment processing endpoints (new unified payment system)
|
||||||
Route::post('/checkout', [PaymentController::class, 'createCheckout'])->name('checkout');
|
Route::post('/checkout', [PaymentController::class, 'createCheckout'])->name('checkout');
|
||||||
Route::post('/subscribe', [PaymentController::class, 'createSubscription'])->name('subscribe');
|
Route::post('/subscribe', [PaymentController::class, 'createSubscription'])->name('subscribe');
|
||||||
Route::get('/methods', [PaymentController::class, 'getPaymentMethods'])->name('methods');
|
Route::get('/methods', [PaymentController::class, 'getPaymentMethods'])->name('methods');
|
||||||
Route::get('/history', [PaymentController::class, 'getHistory'])->name('history');
|
Route::get('/history', [PaymentController::class, 'getHistory'])->name('history');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// UNIFIED: Enhanced checkout routes with provider selection
|
||||||
|
Route::middleware(['auth', 'verified'])->prefix('checkout')->name('checkout.')->group(function () {
|
||||||
|
Route::get('/enhanced/{plan}/{provider}', [PaymentController::class, 'enhancedCheckout'])->name('enhanced');
|
||||||
|
Route::get('/trial/{plan}/{provider}', [PaymentController::class, 'trialCheckout'])->name('trial');
|
||||||
|
});
|
||||||
|
|
||||||
Route::prefix('webhook')->name('webhook.')->group(function () {
|
Route::prefix('webhook')->name('webhook.')->group(function () {
|
||||||
// Unified webhook handler
|
// Unified webhook handler
|
||||||
Route::post('/{provider}', [WebhookController::class, 'handle'])->name('unified');
|
Route::post('/{provider}', [WebhookController::class, 'handle'])->name('unified');
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ Route::middleware(['auth', 'verified', CheckUserBanned::class])->group(function
|
|||||||
Route::get('dashboard/compose-email', Dashboard::class)->name('dashboard.compose');
|
Route::get('dashboard/compose-email', Dashboard::class)->name('dashboard.compose');
|
||||||
Route::get('dashboard/support', Support::class)->name('dashboard.support');
|
Route::get('dashboard/support', Support::class)->name('dashboard.support');
|
||||||
|
|
||||||
// Checkout Routes
|
// LEGACY: Old Stripe Cashier checkout route (deprecated - use unified payment system)
|
||||||
Route::get('checkout/{plan}', function ($pricing_id) {
|
Route::get('checkout/{plan}', function ($pricing_id) {
|
||||||
$plans = config('app.plans');
|
$plans = config('app.plans');
|
||||||
$pricingData = [];
|
$pricingData = [];
|
||||||
@@ -91,6 +91,7 @@ Route::middleware(['auth', 'verified', CheckUserBanned::class])->group(function
|
|||||||
abort(404);
|
abort(404);
|
||||||
})->name('checkout');
|
})->name('checkout');
|
||||||
|
|
||||||
|
// LEGACY: Payment status routes (used by both legacy and unified systems)
|
||||||
Route::get('dashboard/success', [Dashboard::class, 'paymentStatus'])->name('checkout.success')->defaults('status', 'success');
|
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/cancel', [Dashboard::class, 'paymentStatus'])->name('checkout.cancel')->defaults('status', 'cancel');
|
||||||
|
|
||||||
|
|||||||
121
tests/Feature/PricingCheckoutTest.php
Normal file
121
tests/Feature/PricingCheckoutTest.php
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\Plan;
|
||||||
|
use App\Models\User;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class PricingCheckoutTest extends TestCase
|
||||||
|
{
|
||||||
|
/** @test */
|
||||||
|
public function it_renders_pricing_component_with_working_checkout_buttons()
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$plan = Plan::factory()->create([
|
||||||
|
'name' => 'Test Plan',
|
||||||
|
'price' => 99.99,
|
||||||
|
'monthly_billing' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Enable polar provider for this plan
|
||||||
|
$plan->planProviders()->create([
|
||||||
|
'provider' => 'polar',
|
||||||
|
'is_enabled' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Livewire::actingAs($user)
|
||||||
|
->test(\App\Livewire\Dashboard\Pricing::class)
|
||||||
|
->assertSet('plans')
|
||||||
|
->assertSet('planTiers')
|
||||||
|
->assertSee($plan->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_can_redirect_to_enhanced_checkout_for_polar_provider()
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$plan = Plan::factory()->create([
|
||||||
|
'name' => 'Test Plan',
|
||||||
|
'price' => 99.99,
|
||||||
|
'monthly_billing' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Enable polar provider for this plan
|
||||||
|
$plan->planProviders()->create([
|
||||||
|
'provider' => 'polar',
|
||||||
|
'is_enabled' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Livewire::actingAs($user)
|
||||||
|
->test(\App\Livewire\Dashboard\Pricing::class)
|
||||||
|
->call('choosePlan', $plan->id, 'polar')
|
||||||
|
->assertRedirect(route('checkout.enhanced', [
|
||||||
|
'plan' => $plan->id,
|
||||||
|
'provider' => 'polar',
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_can_redirect_to_trial_checkout_when_plan_supports_trials()
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$plan = Plan::factory()->create([
|
||||||
|
'name' => 'Test Plan with Trial',
|
||||||
|
'price' => 99.99,
|
||||||
|
'monthly_billing' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Enable polar provider for this plan
|
||||||
|
$plan->planProviders()->create([
|
||||||
|
'provider' => 'polar',
|
||||||
|
'is_enabled' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create trial configuration
|
||||||
|
$plan->trialConfiguration()->create([
|
||||||
|
'trial_duration_days' => 14,
|
||||||
|
'trial_requires_payment_method' => true,
|
||||||
|
'trial_auto_converts' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Livewire::actingAs($user)
|
||||||
|
->test(\App\Livewire\Dashboard\Pricing::class)
|
||||||
|
->call('startTrial', $plan->id, 'polar')
|
||||||
|
->assertRedirect(route('checkout.trial', [
|
||||||
|
'plan' => $plan->id,
|
||||||
|
'provider' => 'polar',
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_shows_error_when_provider_is_not_supported()
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$plan = Plan::factory()->create();
|
||||||
|
|
||||||
|
Livewire::actingAs($user)
|
||||||
|
->test(\App\Livewire\Dashboard\Pricing::class)
|
||||||
|
->call('choosePlan', $plan->id, 'unsupported_provider')
|
||||||
|
->assertSessionHas('error', "This plan doesn't support unsupported_provider payments.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_shows_error_when_trying_trial_on_plan_without_trial()
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$plan = Plan::factory()->create();
|
||||||
|
|
||||||
|
// Enable provider but no trial configuration
|
||||||
|
$plan->planProviders()->create([
|
||||||
|
'provider' => 'polar',
|
||||||
|
'is_enabled' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Livewire::actingAs($user)
|
||||||
|
->test(\App\Livewire\Dashboard\Pricing::class)
|
||||||
|
->call('startTrial', $plan->id, 'polar')
|
||||||
|
->assertSessionHas('error', "This plan doesn't offer trials.");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user