finance

Checkout Stripe one-shot

Pagamento una-tantum via Stripe Checkout/Payment Intents con webhook firmati, gestione SCA/3DS, ricevuta automatica, refund e riconciliazione DB ↔ Stripe.

Cosa fa questo modulo

Modulo «Checkout Stripe one-shot»: incasso di pagamenti singoli (non ricorrenti, non legati a slot booking come #8324) tramite Stripe per casi d'uso come acquisto prodotto digitale, pagamento corso/evento a importo fisso, donazione, acquisto pacchetto crediti, riscatto carrello e-commerce, vendita servizio professionale forfettario, top-up wallet, sblocco contenuto premium una-tantum, pagamento parcella/preventivo accettato, contributo associativo annuo a importo fisso senza rinnovo automatico. Differenza da #8320 abbonamenti Cashier: qui nessuna ricorrenza, nessun trial, nessun upgrade/downgrade, nessun portale cliente — un singolo `PaymentIntent` confermato e chiuso. Differenza da #8324 caparra: qui pagamento full-amount slegato da uno slot calendario/booking (no holding di disponibilità, no saldo a saldo, no integrazione #8323 calendario). Architettura due modalità complementari attivabili per use-case: (1) **Stripe Checkout Session hosted** — redirect a pagina Stripe-hosted via `checkout.sessions.create` con `mode=payment`+`line_items[{price_data: {currency, unit_amount, product_data: {name, description, images}}, quantity}]`+`success_url`+`cancel_url`+`customer_email` (o `customer` se Customer Stripe già esistente)+`payment_method_types=[card,sepa_debit,bancontact,ideal,apple_pay,google_pay]`+`locale=auto`+`automatic_tax.enabled` se serve+`tax_id_collection.enabled=true` per P.IVA B2B+`billing_address_collection=required` per fatturazione+`shipping_address_collection.allowed_countries=[IT,...]` se prodotto fisico+`metadata` con `ordine_id`/`cliente_id`/`prodotto_slug`/`tenant_id` per riconciliazione lato webhook. Vantaggio: zero codice frontend, UI Stripe-mantenuta, PCI SAQ-A nativo, A/B test conversione gestito da Stripe, supporto promotion codes nativo via `allow_promotion_codes=true` integrabile con #8321; (2) **Payment Intents + Stripe Elements custom** — UI custom in-app via Stripe.js + Payment Element drop-in (`stripe.elements({clientSecret})` con `clientSecret` da `paymentIntents.create({amount, currency, payment_method_types, capture_method: automatic|manual, metadata, receipt_email})`), confirm via `stripe.confirmPayment({elements, confirmParams: {return_url}})` con gestione SCA/3DS automatica nel return flow, fallback su `payment_intent.status` (succeeded/requires_action/requires_payment_method/canceled), preview Apple/Google Pay via Express Checkout Element. Vantaggio: UX completamente integrata nel brand/flusso app, una sola pagina senza redirect, supporta multi-step wizard con upsell pre-pagamento, gestione granulare 3DS con UI propria per fallback retry. Modello dati: tabella `payments_one_shot` (id, riferimento polimorfico `payable_type`+`payable_id` per collegare a Order/Invoice/Donation/EventRegistration/Quote, cliente_id, stripe_payment_intent_id univoco, stripe_checkout_session_id se hosted, importo_minor_units (centesimi int per evitare float), valuta default EUR multi-currency supportata, descrizione_pagamento, metadata JSON con tutto il contesto business per debugging, stato `created`/`requires_action`/`processing`/`succeeded`/`canceled`/`refunded`/`partially_refunded`/`failed`/`disputed`, capture_method `automatic` (default) o `manual` per autorizzazione+capture differita (es. preordini), modalita `checkout_session`/`payment_intent`, payment_method_type effettivo a buon fine (`card`/`sepa_debit`/`bancontact`/`ideal`/`apple_pay`/`google_pay`/`klarna`/`afterpay`), last4 carta+brand per UI ricevuta, paese_carta, receipt_url Stripe-hosted, fee_amount Stripe trattenuta (da `balance_transaction.fee` per profittabilità calcolata netto), net_amount accreditato su account, created_at, paid_at, refunded_at, refund_reason, disputed_at, dispute_outcome, audit_log_id #8330 link), tabella `payment_refunds` (payment_id, stripe_refund_id, importo_minor_units, motivo `requested_by_customer`/`duplicate`/`fraudulent`/`product_not_received`/`other`+note_admin, eseguito_da_user_id admin, eseguito_at, stato refund Stripe), tabella `payment_webhook_events` (stripe_event_id univoco idempotency, tipo evento, payload JSON snapshot, processed_at, processing_errors, retry_count) per audit ricezione webhook ed evitare doppi processing. Flusso checkout hosted Stripe Checkout Session: pagina prodotto/carrello/donazione con CTA «Paga ora» → POST `/checkout/create-session` con `payable_type`+`payable_id`+`amount` → server crea PaymentIntent implicito via Session, salva record `payments_one_shot` con stato `created`+session_id+intent_id, ritorna `session.url` o `session.id` per redirect Stripe.js `stripe.redirectToCheckout({sessionId})` → cliente paga su Stripe-hosted → redirect a `success_url` con `{CHECKOUT_SESSION_ID}` placeholder → server recupera session via `checkout.sessions.retrieve(id, {expand: [payment_intent, customer]})` e mostra success page con riepilogo importo + ricevuta link + CTA prosegui (download prodotto, conferma iscrizione, ringraziamento donazione) → in parallelo webhook `checkout.session.completed` aggiorna stato a `processing` o `succeeded` (se carta istantanea) e triggera business logic (delivery prodotto, accesso area cliente #8326, invio fattura SDI #8322, mail conferma #8331, log #8330). Flusso checkout custom Payment Intents: pagina checkout in-app con riepilogo carrello + Stripe Payment Element montato + form indirizzo fatturazione → POST `/checkout/create-intent` con dati ordine → server crea PaymentIntent con `amount`+`currency`+`automatic_payment_methods.enabled=true`+`metadata` e ritorna `client_secret` → frontend `stripe.confirmPayment({elements, confirmParams: {return_url, payment_method_data: {billing_details}}})` → gestione redirect 3DS automatica da Stripe.js → return su `return_url?payment_intent={pi}&payment_intent_client_secret={cs}` con verifica server-side `paymentIntents.retrieve(id)` per stato finale → success o retry con messaggio errore specifico (`card_declined`/`insufficient_funds`/`expired_card`/`incorrect_cvc`/`authentication_required` mappati a messaggi user-friendly). Gestione SCA/3DS (PSD2): Stripe gestisce challenge 3DS automaticamente sia in Checkout Session sia in Payment Element, esenzioni TRA (Transaction Risk Analysis) richieste automaticamente per importi <€30 con basso rischio, fallback `requires_action` con redirect cliente a banca per OTP/biometria, gestione status `processing` per metodi async (SEPA Direct Debit, bonifico) con webhook `payment_intent.processing` → `payment_intent.succeeded` (anche giorni dopo per SEPA), UI app con messaggio «Pagamento in elaborazione, riceverai conferma via email» evitando di promettere accesso immediato a contenuto per metodi async. Capture differita (preordini, prenotazioni custom non-#8324): `capture_method: manual` in PaymentIntent crea autorizzazione carta senza addebito immediato (hold fondi 7gg carta), endpoint admin `POST /admin/payments/{id}/capture` per `paymentIntents.capture(id, {amount_to_capture})` con possibilità capture parziale (es. autorizzato €100 ma cliente compra solo €70, capture €70 e libera resto), endpoint `POST /admin/payments/{id}/cancel-authorization` per `paymentIntents.cancel(id)` se ordine non confermato, alert email admin 24h prima scadenza autorizzazione per decidere capture/cancel. Webhook Stripe gestiti su endpoint dedicato `/stripe/webhook-one-shot` con verifica firma constant-time `Stripe\Webhook::constructEvent(payload, sigHeader, STRIPE_WEBHOOK_SECRET)` (separato da #8320 abbonamenti per isolare logica e secret rotation): `checkout.session.completed` (Stripe Checkout success, aggiorna stato + triggera delivery), `checkout.session.expired` (cliente abbandona senza pagare entro 24h, marca `canceled`, opzionale email recupero carrello #8331 con coupon #8321), `payment_intent.created` (log creazione), `payment_intent.succeeded` (pagamento confermato per modalità Payment Intent custom, triggera business logic), `payment_intent.payment_failed` (failure con last_payment_error.code per mostrare messaggio specifico al cliente e suggerire metodo alternativo), `payment_intent.processing` (per SEPA/async, UI mostra «in attesa»), `payment_intent.canceled` (annullato lato server o per timeout), `charge.refunded` (refund completato, marca payment come refunded/partially_refunded e libera eventuale prodotto/accesso), `charge.dispute.created` (chargeback aperto cliente contesta, alert admin urgente per fornire evidence via Stripe dashboard, freeze eventuale delivery prodotto fisico se ancora possibile), `charge.dispute.closed` (esito chargeback won/lost con eventuale recupero o perdita definitiva), `radar.early_fraud_warning.created` (warning Stripe Radar su rischio frode imminente, alert admin per ispezione manuale e refund preventivo se sospetto). Tutti i webhook idempotenti via `stripe_event_id` unique con tabella `payment_webhook_events`, processing in queue job async (`ProcessStripeOneShotWebhookJob`) per evitare timeout 5s Stripe richiesto, retry exponential backoff se job fallisce, alert Slack/email admin se webhook accumula failure ricorrenti. Refund admin: pannello dettaglio pagamento con pulsante «Rimborsa» (full o parziale con importo+motivo) → `refunds.create({payment_intent, amount?, reason})` → webhook `charge.refunded` aggiorna stato + revoca accesso prodotto se applicabile + invio email conferma cliente #8331 «Rimborso di €X processato, accredito in 5-10gg lavorativi su carta originale» + nota credito automatica #8322 con riga negativa + audit log #8330 con admin che ha eseguito. Refund completo o parziale, supporto refund multipli su stesso pagamento fino al totale. Receipt e ricevute: Stripe Checkout invia automaticamente ricevuta email Stripe-hosted a `customer_email` con dettaglio pagamento + link a ricevuta web online evergreen, configurabile branding (logo + colori + indirizzo merchant) in dashboard Stripe `/settings/branding`, opzionale ricevuta custom HTML/PDF in-app via #8322 modulo fatturazione (integrazione webhook `payment_intent.succeeded` triggera emissione ricevuta non-fiscale o fattura SDI se P.IVA cliente raccolta, allegato in mail conferma #8331). Per donazioni: ricevuta esente IVA con causale «erogazione liberale ai sensi art. X», archiviazione per dichiarazione redditi. Promotion codes/coupon: integrazione con #8321 via `allow_promotion_codes=true` su Checkout Session (Stripe gestisce inserimento codice in UI hosted, applica sconto, mostra subtotale aggiornato) oppure server-side per Payment Intent custom (validazione coupon prima di create intent, applica sconto al `amount`, traccia redemption in tabella `coupon_redemptions` #8321 con `ordine_id` polimorfico). Multi-line items + tax: Stripe Checkout supporta multi `line_items` con quantità (es. carrello con 3 prodotti + spedizione), shipping_options per scelta corriere + costo, automatic_tax.enabled per calcolo IVA Stripe Tax automatico (IT 22% + reverse charge UE B2B se VAT ID fornito), tax_behavior `inclusive`/`exclusive` su price_data. Per Payment Intent custom: calcolo subtotale + sconto + tax + shipping lato server prima di create intent con amount finale. Multi-currency: supporto pagamenti in EUR/USD/GBP/CHF/altre con `currency` su PaymentIntent/CheckoutSession, conversione automatica Stripe se account abilitato Multi-Currency Settlement, UI prezzo localizzato in-app via geo-IP/lingua utente, esposizione pre-checkout della valuta finale per evitare sorprese su carta non-EUR. Pannello admin pagamenti: datatable Livewire #8327 (filtri: stato, range data, range importo, metodo pagamento, paese carta, cliente, ricerca per email/ID), dettaglio singolo pagamento con timeline eventi (intent created, requires_action, succeeded/failed, refunds, disputes), azioni manuali (refund full/parziale, marca paid manuale per fallback offline, cancella intent in attesa, contatta cliente via mail integrata, link a dashboard Stripe per drill-down), report giornaliero/mensile (volume incassato, fee Stripe totali, netto accreditato, % failed, % refunded, top metodi pagamento, conversion rate da intent creato a paid, drop-off step funnel checkout), export CSV/Excel #8328 per commercialista/contabilità con colonne (data, importo lordo, fee, netto, stato, riferimento ordine, ricevuta_url), reconciliation report che matcha `payments_one_shot.succeeded` con `Stripe Balance Transactions` per spotting discrepanze. Comandi artisan: `php artisan stripe:reconcile-one-shot {--from=} {--to=}` (verifica DB locale vs Stripe API per range date, alert su payment_intent presenti in Stripe ma assenti in DB e viceversa, opzionale auto-fix con `--fix`), `php artisan stripe:retry-failed-webhooks` (riprocessa webhook events con `processing_errors` non null per recovery dopo down momentaneo), `php artisan stripe:cleanup-abandoned-sessions {--older-than-days=7}` (purge `payments_one_shot` con stato `created`/`requires_action` da più di N giorni senza completamento per igiene DB), `php artisan stripe:report-fees {month}` (report fee Stripe del mese per analisi profittabilità), `php artisan stripe:export-tax-report {--year=}` (report annuale per dichiarazione tasse con breakdown per paese). Sicurezza: verifica firma webhook constant-time con secret separato `STRIPE_WEBHOOK_SECRET_ONE_SHOT` (rotabile senza impattare #8320 abbonamenti), idempotency su create PaymentIntent via `Idempotency-Key` header con UUID per ogni tentativo (evita double-charge in caso retry client), HTTPS forzato su success_url/cancel_url/return_url/webhook endpoint, rate limit endpoint create-session/create-intent per cliente+IP (max 10 al minuto) per anti-fraud, no carta toccata dal server (PCI SAQ-A via Stripe Elements/Checkout), validazione amount server-side da carrello DB mai accettando importo client-side (cliente potrebbe manipolare DevTools), integrazione Stripe Radar attivo con rules custom (block test cards in prod, geo-blocking paesi alto rischio configurabili, velocity rules >5 tentativi falliti stesso IP in 10min), 3DS forzato `payment_method_options.card.request_three_d_secure=any` per importi alti o paesi non-EU per ridurre chargeback, audit log #8330 immutabile su ogni evento (create/succeed/fail/refund/dispute) con admin attore se manuale + before/after snapshot per dispute investigation, alert admin webhook `charge.dispute.created` urgente per evidence submission entro 7-14gg. Compliance: GDPR diritto cancellazione cliente con anonimizzazione (sostituzione email con hash, mantenimento aggregati per fisco), conservazione ricevute/fatture 10 anni per fisco italiano (archivio S3 con WORM + #8322 storage SDI), invio fattura elettronica SDI #8322 entro 12gg dal pagamento se cliente B2B con P.IVA raccolta (webhook `checkout.session.completed`/`payment_intent.succeeded` triggera job emissione FE), gestione VAT ID UE con reverse charge B2B automatico via Stripe Tax, T&C checkout obbligatori con checkbox consenso + privacy policy link + diritto recesso 14gg per consumer EU. UX patterns chiave: «Trust badges» (loghi Visa/Mastercard/Apple Pay/Stripe verified per credibilità), «Express Checkout» (Apple/Google Pay one-tap pre-form per ridurre friction), «Order bump» (upsell prodotto correlato +€X con singolo checkbox in checkout, +20-30% AOV documentato), «Abandoned cart recovery» (cron scan sessions `expired` o intents `requires_payment_method` >24h → mail #8331 con coupon #8321 «hai dimenticato qualcosa, ecco -10%»), «Multi-step progress indicator» (Carrello → Dati → Pagamento → Conferma con avanzamento visibile riduce drop-off), «Save card for later» (opzionale checkbox per salvare metodo via `setup_future_usage=on_session` per acquisti futuri one-click ma senza ricorrenza automatica), «Mobile-first checkout» (form full-width, tastiera numerica per CVC, Apple/Google Pay prominente), «Error recovery inline» (se carta declinata, mostra messaggio specifico + suggerimento «prova SEPA o Apple Pay» senza ricaricare pagina). Metriche e KPI dashboard: conversion rate da pagina prodotto a paid (overall + per device + per metodo), AOV (Average Order Value), authorization rate (% carte autorizzate al primo tentativo, target >85%), 3DS challenge rate + 3DS success rate (target >90%), refund rate + chargeback rate (target <0.5% chargeback per evitare programmi Visa/Mastercard), fee % su revenue (target ~2.9% + €0.25 per transazione EU card), failed payments breakdown per `decline_code` per identificare problemi sistemici. Casi d'uso applicati al workspace: Footility (acquisto credito agentic top-up, sblocco feature one-time, parcella consulenza), Klabhouse (acquisto corso/evento singolo, donazione concerto, vendita merchandise), Holiday Self Drive (vendita pacchetto extra non-noleggio tipo gadget/assicurazione standalone, gift card), gestionali (acquisto modulo aggiuntivo licenza perpetua), the-body-code/realpilates (acquisto pacchetto lezioni a importo fisso senza calendario specifico — per slot calendario usare #8324 caparra), mscarichi (acquisto materiale extra fuori contratto). Differenza da #8324 caparra Stripe (one-time legato a booking slot): qui no integrazione #8323 calendario, no holding slot, pagamento full-amount slegato da disponibilità temporale. Differenza da #8320 abbonamenti Cashier: qui no Cashier, no ricorrenza, no `subscriptions` table, no proration, no portale Stripe — utilizzo diretto PaymentIntents API. Differenza da PayPal/Satispay/altri PSP: qui Stripe-only per coerenza con #8320/#8324 ma architettura estendibile a multi-PSP con strategy pattern futuro (`StripePaymentProvider` vs `PaypalPaymentProvider` vs `SatispayPaymentProvider`). Estensioni future: BNPL (Buy Now Pay Later) integrato via Stripe Klarna/Afterpay/Affirm con `payment_method_types` aggiuntivo per importi >€100 (+15-30% conversion documentato su e-commerce), wallet pre-pagato per cliente B2B con top-up one-shot e scalo crediti su servizi successivi senza re-checkout, A/B testing landing checkout (hosted vs custom) con tracking conversion, dynamic price per cliente VIP (sconto runtime via #8321), gift purchase (acquisto regalo per terzo con email destinatario per attivazione codice), multi-vendor split payment via Stripe Connect (per marketplace, fee piattaforma + payout merchant automatici), Apple Pay / Google Pay button su pagina prodotto pre-checkout per «Buy Now» one-tap. Costo: 1 giorno setup Stripe account + chiavi test/live + webhook endpoint + secret, 1 giorno modello dati `payments_one_shot` + `payment_refunds` + `payment_webhook_events` migration + relazioni polimorfiche, 1.5 giorni implementazione Checkout Session hosted (create-session endpoint + redirect + success/cancel pages + retrieve sessione), 1.5 giorni implementazione Payment Intent custom + Stripe Elements + Payment Element drop-in + Express Checkout + gestione SCA/3DS + return flow, 1 giorno webhook handlers + verifica firma + idempotency + queue job processing async, 1 giorno admin panel datatable + dettaglio pagamento + timeline + refund UI + report KPI, 0.5 giorno integrazione #8321 coupon (allow_promotion_codes + validation), 0.5 giorno integrazione #8322 fatturazione SDI (job emissione su succeeded), 0.5 giorno integrazione #8331 mail conferma + #8330 audit log, 0.5 giorno comandi artisan reconcile + cleanup + report, 1 giorno testing flussi (success carta, decline, SCA challenge, refund full/parziale, dispute simulation, webhook retry, multi-currency, Apple Pay, capture differita, abandoned cart). Dipendenze: pacchetto `stripe/stripe-php` v13+, account Stripe attivo con prodotti/prezzi configurati o gestione amount dinamico server-side, dominio HTTPS + webhook endpoint pubblico raggiungibile da Stripe (no localhost in prod, usare Stripe CLI `stripe listen` in dev), #8331 mail transazionali (obbligatorio per conferma + refund + abandoned cart recovery), #8330 audit log per tracking eventi e dispute investigation, opzionale #8322 fatturazione elettronica per integrazione SDI B2B, opzionale #8321 coupon per promotion codes, opzionale #8326 area cliente per sezione «I miei pagamenti» con storico + ricevute scaricabili, opzionale #8328 export per report contabilità/commercialista, opzionale #8327 anagrafica clienti per arricchimento dati cliente in dettaglio pagamento.

Esempi d'uso

  • finance

Demo correlate

Disponibile nei pacchetti