247 lines
8.4 KiB
PHP
247 lines
8.4 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\PackagePurchase;
|
|
use App\Models\TenantPackage;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Stripe\Stripe;
|
|
use Stripe\Webhook;
|
|
|
|
class StripeWebhookController extends Controller
|
|
{
|
|
public function __construct()
|
|
{
|
|
Stripe::setApiKey(config('services.stripe.secret'));
|
|
}
|
|
|
|
public function handleWebhook(Request $request)
|
|
{
|
|
$payload = $request->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 'checkout.session.completed':
|
|
$session = $event['data']['object'];
|
|
if ($session['mode'] === 'subscription' && isset($session['metadata']['subscription']) && $session['metadata']['subscription'] === 'true') {
|
|
$this->handleSubscriptionStarted($session);
|
|
}
|
|
break;
|
|
|
|
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]);
|
|
}
|
|
|
|
private function handleSubscriptionStarted($session)
|
|
{
|
|
$metadata = $session['metadata'];
|
|
if (!$metadata['user_id'] && !$metadata['tenant_id'] || !$metadata['package_id']) {
|
|
Log::warning('Missing metadata in Stripe checkout session: ' . $session['id']);
|
|
return;
|
|
}
|
|
|
|
$userId = $metadata['user_id'] ?? null;
|
|
$tenantId = $metadata['tenant_id'] ?? null;
|
|
$packageId = $metadata['package_id'];
|
|
$type = $metadata['type'] ?? 'reseller_subscription';
|
|
|
|
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 checkout session: ' . $session['id']);
|
|
return;
|
|
}
|
|
|
|
$subscriptionId = $session['subscription']['id'] ?? null;
|
|
if (!$subscriptionId) {
|
|
Log::error('No subscription ID in checkout session: ' . $session['id']);
|
|
return;
|
|
}
|
|
|
|
// Activate TenantPackage for initial subscription
|
|
\App\Models\TenantPackage::updateOrCreate(
|
|
[
|
|
'tenant_id' => $tenantId,
|
|
'package_id' => $packageId,
|
|
],
|
|
[
|
|
'purchased_at' => now(),
|
|
'expires_at' => now()->addYear(),
|
|
'active' => true,
|
|
]
|
|
);
|
|
|
|
// Create initial PackagePurchase
|
|
\App\Models\PackagePurchase::create([
|
|
'tenant_id' => $tenantId,
|
|
'package_id' => $packageId,
|
|
'provider_id' => $subscriptionId,
|
|
'price' => $session['amount_total'] / 100,
|
|
'type' => $type,
|
|
'purchased_at' => now(),
|
|
'refunded' => false,
|
|
]);
|
|
|
|
// Update tenant subscription fields if needed
|
|
$tenant = \App\Models\Tenant::find($tenantId);
|
|
if ($tenant) {
|
|
$tenant->update([
|
|
'subscription_id' => $subscriptionId,
|
|
'subscription_status' => 'active',
|
|
]);
|
|
}
|
|
|
|
Log::info('Initial subscription activated via Stripe checkout session: ' . $session['id'] . ' for tenant ' . $tenantId);
|
|
}
|
|
} |