67 lines
2.5 KiB
PHP
67 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace App\Actions;
|
|
|
|
use App\Models\DunningCase;
|
|
use App\Models\Subscription;
|
|
use App\Services\Billing\DunningMailer;
|
|
use App\Services\Billing\DunningSchedule;
|
|
use Illuminate\Support\Carbon;
|
|
|
|
/**
|
|
* Der Fall, der entsteht, wenn eine Abbuchung zum ersten Mal scheitert.
|
|
*
|
|
* Hängt an ApplyStripeBillingEvent::invoicePaymentFailed(), das bis hierher
|
|
* `past_due` setzte und schwieg. Der Kunde erfuhr davon erst, wenn seine Cloud
|
|
* stand.
|
|
*
|
|
* JE RECHNUNG GENAU EINMAL. Stripe versucht dieselbe Rechnung mehrfach, und
|
|
* jeder Versuch ist ein eigenes Ereignis — ohne diese Bedingung entstünden drei
|
|
* Fälle für eine Schuld, mit drei Mahnläufen und dreifachen Gebühren. Die
|
|
* Eindeutigkeit steht zusätzlich als Index in der Tabelle, damit zwei
|
|
* gleichzeitig eintreffende Ereignisse sie nicht umgehen können.
|
|
*
|
|
* Eröffnet wird auf Stufe 0 — dem HINWEIS. Keine Mahnung, keine Gebühr: eine
|
|
* abgelaufene Karte ist keine Zahlungsverweigerung.
|
|
*/
|
|
class OpenDunningCase
|
|
{
|
|
public function __invoke(Subscription $subscription, string $invoiceId): ?DunningCase
|
|
{
|
|
if ($invoiceId === '') {
|
|
return null;
|
|
}
|
|
|
|
$now = Carbon::now();
|
|
|
|
$case = DunningCase::query()->firstOrCreate(
|
|
['stripe_invoice_id' => $invoiceId],
|
|
[
|
|
'subscription_id' => $subscription->id,
|
|
'level' => 0,
|
|
'opened_at' => $now,
|
|
// Die erste Mahnung, nicht der Hinweis: der geht sofort hinaus.
|
|
'next_step_at' => $now->copy()->addDays(DunningSchedule::dayOfLevel(1)),
|
|
'fee_invoice_ids' => [],
|
|
'notified_levels' => [],
|
|
],
|
|
);
|
|
|
|
// Der Hinweis geht SOFORT hinaus, nicht erst mit dem Tageslauf: der
|
|
// Kunde soll von der gescheiterten Abbuchung erfahren, solange er noch
|
|
// weiss, wovon die Rede ist. Nur beim ERSTEN Mal — wasRecentlyCreated
|
|
// unterscheidet das Eröffnen vom Wiedersehen derselben Rechnung.
|
|
if ($case->wasRecentlyCreated) {
|
|
app(DunningMailer::class)->level($case->load('subscription.customer'), 0);
|
|
|
|
// VERMERKEN, sonst hält der Nachhol-Lauf des nächsten Tages diese
|
|
// Nachricht für verloren und schickt dem Kunden dieselbe Mail ein
|
|
// zweites Mal. Erst nach dem Einreihen: andersherum stünde hier
|
|
// eine Nachricht, die nie einging.
|
|
$case->update(['notified_levels' => [0]]);
|
|
}
|
|
|
|
return $case;
|
|
}
|
|
}
|