CluPilotCloud/app/Actions/StartCustomerProvisioning.php

65 lines
2.2 KiB
PHP

<?php
namespace App\Actions;
use App\Models\Customer;
use App\Models\Order;
use App\Models\ProvisioningRun;
use App\Provisioning\Jobs\AdvanceRunJob;
use Illuminate\Database\UniqueConstraintViolationException;
use Illuminate\Support\Facades\DB;
/**
* Turns a paid Stripe event into a customer + order + provisioning run. The
* order's unique stripe_event_id is the idempotency key: a duplicate webhook
* never starts a second run.
*/
class StartCustomerProvisioning
{
/**
* @param array{id:string,email:string,name:?string,stripe_customer_id:?string,plan:string,datacenter:string,amount_cents:int,currency:string} $event
*/
public function fromStripeEvent(array $event): ?Order
{
if (Order::query()->where('stripe_event_id', $event['id'])->exists()) {
return null; // already processed
}
try {
[$order, $run] = DB::transaction(function () use ($event) {
$customer = Customer::query()->firstOrCreate(
['email' => $event['email']],
['name' => $event['name'] ?? $event['email'], 'stripe_customer_id' => $event['stripe_customer_id'] ?? null],
);
$order = Order::create([
'customer_id' => $customer->id,
'plan' => $event['plan'],
'amount_cents' => $event['amount_cents'],
'currency' => $event['currency'],
'datacenter' => $event['datacenter'],
'stripe_event_id' => $event['id'],
'status' => 'paid',
]);
$run = ProvisioningRun::create([
'subject_type' => Order::class,
'subject_id' => $order->id,
'pipeline' => 'customer',
'status' => ProvisioningRun::STATUS_PENDING,
'current_step' => 0,
'context' => [],
]);
return [$order, $run];
});
} catch (UniqueConstraintViolationException) {
return null; // concurrent duplicate webhook
}
AdvanceRunJob::dispatch($run->uuid); // after commit
return $order;
}
}