58 lines
2.3 KiB
PHP
58 lines
2.3 KiB
PHP
<?php
|
|
|
|
namespace App\Observers;
|
|
|
|
use App\Actions\ApplyPlanChange;
|
|
use App\Models\Order;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Throwable;
|
|
|
|
/**
|
|
* The moment a bought upgrade becomes a bigger machine.
|
|
*
|
|
* StartCustomerProvisioning turns a paid order into work from the webhook that
|
|
* announced the payment. An upgrade has no such webhook: there is no checkout in
|
|
* this application yet, payment is mocked repo-wide, and the order sits in the
|
|
* cart at `pending` until something marks it paid. So the trigger is hung on the
|
|
* fact itself — the order becoming paid — rather than on whichever call site
|
|
* happens to exist today. A real Stripe checkout, an operator settling a
|
|
* purchase by hand and a seeder all end at the same `status = paid`, and all
|
|
* three have to result in the customer getting what they bought.
|
|
*
|
|
* Only an UPDATE, never a create. Every order in this codebase is created either
|
|
* already-paid by provisioning (a first purchase, which opens a contract rather
|
|
* than changing one) or pending by the shop; an upgrade that goes straight in as
|
|
* paid is not a state this application produces, and firing on create would make
|
|
* every fixture that builds one start a provisioning run.
|
|
*
|
|
* A downgrade is deliberately not here. It costs nothing, is never paid, and
|
|
* lands at the end of the term — clupilot:apply-due-plan-changes carries those.
|
|
*/
|
|
class OrderObserver
|
|
{
|
|
public function updated(Order $order): void
|
|
{
|
|
if (! $order->wasChanged('status') || $order->status !== 'paid') {
|
|
return;
|
|
}
|
|
|
|
if ($order->type !== 'upgrade') {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
app(ApplyPlanChange::class)->forOrder($order);
|
|
} catch (Throwable $e) {
|
|
// Never allowed to fail the write that marked the order paid. That
|
|
// write is the record that money changed hands, and undoing it over
|
|
// a plan change would erase the payment and leave nothing to retry
|
|
// from. A change that did not land is loud in its own right: the
|
|
// order stays unconsumed and the contract still says the old
|
|
// package, both of which somebody can see.
|
|
Log::error('Failed to apply the plan change a paid order bought.', [
|
|
'order_id' => $order->id, 'plan' => $order->plan, 'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
}
|