Created
March 3, 2025 14:23
-
-
Save larson-carter/40009cc9004c1224c159605c3af01129 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Http\Controllers; | |
use Illuminate\Http\Request; | |
use Illuminate\Support\Facades\Auth; | |
use App\Models\CoverPass; | |
use App\Models\CoverManagement; | |
use App\Models\SubscriptionOption; | |
use Carbon\Carbon; | |
use Illuminate\Support\Facades\Log; | |
use Illuminate\Support\Str; // For Str::uuid() | |
class PaymentController extends Controller | |
{ | |
public function subscriptionStatus(Request $request) | |
{ | |
$user = Auth::user(); | |
$subscription = $user->subscription('default'); | |
$options = SubscriptionOption::where('is_active', true)->get(); | |
$subscriptionData = null; | |
if ($subscription && $subscription->stripe_status === 'active') { | |
$option = SubscriptionOption::where('stripe_price_id', $subscription->stripe_price)->first(); | |
$subscriptionData = [ | |
'id' => (string) $subscription->id, | |
'type' => $option ? $option->name : $subscription->stripe_price, | |
'user_id' => $user->id, | |
'duration' => $option ? $option->duration : 'unknown', | |
'ends_at' => $subscription->ends_at?->toIso8601String(), | |
'stripe_status' => $subscription->stripe_status, | |
'auto_renew' => !$subscription->canceled(), | |
]; | |
} | |
return response()->json([ | |
'has_subscription' => $user->hasActiveSubscription(), | |
'subscription' => $subscriptionData, | |
'options' => $options->map(function ($option) { | |
return [ | |
'id' => $option->id, | |
'name' => $option->name, | |
'price' => $option->price, | |
'stripe_price' => $option->stripe_price_id, | |
'duration' => $option->duration, | |
'description' => $option->description, | |
'features' => $option->features, | |
]; | |
}), | |
], 200); | |
} | |
public function processSubscriptionPayment(Request $request) | |
{ | |
$request->validate([ | |
'stripe_price' => 'required|string|exists:subscription_options,stripe_price_id', | |
'payment_method' => 'required|string', | |
]); | |
$user = Auth::user(); | |
$stripePrice = $request->stripe_price; | |
if ($user->hasActiveSubscription()) { | |
return response()->json(['error' => 'User already has an active subscription'], 400); | |
} | |
$option = SubscriptionOption::where('stripe_price_id', $stripePrice)->firstOrFail(); | |
try { | |
$user->addPaymentMethod($request->payment_method); | |
$isRecurring = $option->duration !== 'lifetime'; | |
if ($isRecurring) { | |
// For recurring subscriptions: | |
$subscription = $user->newSubscription('default', $stripePrice) | |
->create($request->payment_method, [ | |
'transfer_data' => ['destination' => config('services.stripe.connect_account_id')], | |
'application_fee_percent' => 5, | |
]); | |
\Stripe\Stripe::setApiKey(config('services.stripe.secret')); | |
$stripeSubscription = \Stripe\Subscription::retrieve($subscription->stripe_id); | |
$endsAt = Carbon::createFromTimestamp($stripeSubscription->current_period_end); | |
$subscription->ends_at = $endsAt; | |
$subscription->save(); | |
return response()->json([ | |
'has_subscription' => true, | |
'subscription' => [ | |
'id' => (string) $subscription->id, | |
'type' => $option->name, | |
'user_id' => $user->id, | |
'duration' => $option->duration, | |
'ends_at' => $endsAt->toIso8601String(), | |
'stripe_status' => $subscription->stripe_status, | |
'auto_renew' => true, | |
], | |
], 201); | |
} else { | |
// One-time "lifetime" subscription: | |
$charge = $user->charge( | |
$option->price * 100, | |
$request->payment_method, | |
[ | |
'transfer_data' => ['destination' => config('services.stripe.connect_account_id')], | |
'application_fee_amount' => ($option->price * 100) * 0.05, | |
// Only allow card; no redirect-based payment methods | |
'payment_method_types' => ['card'], | |
] | |
); | |
return response()->json([ | |
'has_subscription' => true, | |
'subscription' => [ | |
'id' => $charge->id, | |
'type' => $option->name, | |
'user_id' => $user->id, | |
'duration' => $option->duration, | |
'ends_at' => null, | |
'stripe_status' => 'active', | |
'auto_renew' => false, | |
], | |
], 201); | |
} | |
} catch (\Exception $e) { | |
Log::error('Subscription payment failed: ' . $e->getMessage()); | |
return response()->json(['error' => 'Payment failed: ' . $e->getMessage()], 500); | |
} | |
} | |
public function processCoverPayment(Request $request) | |
{ | |
$request->validate([ | |
'payment_method' => 'required|string', | |
]); | |
$user = Auth::user(); | |
$coverManagement = CoverManagement::firstOrFail(); | |
if (!$coverManagement->charging_cover) { | |
return response()->json(['message' => 'Cover charge is not currently active.'], 400); | |
} | |
try { | |
$user->addPaymentMethod($request->payment_method); | |
// Charge for the cover pass, allowing only card payments | |
$charge = $user->charge( | |
$coverManagement->cover_cost * 100, | |
$request->payment_method, | |
[ | |
'transfer_data' => ['destination' => config('services.stripe.connect_account_id')], | |
'application_fee_amount' => ($coverManagement->cover_cost * 100) * 0.05, | |
'payment_method_types' => ['card'], // disable redirect-based methods | |
] | |
); | |
$coverPass = CoverPass::create([ | |
'id' => Str::uuid(), | |
'user_id' => $user->id, | |
'amount_paid' => $coverManagement->cover_cost, | |
'purchased_at' => Carbon::now(), | |
'expires_at' => Carbon::now()->addHours(6), | |
'is_used' => false, | |
'stripe_charge_id' => $charge->id, | |
]); | |
return response()->json([ | |
'message' => 'Cover pass purchased successfully', | |
'cover_pass' => [ | |
'id' => $coverPass->id, | |
'user_id' => $coverPass->user_id, | |
'purchased_at' => $coverPass->purchased_at->toIso8601String(), | |
'expires_at' => $coverPass->expires_at->toIso8601String(), | |
'amount_paid' => (string) $coverPass->amount_paid, | |
], | |
], 201); | |
} catch (\Exception $e) { | |
Log::error('Cover payment failed: ' . $e->getMessage()); | |
return response()->json(['error' => 'Payment failed: ' . $e->getMessage()], 500); | |
} | |
} | |
public function toggleAutoRenew(Request $request) | |
{ | |
$user = Auth::user(); | |
$subscription = $user->subscription('default'); | |
if (!$subscription) { | |
return response()->json(['error' => 'No active subscription found'], 400); | |
} | |
try { | |
if ($subscription->canceled()) { | |
$subscription->resume(); | |
} else { | |
$subscription->cancel(); | |
} | |
return response()->json([ | |
'message' => 'Auto-renew status updated', | |
'auto_renew' => !$subscription->canceled(), | |
]); | |
} catch (\Exception $e) { | |
Log::error('Toggle auto-renew failed: ' . $e->getMessage()); | |
return response()->json(['error' => 'Failed to update auto-renew: ' . $e->getMessage()], 500); | |
} | |
} | |
public function createSetupIntent(Request $request) | |
{ | |
$user = Auth::user(); | |
$setupIntent = $user->createSetupIntent(); | |
return response()->json([ | |
'client_secret' => $setupIntent->client_secret, | |
'publishable_key' => config('services.stripe.key'), | |
]); | |
} | |
public function getSetupIntentPaymentMethod(Request $request, $setupIntentId) | |
{ | |
$user = Auth::user(); | |
\Stripe\Stripe::setApiKey(config('services.stripe.secret')); | |
try { | |
$setupIntent = \Stripe\SetupIntent::retrieve($setupIntentId); | |
if ($setupIntent->customer !== $user->stripe_id) { | |
return response()->json(['error' => 'Unauthorized'], 403); | |
} | |
if ($setupIntent->status !== 'succeeded' || !$setupIntent->payment_method) { | |
return response()->json(['error' => 'SetupIntent not confirmed or no payment method attached'], 400); | |
} | |
return response()->json([ | |
'payment_method' => $setupIntent->payment_method, | |
]); | |
} catch (\Stripe\Exception\AuthenticationException $e) { | |
Log::error('Stripe Authentication Error: ' . $e->getMessage()); | |
return response()->json(['error' => 'Authentication failed with Stripe'], 500); | |
} catch (\Exception $e) { | |
Log::error('Error retrieving SetupIntent: ' . $e->getMessage()); | |
return response()->json(['error' => 'Failed to retrieve payment method'], 500); | |
} | |
} | |
public function coverInfo(Request $request) | |
{ | |
$user = Auth::user(); | |
$subscription = $user->subscription('default'); | |
$coverManagement = CoverManagement::first(); | |
$hasMonthlySubscription = false; | |
$activeCoverPass = null; | |
$coverCost = $coverManagement ? (float) $coverManagement->cover_cost : 0.0; | |
$isChargingCover = $coverManagement ? $coverManagement->charging_cover : false; | |
if ($subscription) { | |
$now = Carbon::now(); | |
$endsAt = Carbon::parse($subscription->ends_at); | |
if ($now->lt($endsAt)) { | |
$option = SubscriptionOption::where('stripe_price_id', $subscription->stripe_price)->first(); | |
if ($option && $option->duration === 'monthly') { | |
$hasMonthlySubscription = true; | |
} | |
} | |
} | |
if ($hasMonthlySubscription && $isChargingCover) { | |
$activeCoverPass = CoverPass::where('user_id', $user->id) | |
->where('expires_at', '>', Carbon::now()) | |
->where('is_used', false) | |
->latest('purchased_at') | |
->first(); | |
} | |
return response()->json([ | |
'has_monthly_subscription' => $hasMonthlySubscription, | |
'is_charging_cover' => $isChargingCover, | |
'cover_cost' => $coverCost, | |
'active_cover_pass' => $activeCoverPass ? [ | |
'id' => $activeCoverPass->id, | |
'user_id' => $activeCoverPass->user_id, | |
'purchased_at' => $activeCoverPass->purchased_at->toIso8601String(), | |
'expires_at' => $activeCoverPass->expires_at->toIso8601String(), | |
'amount_paid' => (string) $activeCoverPass->amount_paid, | |
] : null, | |
], 200); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment