CluPilotCloud/app/Http/Controllers/StripeWebhookController.php

73 lines
2.6 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Actions\StartCustomerProvisioning;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
/**
* Stripe webhook: verifies the signature (when a secret is configured) and turns
* a paid checkout into a provisioning run. Idempotent via the order's
* stripe_event_id — Stripe retries a webhook until it gets a 2xx.
*/
class StripeWebhookController extends Controller
{
public function __invoke(Request $request, StartCustomerProvisioning $action): JsonResponse
{
$payload = $request->getContent();
$secret = (string) config('services.stripe.webhook_secret');
if (filled($secret) && ! $this->signatureValid($payload, (string) $request->header('Stripe-Signature'), $secret)) {
abort(400, 'invalid signature');
}
$event = json_decode($payload, true) ?: [];
$type = $event['type'] ?? '';
if (! in_array($type, ['checkout.session.completed', 'payment_intent.succeeded'], true)) {
return response()->json(['ignored' => true]);
}
$object = $event['data']['object'] ?? [];
$meta = $object['metadata'] ?? [];
$action->fromStripeEvent([
'id' => (string) ($event['id'] ?? ''),
'email' => $object['customer_details']['email'] ?? $object['customer_email'] ?? ($meta['email'] ?? 'unknown@example.com'),
'name' => $object['customer_details']['name'] ?? ($meta['name'] ?? null),
'stripe_customer_id' => $object['customer'] ?? null,
'plan' => $meta['plan'] ?? 'start',
'datacenter' => $meta['datacenter'] ?? 'fsn',
'amount_cents' => (int) ($object['amount_total'] ?? $object['amount'] ?? 0),
'currency' => strtoupper($object['currency'] ?? 'eur'),
]);
return response()->json(['ok' => true]);
}
/** Verify Stripe's `t=…,v1=…` signature: HMAC-SHA256 of "{t}.{payload}". */
private function signatureValid(string $payload, string $header, string $secret): bool
{
if (blank($header)) {
return false;
}
$parts = [];
foreach (explode(',', $header) as $segment) {
[$key, $value] = array_pad(explode('=', $segment, 2), 2, '');
$parts[$key] = $value;
}
$timestamp = $parts['t'] ?? null;
$signature = $parts['v1'] ?? null;
if ($timestamp === null || $signature === null) {
return false;
}
$expected = hash_hmac('sha256', $timestamp.'.'.$payload, $secret);
return hash_equals($expected, $signature);
}
}