70 lines
2.3 KiB
PHP
70 lines
2.3 KiB
PHP
<?php
|
|
|
|
namespace App\Actions;
|
|
|
|
use App\Models\DunningCase;
|
|
use App\Models\Instance;
|
|
use App\Services\Billing\DunningMailer;
|
|
use App\Services\Billing\DunningSchedule;
|
|
use App\Services\Stripe\StripeClient;
|
|
use Illuminate\Support\Carbon;
|
|
|
|
/**
|
|
* Einen Mahnfall schliessen — aber erst, wenn WIRKLICH alles bezahlt ist.
|
|
*
|
|
* Die Bedingung des Betreibers lautete „bis der Betrag + Mahngebühren beglichen
|
|
* ist". Wer den Rückstand zahlt und die Mahnspesen offen lässt, ist damit nicht
|
|
* fertig, und seine Cloud bleibt aus.
|
|
*
|
|
* Gefragt wird STRIPE, nicht der eigene Zustand: die offenen Rechnungen des
|
|
* Kunden sind die Wahrheit über das, was noch aussteht. Ein eigenes Feld
|
|
* „bezahlt" je Gebührenrechnung wäre eine zweite Buchhaltung, die mit der
|
|
* ersten auseinanderläuft — und zwar genau dann, wenn eine Zahlung ausserhalb
|
|
* unseres Ablaufs eingeht.
|
|
*/
|
|
class SettleDunningCase
|
|
{
|
|
public function __invoke(DunningCase $case): bool
|
|
{
|
|
if (! $case->isOpen()) {
|
|
return false;
|
|
}
|
|
|
|
$customerId = $case->subscription?->customer?->stripe_customer_id;
|
|
|
|
if (blank($customerId)) {
|
|
return false;
|
|
}
|
|
|
|
$offen = array_column(app(StripeClient::class)->openInvoices((string) $customerId), 'id');
|
|
$unsere = array_merge([$case->stripe_invoice_id], array_values($case->fee_invoice_ids ?? []));
|
|
|
|
// Steht noch eine davon offen, ist der Fall nicht zu.
|
|
if (array_intersect($unsere, $offen) !== []) {
|
|
return false;
|
|
}
|
|
|
|
$case->update(['settled_at' => Carbon::now()]);
|
|
|
|
$stand = $case->level;
|
|
|
|
// Und die Cloud kommt zurück. Nur die des betroffenen Vertrags —
|
|
// andere Verträge desselben Kunden waren nie betroffen.
|
|
foreach (Instance::query()
|
|
->where('customer_id', $case->subscription?->customer_id)
|
|
->whereNotNull('suspended_at')
|
|
->get() as $instance) {
|
|
app(ResumeInstance::class)($instance);
|
|
}
|
|
|
|
// Nur wer wirklich stand, bekommt die Entwarnung. Wer auf Stufe 1
|
|
// bezahlt hat, hat nie eine Sperre gesehen — ihm zu schreiben, seine
|
|
// Cloud laufe wieder, wäre eine Nachricht über etwas, das nie war.
|
|
if ($stand >= DunningSchedule::SUSPENDED) {
|
|
app(DunningMailer::class)->resumed($case);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|