78 lines
2.6 KiB
PHP
78 lines
2.6 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire;
|
|
|
|
use App\Models\Customer;
|
|
use App\Services\Stripe\StripeClient;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Livewire\Component;
|
|
|
|
/**
|
|
* Was der Kunde noch schuldet, und der Knopf, es zu begleichen.
|
|
*
|
|
* Die Liste kommt bei jedem Rendern frisch von Stripe. Eine zwischengelagerte
|
|
* Kopie zeigte einen Bezahlknopf neben einer Rechnung, die inzwischen beglichen
|
|
* ist — und der zweite Klick wäre eine zweite Abbuchung.
|
|
*/
|
|
class OpenInvoices extends Component
|
|
{
|
|
/**
|
|
* Was Stripe zur letzten Zahlung gesagt hat, wenn sie nicht durchging.
|
|
*
|
|
* Stripes eigener Wortlaut, nicht eine eigene Umschreibung: er nennt den
|
|
* Grund — abgelaufen, abgelehnt, Deckung — und danach kann der Kunde
|
|
* handeln. „Zahlung fehlgeschlagen" könnte er nur weiterreichen.
|
|
*/
|
|
public ?string $failure = null;
|
|
|
|
public function pay(string $invoiceId): void
|
|
{
|
|
$stripe = app(StripeClient::class);
|
|
$customerId = $this->stripeCustomerId();
|
|
|
|
// Die ID kommt aus dem Browser. Nur was in DIESER Liste steht, darf
|
|
// eingezogen werden — sonst reicht jemand eine fremde Rechnungs-ID ein
|
|
// und Stripe bucht sie von der Karte eines fremden Kunden ab.
|
|
$offen = array_column($stripe->openInvoices($customerId), 'id');
|
|
abort_unless(in_array($invoiceId, $offen, true), 403);
|
|
|
|
$ergebnis = $stripe->payInvoice($invoiceId);
|
|
$this->failure = $ergebnis['paid'] ? null : $ergebnis['failure'];
|
|
|
|
if ($ergebnis['paid']) {
|
|
$this->dispatch('notify', message: __('billing.invoice_paid'));
|
|
}
|
|
}
|
|
|
|
public function payAll(): void
|
|
{
|
|
foreach (app(StripeClient::class)->openInvoices($this->stripeCustomerId()) as $rechnung) {
|
|
$this->pay($rechnung['id']);
|
|
|
|
// Nach der ersten Ablehnung anhalten. Alle weiteren scheitern an
|
|
// derselben Karte, und drei Ablehnungen hintereinander lassen
|
|
// Stripes Betrugserkennung anschlagen — danach lehnt sie auch die
|
|
// Karte ab, die in Ordnung wäre.
|
|
if ($this->failure !== null) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private function stripeCustomerId(): string
|
|
{
|
|
$customer = Customer::forUser(Auth::user());
|
|
|
|
abort_if($customer === null || blank($customer->stripe_customer_id), 404);
|
|
|
|
return (string) $customer->stripe_customer_id;
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('livewire.open-invoices', [
|
|
'invoices' => app(StripeClient::class)->openInvoices($this->stripeCustomerId()),
|
|
]);
|
|
}
|
|
}
|