getContent(); $sigHeader = $request->header('Stripe-Signature'); $endpointSecret = config('services.stripe.webhook_secret'); try { $event = Webhook::constructEvent($payload, $sigHeader, $endpointSecret); } catch (\Exception $e) { Log::error('Stripe webhook signature verification failed: ' . $e->getMessage()); return response()->json(['error' => 'Invalid signature'], 400); } switch ($event['type']) { case 'payment_intent.succeeded': $paymentIntent = $event['data']['object']; $this->handlePaymentIntentSucceeded($paymentIntent); break; case 'invoice.payment_succeeded': $invoice = $event['data']['object']; $this->handleInvoicePaymentSucceeded($invoice); break; case 'invoice.payment_failed': $invoice = $event['data']['object']; $this->handleInvoicePaymentFailed($invoice); break; default: Log::info('Unhandled Stripe event type: ' . $event['type']); } return response()->json(['status' => 'success']); } private function handlePaymentIntentSucceeded($paymentIntent) { $metadata = $paymentIntent['metadata']; if (!$metadata['user_id'] && !$metadata['tenant_id'] || !$metadata['package_id']) { Log::warning('Missing metadata in Stripe payment intent: ' . $paymentIntent['id']); return; } $userId = $metadata['user_id'] ?? null; $tenantId = $metadata['tenant_id'] ?? null; $packageId = $metadata['package_id']; $type = $metadata['type'] ?? 'endcustomer_event'; if ($userId && !$tenantId) { $tenant = \App\Models\Tenant::where('user_id', $userId)->first(); if ($tenant) { $tenantId = $tenant->id; } else { Log::error('Tenant not found for user_id: ' . $userId); return; } } if (!$tenantId) { Log::error('No tenant_id found for Stripe payment intent: ' . $paymentIntent['id']); return; } // Create PackagePurchase for one-off payment \App\Models\PackagePurchase::create([ 'tenant_id' => $tenantId, 'package_id' => $packageId, 'provider_id' => $paymentIntent['id'], 'price' => $paymentIntent['amount_received'] / 100, 'type' => $type, 'purchased_at' => now(), 'refunded' => false, ]); if ($type === 'endcustomer_event') { // For event packages, assume event_id from metadata or handle separately // TODO: Link to specific event if provided } Log::info('Package purchase created via Stripe payment intent: ' . $paymentIntent['id'] . ' for tenant ' . $tenantId); } private function handleInvoicePaymentSucceeded($invoice) { $subscription = $invoice['subscription']; $metadata = $invoice['metadata']; if (!$metadata['user_id'] && !$metadata['tenant_id'] || !$metadata['package_id']) { Log::warning('Missing metadata in Stripe invoice: ' . $invoice['id']); return; } $userId = $metadata['user_id'] ?? null; $tenantId = $metadata['tenant_id'] ?? null; $packageId = $metadata['package_id']; if ($userId && !$tenantId) { $tenant = \App\Models\Tenant::where('user_id', $userId)->first(); if ($tenant) { $tenantId = $tenant->id; } else { Log::error('Tenant not found for user_id: ' . $userId); return; } } if (!$tenantId) { Log::error('No tenant_id found for Stripe invoice: ' . $invoice['id']); return; } // Update or create TenantPackage for subscription \App\Models\TenantPackage::updateOrCreate( [ 'tenant_id' => $tenantId, 'package_id' => $packageId, ], [ 'purchased_at' => now(), 'expires_at' => now()->addYear(), // Renew annually 'active' => true, ] ); // Create or update PackagePurchase \App\Models\PackagePurchase::updateOrCreate( [ 'tenant_id' => $tenantId, 'package_id' => $packageId, 'provider_id' => $subscription, ], [ 'price' => $invoice['amount_paid'] / 100, 'type' => 'reseller_subscription', 'purchased_at' => now(), 'refunded' => false, ] ); Log::info('Subscription renewed via Stripe invoice: ' . $invoice['id'] . ' for tenant ' . $tenantId); } private function handleInvoicePaymentFailed($invoice) { $subscription = $invoice['subscription']; Log::warning('Stripe invoice payment failed: ' . $invoice['id'] . ' for subscription ' . $subscription); // TODO: Deactivate package or notify tenant // e.g., TenantPackage::where('provider_id', $subscription)->update(['active' => false]); } }