STR-Spiegelung: USt aus total_taxes lesen (Stripe-API ohne tax-Feld)

Aktuelle Stripe-API-Versionen liefern kein Top-Level-`tax` mehr auf der
Invoice — die Spiegelung buchte 0 USt und markierte die Rechnung als
netto (QA-Befund 07.07., STR-00001). Fallback-Kette: tax (alt) →
total_taxes (aktuell) → total_tax_amounts (Übergangsname).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-07 11:42:31 +00:00
parent 4b7403bd4e
commit 4cda4d3bda
2 changed files with 23 additions and 1 deletions

View file

@ -74,7 +74,10 @@ class ProcessStripeWebhook
} }
$subtotal = (int) ($stripeInvoice['subtotal'] ?? 0); $subtotal = (int) ($stripeInvoice['subtotal'] ?? 0);
$tax = (int) ($stripeInvoice['tax'] ?? 0); // Steuer je nach Stripe-API-Generation: `tax` (alt), sonst Summe aus
// `total_taxes` (aktuell) bzw. `total_tax_amounts` (Übergangsname).
$tax = (int) ($stripeInvoice['tax']
?? collect($stripeInvoice['total_taxes'] ?? $stripeInvoice['total_tax_amounts'] ?? [])->sum('amount'));
$total = (int) ($stripeInvoice['total'] ?? $subtotal + $tax); $total = (int) ($stripeInvoice['total'] ?? $subtotal + $tax);
$invoice = Invoice::query()->create([ $invoice = Invoice::query()->create([

View file

@ -52,6 +52,25 @@ test('a paid stripe invoice is mirrored into the STR circle', function () {
expect($invoice->invoiceBillingAddress->name)->toBe('Alpha GmbH'); expect($invoice->invoiceBillingAddress->name)->toBe('Alpha GmbH');
}); });
test('a current-api invoice without top-level tax reads total_taxes', function () {
// Stripe-API ab 2025: kein `tax`-Feld mehr, Steuer steht in `total_taxes`
// (QA-Befund 07.07.: USt. wurde als 0 gespiegelt).
$user = User::factory()->create(['stripe_id' => 'cus_test_1']);
app(ProcessStripeWebhook::class)->handle(new WebhookReceived(stripeInvoicePayload($user, [
'tax' => null,
'subtotal' => 2900,
'total' => 3451,
'total_taxes' => [['amount' => 551, 'taxable_amount' => 2900, 'tax_behavior' => 'exclusive']],
])));
$invoice = Invoice::sole();
expect($invoice->amount_cents)->toBe(2900);
expect($invoice->tax_cents)->toBe(551);
expect($invoice->total_cents)->toBe(3451);
expect($invoice->is_netto)->toBeFalse();
});
test('duplicate webhook deliveries do not create a second invoice', function () { test('duplicate webhook deliveries do not create a second invoice', function () {
$user = User::factory()->create(['stripe_id' => 'cus_test_1']); $user = User::factory()->create(['stripe_id' => 'cus_test_1']);
$listener = app(ProcessStripeWebhook::class); $listener = app(ProcessStripeWebhook::class);