webrteu / stripe
Stripe Checkout driver for webrteu/payments (Laravel)
Requires
- php: ^8.2
- illuminate/console: ^12.0|^13.0
- illuminate/http: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- webrteu/payments: ^1.0
Requires (Dev)
- larastan/larastan: ^3.13
- laravel/pint: ^1.32
- orchestra/testbench: ^10.0|^11.0
- peckphp/peck: ^0.3
- pestphp/pest: ^3.8|^4.7
- pestphp/pest-plugin-laravel: ^3.2|^4.1
- pestphp/pest-plugin-type-coverage: ^3.6|^4.0.4
- phpstan/phpstan: ^2.3.3,!=2.3.4
- rector/rector: ^2.8
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Integrare Laravel pentru Stripe Checkout (pagina de plată găzduită de Stripe): plăți one-off cu card, Apple Pay,
Google Pay și celelalte metode active în Dashboard, webhook semnat HMAC-SHA256, captură, anulare și refund.
Implementează Gateway, Refundable, Capturable și ConfirmsWebhooks din webrteu/payments.
Nu este un pachet oficial Stripe.
Versiunea 2.x e o rescriere. 1.x era legată de aplicația Auditor (modele
Order,AuditRequest, emailuri, SmartBill). 2.x e generică, ca restul pachetelorwebrteu/*de plăți. Aplicațiile pe 1.x rămân pe^1.0.
Stripe direct (acest pachet) vs Laravel Cashier
webrteu/stripe |
Laravel Cashier | |
|---|---|---|
| Scop | plăți one-off pentru comenzi (magazin, servicii) | abonamente (SaaS): planuri, trial, upgrade, facturi Stripe |
| Model | rândul payments din webrteu/payments, legat polimorfic de comanda ta |
trait Billable pe User, coloane stripe_id, tabele subscriptions |
| Interfață comună | aceeași cu Netopia, PayU, Revolut… (Payments::start('stripe', …)) |
API propriu Stripe |
| Webhook | /webhooks/payments/stripe, idempotent, pe coadă |
/stripe/webhook |
| Dependențe | doar illuminate/* (HTTP prin Http::) |
stripe/stripe-php, moneyphp/money |
Pentru abonamente folosește Cashier. Cele două pot sta în aceeași aplicație: rutele nu se suprapun și ambele
citesc STRIPE_KEY / STRIPE_SECRET. Fiecare endpoint de webhook are însă secretul lui whsec_.
Cerințe
- PHP
^8.2, Laravel 12–13 - cont Stripe (modul test e suficient pentru dezvoltare)
webrteu/payments
Instalare
composer require webrteu/stripe php artisan vendor:publish --tag=stripe-config php artisan stripe:webhook:create --url=https://site.ro/webhooks/payments/stripe
Migrațiile sunt cele din webrteu/payments (--tag=payments-migrations).
Configurare
STRIPE_KEY=pk_test_... # Dashboard → Developers → API keys (folosită doar în frontend, opțional) STRIPE_SECRET=sk_test_... # cheia secretă; sk_live_... în producție STRIPE_WEBHOOK_SECRET=whsec_... # afișat de stripe:webhook:create; mai multe separate prin virgulă STRIPE_API_VERSION=2025-03-31.basil # versiunea API fixată STRIPE_CAPTURE_MODE=automatic # automatic | manual (pre-autorizare, capturezi cu Stripe::capture) STRIPE_CANCEL_URL= # unde revine clientul dacă renunță; gol = returnUrl STRIPE_TIMEOUT=20
Nu există variabilă „sandbox”: modul test / live e dat de cheie (sk_test_ / sk_live_).
Metodele de plată se aleg din Dashboard (payment_method_types = null în config); pentru doar card:
'payment_method_types' => ['card']. ⚠️ Câmpurile sesiunii se confirmă pe versiunea API fixată.
Utilizare
use WeBRTeu\Payments\Data\CustomerData; use WeBRTeu\Payments\Data\PaymentRequestData; use WeBRTeu\Payments\Facades\Payments; $redirect = Payments::start('stripe', new PaymentRequestData( orderId: $order->number.'-'.$attempt, // unic per încercare amount: 14170, // în bani description: 'Comanda #'.$order->number, customer: new CustomerData($email, $firstName, $lastName), returnUrl: route('plata.return', $order), // Stripe adaugă ?session_id=cs_... ), payable: $order); return $redirect->toResponse(); // redirect spre checkout.stripe.com
Dacă items sunt trimise și totalul lor e exact amount, apar ca rânduri separate în Checkout; altfel apare un singur
rând cu description. orderId ajunge în client_reference_id și în metadata.order_id (sesiune și PaymentIntent).
Crearea sesiunii trimite Idempotency-Key: webrteu-checkout-{orderId}.
| Metodă | Stripe |
|---|---|
status('cs_…') / status('pi_…') |
GET /v1/checkout/sessions/{id} / GET /v1/payment_intents/{id} (cu refund-urile) |
Stripe::capture($id, $bani) |
POST /v1/payment_intents/{pi}/capture |
Stripe::void($id) |
POST /v1/payment_intents/{pi}/cancel |
Stripe::refund($id, $bani) |
POST /v1/refunds |
Stripe::intentId('cs_…') |
id-ul PaymentIntent-ului sesiunii |
provider_payment_id salvat e id-ul sesiunii (cs_…); metodele de mai sus acceptă și pi_….
Webhook
php artisan stripe:webhook:create --url=https://site.ro/webhooks/payments/stripe php artisan stripe:webhook:list
create afișează secretul whsec_ o singură dată și nu scrie nimic în fișiere: îl pui tu în STRIPE_WEBHOOK_SECRET.
Local, cu Stripe CLI: stripe listen --forward-to localhost:8000/webhooks/payments/stripe (secretul afișat de CLI
merge pus lângă cel de producție, separat prin virgulă).
Ruta e înregistrată de webrteu/payments în afara grupului web. Dacă toate rutele tale sunt sub web:
->withMiddleware(function (Middleware $middleware) { $middleware->validateCsrfTokens(except: ['webhooks/payments/*']); })
Verificare: Stripe-Signature = t={timestamp},v1={HMAC-SHA256("{timestamp}.{corp brut}", whsec)}, comparat cu toate
v1 și toate secretele; timestamp-ul trebuie să fie în toleranța de 300 s. Răspuns: HTTP 200 {"received":true}.
Evenimentul e semnat și conține obiectul complet, deci nu se mai cere API-ul, cu o excepție: la charge.refunded
comanda se ia din metadata.order_id al PaymentIntent-ului.
Aplicația ascultă evenimentul din core:
Event::listen(PaymentStatusChanged::class, function (PaymentStatusChanged $event): void { if ($event->to === PaymentStatus::Paid) { $event->payment->payable?->markAsPaid(); // + factura, emailuri etc. } });
Pagina de return nu marchează comanda plătită; poate afișa Payments::gateway('stripe')->status($request->session_id).
Statusuri
| Eveniment Stripe | PaymentStatus |
|---|---|
checkout.session.completed cu payment_status=paid |
Paid (altfel Pending, ex. transfer bancar) |
checkout.session.async_payment_succeeded / _failed |
Paid / Failed |
checkout.session.expired |
Cancelled |
payment_intent.amount_capturable_updated |
Authorized (la capture_mode=manual) |
payment_intent.succeeded / .canceled |
Paid / Cancelled |
payment_intent.payment_failed |
Failed (nu e în lista implicită: în Checkout clientul poate reîncerca) |
charge.refunded |
Refunded sau PartiallyRefunded |
Comenzi Artisan
stripe:webhook:create {--url=} {--events=*}stripe:webhook:list- din core:
payments:reconcile, de programat la 10 minute (prinde webhook-urile pierdute)
Ce nu face pachetul
- abonamente, clienți Stripe salvați, Customer Portal (pentru asta: Laravel Cashier)
- Stripe Elements / Payment Element în pagina ta (doar Checkout găzduit)
- Stripe Connect
- nu scrie secretele webhook-urilor în
.env
Testare
composer test
STRIPE_SANDBOX_SECRET=sk_test_... vendor/bin/pest --group=sandbox
Fixture-urile sunt scrise după documentația Stripe. De înlocuit cu răspunsuri reale din modul test.
Changelog
Vezi CHANGELOG.md.
Autor
Licență
MIT. Vezi LICENSE.md.