Wallet-Topup ueber Stripe (Credit-Pakete mit Bonus-Staffel)

End-to-end-Aufladung der Credit-Wallet, spiegelt den Einzel-PM-Fluss:

- Paket-Staffel mit Bonus in config/credits.php (10->10, 25->27, 50->55,
  100->115 Credits; price_cents netto, Stripe Tax ergaenzt USt.)
- credit_topups (Pending->Paid) + CreditTopupService: startTopup legt den
  Pending-Kauf an, fulfill() schreibt die Wallet idempotent gut (credited_at)
- StripeCheckoutService::forCreditTopup via checkoutCharge + credit_topup_id
  Metadata; ProcessStripeWebhook schreibt bei checkout.session.completed gut
- Checkout-Route /admin/me/checkout/credits/{pack} mit Rechnungsadress-Gate

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-06-17 14:56:23 +00:00
parent c89589beb0
commit bd7022ffaf
10 changed files with 382 additions and 1 deletions

View file

@ -2,6 +2,7 @@
namespace App\Services\Billing;
use App\Models\CreditTopup;
use App\Models\Plan;
use App\Models\SinglePurchase;
use App\Models\User;
@ -117,4 +118,24 @@ class StripeCheckoutService
'metadata' => ['single_purchase_id' => (string) $purchase->id],
]);
}
/**
* Stripe-Checkout für eine Wallet-Aufladung. Ad-hoc-Charge über den
* Netto-Paketpreis (Stripe Tax ergänzt die USt.); die `credit_topup_id`
* in den Metadaten schließt den Kreis im Webhook (Wallet-Gutschrift).
*/
public function forCreditTopup(User $user, CreditTopup $topup): Checkout
{
$this->syncTaxIdFromBillingAddress($user);
return $user->checkoutCharge(
$topup->price_cents,
"Credit-Guthaben ({$topup->credits} Credits)",
1,
[
...$this->sessionOptions(),
'metadata' => ['credit_topup_id' => (string) $topup->id],
],
);
}
}