Configurazione Pagamenti e Fatturazione Elettronica
Flo espone due superfici indipendenti rivolte al denaro, entrambe operate per tenant:
| Superficie | Dove vive la configurazione | Gate |
|---|---|---|
| Pagamenti online (checkout ospitato Stripe / PayPal) | Variabili d'ambiente del backend (PAYMENT__*) | enable_online_payments |
| Fatturazione elettronica italiana (SDI tramite gateway esterno) | Database del tenant, modificato nella UI admin (credenziali cifrate a riposo) | enable_einvoicing |
Questa pagina è la guida operativa di setup per entrambe. La semantica generale delle variabili d'ambiente è documentata in Variabili d'Ambiente.
Feature flag e dipendenze
| Flag | Effetto |
|---|---|
enable_online_payments | Abilita il checkout online. Il salvataggio in blocco dalla UI admin lo rifiuta salvo che anche enable_bookings ed enable_einvoicing siano attivi (errors.payment.featureDependency). |
enable_bookings | Flusso prenotazioni; il checkout prenotazioni lo controlla per primo. |
enable_einvoicing | Bounded context fatturazione elettronica. Con flag spento, ogni endpoint /api/v1/einvoicing/* risponde 404 (la feature è nascosta, non solo disabilitata). |
enable_subscriptions | Offerte piani studio e checkout commerce; senza di esso l'endpoint offerte restituisce lista vuota. |
enable_finance | Sezione sidebar Finanza (pagamenti, rimborsi, incassi/report). |
- Il checkout prenotazioni (
POST /api/v1/payments/booking-checkout) richiedeenable_bookings,enable_einvoicingedenable_online_payments; se uno è spento restituisce l'errore tradotto di disabilitazione. - Il checkout piani studio (
POST /api/v1/commerce/studio-plan-checkout) richiedeenable_subscriptionseenable_online_payments. - I valori dei flag sono in cache per 60 secondi; una modifica ha effetto entro quella finestra.
- CLI:
flo config flags set <id> enable_online_payments on(forma dichiarativa).flo instance payments <id> --enable-flagsabilitaenable_online_paymentsedenable_einvoicingin un'unica esecuzione.
Vedi Feature Flag per il catalogo completo.
Configurazione provider di pagamento (ambiente)
I segreti del provider restano nell'ambiente del backend e non vengono mai persistiti nel
database del tenant. Le chiavi PAYMENT__* mappano la sezione di configurazione ASP.NET Payment:*
(__ è il separatore di sezione).
Stripe
| Variabile | Valore |
|---|---|
PAYMENT__PROVIDER | stripe (predefinito) oppure paypal |
PAYMENT__STRIPE__SECRETKEY | Chiave segreta Stripe (sk_live_ / sk_test_) |
PAYMENT__STRIPE__WEBHOOKSECRET | Segreto di firma webhook Stripe (whsec_) |
La readiness Stripe richiede sia la chiave segreta sia il segreto webhook.
PayPal
| Variabile | Valore |
|---|---|
PAYMENT__PROVIDER | paypal |
PAYMENT__PAYPAL__ENVIRONMENT | sandbox oppure live; vuoto significa live. Qualsiasi altro valore è rifiutato invece di puntare silenziosamente a live |
PAYMENT__PAYPAL__CLIENTID / PAYMENT__PAYPAL__CLIENTSECRET | Credenziali OAuth |
PAYMENT__PAYPAL__WEBHOOKID | ID webhook PayPal usato per la verifica della firma |
La readiness PayPal richiede client id, client secret e webhook id.
Chiavi condivise
| Variabile | Valore |
|---|---|
PAYMENT__SUCCESSURL / PAYMENT__CANCELURL | Redirect dopo il checkout (predefinito: URL frontend) |
PAYMENT__INVOICESELLER__NAME | Denominazione legale del cedente per le fatture emesse |
PAYMENT__INVOICESELLER__VATNUMBER | Partita IVA del cedente |
PAYMENT__INVOICESELLER__FISCALCODE | Codice fiscale del cedente |
PAYMENT__INVOICESELLER__REGIMEFISCALE | Codice regime fiscale (es. RF01) |
PAYMENT__INVOICESELLER__ADDRESS / __STREETNUMBER | Indirizzo |
PAYMENT__INVOICESELLER__POSTALCODE / __CITY / __PROVINCE / __COUNTRY | CAP, città, sigla provincia, nazione (deve essere IT) |
PAYMENT__INVOICESELLER__EMAIL | Email del cedente |
Il cedente fattura è validato come un blocco coerente: se il tenant ha salvato un qualsiasi
campo cedente nelle impostazioni di fatturazione elettronica, l'intero cedente proviene dalle
impostazioni; altrimenti proviene interamente dall'ambiente. I campi obbligatori
sono partita IVA, denominazione legale, regime, indirizzo, CAP, città e nazione IT.
Manopole di runtime
Queste chiavi di configurazione ASP.NET sono sovrascrivibili con la convenzione __
(es. PAYMENT__HOLDMINUTES):
| Chiave | Predefinito | Effetto |
|---|---|---|
Payment:HoldMinutes | 30 | Durata del blocco (hold) di checkout |
Payment:MaxActiveHoldsPerUser | 3 | Massimo di hold attivi concorrenti per utente |
Payment:PaidReviewHoldMinutes | 1440 | Hold conservato per un ordine in attesa di revisione operatore |
Payment:InvoiceSeries / Payment:InvoiceTimeZone | FLO / — | Fallback di numerazione fatture quando le impostazioni tenant sono vuote |
Payment:InvoiceEmailMaxAttempts | — | Max tentativi email fattura prima che il job passi in revisione |
Payment:FinalizationMaxAttempts | — | Max tentativi di finalizzazione ordine |
Configurazione via CLI
# Stripe (chiavi di test sui sandbox), abilita entrambi i flag
flo instance payments api-pflocal-useflo-net \
--stripe-secret-key sk_test_… --stripe-webhook-secret whsec_… --enable-flags
# PayPal sandbox
flo instance payments api-pflocal-useflo-net --provider paypal \
--paypal-client-id … --paypal-client-secret … --paypal-webhook-id … \
--paypal-environment sandbox
# Cedente fattura via ambiente (i campi cedente a DB vincono per blocco)
flo instance payments api-pflocal-useflo-net --invoice-seller-vat … --invoice-seller-name …
# Ispezione: env (mascherato), flag e commerce readiness; --json per le macchine
flo instance payments api-pflocal-useflo-net
Il comando scrive solo i valori modificati; quando cambiano valori env riavvia
l'istanza (salta con --no-restart). --dry-run stampa il piano senza scrivere.
Le scritture verso un target remoto live (non di test) richiedono -y o una
conferma interattiva; --seed (attività di test) è rifiutato sui target live.
instance payments non è esposto tramite il control plane --vps — eseguilo
su un target locale o tramite il percorso SSH diretto.
Endpoint webhook
| Provider | Endpoint |
|---|---|
| Stripe | POST /api/v1/payments/stripe/webhook |
| PayPal | POST /api/v1/payments/paypal/webhook |
- Anonimi, esenti da antiforgery, con rate limit, body max 64 KB. Il redirect browser non conferma mai un pagamento: il webhook firmato (o una verifica lato server) è la fonte di verità.
- Stripe: verificato localmente con HMAC-SHA256 di
{timestamp}.{payload}usando il segretowhsec_, letto dall'headerStripe-Signature; firme più vecchie di 5 minuti sono rifiutate. Eventi supportati:checkout.session.completed(solo quandopayment_status=paid),checkout.session.async_payment_succeeded,checkout.session.async_payment_failed. Gli altri eventi sono riscontrati senza cambio di stato. - PayPal: verificato da remoto tramite
POST /v1/notifications/verify-webhook-signatureconPAYMENT__PAYPAL__WEBHOOKID. Eventi supportati:CHECKOUT.ORDER.APPROVED,PAYMENT.CAPTURE.COMPLETED,PAYMENT.CAPTURE.DENIED; gli eventi non supportati sono riscontrati. - Gli eventi webhook sono deduplicati su
(Provider, ExternalEventId). - Instradamento: un riferimento ordine
commerce:<id>va alla macchina a stati commerce; un riferimento numerico va alla macchina a stati delle prenotazioni.
Per lo sviluppo locale, flo instance payments <id> --watch-stripe avvia il
listener Stripe CLI, inoltra alla rotta webhook locale, cattura il segreto di firma
whsec_… e lo scrive nel .env del tenant; --stop-watch lo ferma.
Readiness e verifica
| Superficie | Endpoint / comando |
|---|---|
| Liveness | GET /health |
| Readiness completa | GET /health/ready |
| Commerce readiness (JSON sicuro) | GET /health/commerce |
| API readiness admin | GET /api/v1/payments/admin/readiness (Admin) |
| Impostazioni fatturazione elettronica + probe | GET/PUT /api/v1/einvoicing/settings, POST /api/v1/einvoicing/settings/test-connection (Admin) |
| CLI operatore | flo instance health <id> (controllo Commerce readiness), flo instance payments <id> |
I controlli di readiness sono:
| Chiave controllo | Passa quando |
|---|---|
paymentProvider | Le credenziali del provider configurato sono presenti (stripe o paypal) |
einvoicingCredentials | Le credenziali del provider sono memorizzate e decifrabili |
invoiceSeller | L'identità cedente effettiva supera la validazione |
/health/commerce riporta enabled: false (sano) quando
enable_online_payments è spento, e ready: true/false quando è acceso. Un
checkout è rifiutato con errors.payment.providerUnavailable,
errors.eInvoicing.notConfigured o errors.payment.invoiceConfigurationInvalid
finché tutti e tre i controlli passano.
Ordini, hold e finalizzazione
- Prezzi, IVA, valuta e righe sono sempre calcolati lato server; il client non può inviare importi.
- Un checkout crea un ordine in sospeso più un hold di capacità (predefinito 30 minuti, max 3 hold attivi per utente). L'hold è rilasciato alla scadenza o su fallimento e convertito alla conferma.
- Un ordine pagato verificato è finalizzato in modo asincrono: viene creata una prenotazione (o un
ClientSubscriptionper i piani studio) e accodato un job di fattura. - Se la capacità è andata persa tra pagamento e finalizzazione, il pagamento è rimborsato
automaticamente (motivo
capacity_refunded); se il rimborso stesso fallisce, l'ordine resta in revisione operatore con le evidenze conservate. - Azioni di revisione admin:
POST /api/v1/payments/admin/orders/{id}/reviewconConfirmPayment,FinalizeBooking,ReleaseoRefundPayment;POST /api/v1/commerce/admin/orders/{id}/reviewconConfirmPayment,FinalizeFulfillment,ReleaseoRefundPayment. Liste/dettagli ordini sono esposti sotto/api/v1/payments/admin/orderse/api/v1/commerce/admin/orders. - I clienti possono riconciliare un checkout rientrato senza webhook tramite
POST /api/v1/commerce/orders/{id}/reconcile, che verifica la sessione presso il provider e applica la stessa macchina a stati del webhook.
Job in background, rimborsi e riconciliazione
Due servizi in background sempre registrati interrogano ogni 30 secondi:
| Servizio | Ciclo |
|---|---|
BookingPaymentBackgroundService | Scade gli ordini in sospeso, finalizza gli ordini pagati, elabora la coda fatture |
CommerceOrderBackgroundService | Stesso, più la coda email commerce |
La fatturazione elettronica non ha webhook: EInvoiceReconcileService interroga il gateway ogni
5 minuti (EInvoicing:ReconcileIntervalMinutes; valori sotto 1 ricadono a
5) dopo un ritardo iniziale di un minuto, ed è no-op salvo enable_einvoicing
attivo e credenziali configurate. Replica le fatture in ingresso e i cambi di stato SDI
usando un watermark per provider/ambiente (LastSyncedAt), che viene
azzerato quando cambiano provider o ambiente.
Rimborsi:
- I rimborsi admin girano solo dopo verifica lato server del pagamento remoto;
i rimborsi provider usano chiavi di idempotenza stabili
(
flo-refund-order-<id>,flo-refund-subscription-<id>). - Un rimborso prima dell'emissione fattura cancella il job accodato. Un rimborso dopo l'emissione crea una bozza di nota di credito TD04 collegata che l'operatore rivede e invia; un rimborso parziale presso il provider non viene mai addebitato due volte.
- Le sottoscrizioni cliente pagate online possono essere rimborsate in toto o per il periodo residuo
(
ComputeRefundAmount); il rimborso cancella la sottoscrizione, cancella le prenotazioni future e risveglia la lista d'attesa. - Mentre un rimborso è in volo l'ordine è
RefundPending(fail closed); un errore del provider ripristina lo stato precedente.
Endpoint di recupero manuale:
| Azione | Endpoint |
|---|---|
| Riprova una fattura prenotazioni | POST /api/v1/payments/orders/{id}/invoice/retry |
| Riprova una fattura commerce | POST /api/v1/commerce/admin/orders/{id}/invoice/retry |
| Riconcilia una fattura replicata | POST /api/v1/einvoicing/fatture/{id}/sync |
| Riconcilia l'intera replica | POST /api/v1/einvoicing/fatture/sync |
Fatturazione elettronica (SDI)
Modello dei provider
| Provider | Credenziali | Note |
|---|---|---|
FatturaApi | Username + password | Token gateway in cache per (ambiente, username); cache invalidata al cambio credenziali |
OpenApi | Token API (bearer) | Invoice API di OpenAPI.it |
Local | nessuna | Righe replica importate da XML FatturaPA; non chiama mai un provider. Non selezionabile come provider |
Ambiente: Test (predefinito) o Prod. Cambiare provider o ambiente azzera
il watermark di riconciliazione e forza una risincronizzazione completa.
URL gateway predefiniti (sovrascrivibili tramite chiavi di configurazione):
| Provider | Predefinito | Override |
|---|---|---|
FatturaApi | https://fattura-elettronica-api.it/ws2.0/{test|prod} | EInvoicing:BaseUrl |
OpenApi | https://test.invoice.openapi.com / https://invoice.openapi.com | EInvoicing:OpenApiBaseUrl:Test / EInvoicing:OpenApiBaseUrl:Prod |
Cosa configura l'operatore
UI admin: Amministrazione → Fatturazione, scheda Impostazioni (solo Admin,
visibile con enable_einvoicing):
- Provider e ambiente.
- Credenziali: username + password (
FatturaApi) o token API (OpenApi). I segreti sono write-only — l'API restituisce solopasswordConfigured/apiTokenConfigured, e la UI mostra se uno è memorizzato. Un salvataggio è una patch: i campi assenti/vuoti conservano il valore memorizzato. - Partita IVA mittente predefinita, serie fatture e fuso orario fiscale.
- Identità cedente (denominazione, P.IVA, codice fiscale, regime, indirizzo, CAP, città, provincia, nazione, email).
- Testi brandizzati del PDF di cortesia: intestazione (≤500 caratteri), piè di pagina (≤500) e note legali (≤2000).
- Test connessione interroga il gateway con le credenziali memorizzate.
Le credenziali sono cifrate con ASP.NET Data Protection (purpose
Flo.EInvoicing.Credentials.v1); il chiaro non è mai persistito né restituito. Se
il key ring viene ruotato e un payload memorizzato non è più decifrabile, le
credenziali sono trattate come non configurate e vanno reinserite.
Alternativa CLI (parla con l'API delle impostazioni admin, richiede una sessione admin e attende la cache dei flag da 60 secondi):
flo instance payments api-pflocal-useflo-net \
--e-invoicing-provider FatturaApi --e-invoicing-environment Test \
--fatturaapi-username … --fatturaapi-password … \
--admin-email admin@example.com --test-connection
Usa --fatturaapi-token per OpenApi. I campi cedente salvati nelle impostazioni a DB
hanno precedenza su PAYMENT__INVOICESELLER__*; la CLI avverte quando entrambi sono impostati.
Cosa fa il cliente
Il cliente non vede mai le credenziali del gateway. Al checkout compila il
profilo di fatturazione (nome/indirizzo, partita IVA o codice fiscale, codice destinatario SDI o
PEC); Flo lo fotografa sull'ordine e invia il PDF di cortesia via email dopo
l'emissione (richiede enable_email_sender; altrimenti il job email è marcato
Skipped). L'identità cedente proviene sempre dalla configurazione del tenant.
Flusso di emissione e stati
-
Un ordine pagato finalizzato accoda esattamente un job di fattura (sorgente
BookingoCommerceOrder) con snapshot immutabile del cedente e numero fattura riservato. -
Il worker emette tramite il gateway configurato. In caso di successo la riga replica riceve il riferimento gateway e lo stato SDI, e l'email del PDF è accodata.
-
Gli esiti sono classificati centralmente:
Classe Stati SDI Gestione Confermato INVI,PREN,CONS,NONC,ACCE,DECOJob Submitted, email accodataRifiuto deterministico ERRO,RIFIJob Failed, riprovabile da un admin dopo correzioneEsito ignoto / risposta persa qualsiasi altro Job NeedsReview, mai riprovato automaticamente -
Il retry fattura admin (
.../invoice/retry) richiede che provider e ambiente del job corrispondano alle impostazioni correnti. Quando esiste un id fattura remoto, Flo prima lo sincronizza e reinvia solo su rifiuto deterministico; un jobNeedsReviewsenza id remoto richiede un flag esplicitoproviderOutcomeConfirmedprima di essere riaperto. -
Il reinvio manuale (
POST /api/v1/einvoicing/fatture/{id}/resend) è consentito solo per righeERRO/RIFIoNEEDS_REVIEWe avvia un nuovo tentativo idempotente. -
La replica è riconciliata dal ciclo ogni 5 minuti e dagli endpoint sync manuali. L'XML FatturaPA importato (
POST /api/v1/einvoicing/fatture/import) diventa una bozza locale che un admin può trasmettere conPOST /api/v1/einvoicing/fatture/{id}/send.
Superfici admin nella stessa pagina: Inviate (inviate, con bozze e note di
credito), Ricevute (ricevute, con badge non lette), Nuova (compositore, incluse
note di credito TD04/TD05 collegate all'originale), Aziende (controparti),
Pagamenti e Commerce (superfici ordini). Fatture offline e collettive
sono generate tramite POST /api/v1/einvoicing/fatture/offline/booking/{id},
.../offline/subscription/{id} e POST .../collective; GET .../export
scarica un CSV per intervallo di date.
Stati di errore
| Codice errore | Significato |
|---|---|
errors.eInvoicing.notConfigured | Nessuna credenziale provider utilizzabile |
errors.eInvoicing.authenticationFailed | Il gateway ha rifiutato le credenziali |
errors.eInvoicing.gatewayUnavailable | Errore di trasporto verso il gateway |
errors.eInvoicing.gatewayRejected | Il gateway ha rifiutato la richiesta |
errors.eInvoicing.structuredInvoiceInvalid | Il payload FatturaPA non ha superato la validazione |
errors.eInvoicing.sendFailed | Fallimento di invio deterministico (sicuro riprovare dopo correzione) |
errors.eInvoicing.emissionOutcomeUnknown | Esito perso/ignoto — riconciliare da remoto prima di riprovare |
errors.eInvoicing.resendNotAllowed | La riga non è in stato reinviabile |
errors.eInvoicing.invoiceImportInvalid | L'XML importato non è un documento FatturaPA valido |
errors.eInvoicing.invoiceNumberAlreadyUsed | Collisione sul numero riservato |
errors.eInvoicing.creditNoteSourceUnavailable / ...CreationFailed | La nota di credito non può essere creata dalla fattura sorgente |
errors.eInvoicing.permissionDenied | Il chiamante non è Admin (o manca la policy di vista fatturazione elettronica di finanza) |
Riferimento codice
| File | Scopo |
|---|---|
Flo.BE/Controllers/PaymentsController.cs | Readiness, checkout prenotazioni, webhook provider, ordini admin e retry |
Flo.BE/Controllers/CommerceController.cs | Offerte/checkout piani studio, reconcile cliente, ordini commerce admin |
Flo.BE/Services/Payments/StripePaymentGateway.cs / PayPalPaymentGateway.cs | Adattatori provider, parsing webhook e verifica firma |
Flo.BE/Services/Payments/OnlinePaymentGatewayResolver.cs | Selezione provider dalla configurazione tenant |
Flo.BE/Services/Payments/PaymentReadinessService.cs | I tre controlli di readiness |
Flo.BE/Services/Payments/BookingPaymentService.cs / CommerceOrderService.cs | Macchine a stati ordini, hold, finalizzazione, rimborsi, job fatture |
Flo.BE/Services/Payments/SubscriptionRefundService.cs | Rimborsi sottoscrizioni + bozza TD04 |
Flo.BE/Services/Payments/BookingPaymentBackgroundService.cs / CommerceOrderBackgroundService.cs | Cicli in background ogni 30 secondi |
Flo.BE/BoundedContexts/EInvoicing/Controllers/FattureController.cs | Replica fatture, compositore, import, resend, sync, PDF |
Flo.BE/BoundedContexts/EInvoicing/Controllers/EInvoicingSettingsController.cs | CRUD impostazioni e probe di connessione |
Flo.BE/BoundedContexts/EInvoicing/Services/EInvoicingSettingsService.cs | Credenziali cifrate, default cedente |
Flo.BE/BoundedContexts/EInvoicing/Services/FattureService.cs | Macchine a stati emit / import / resend / sync |
Flo.BE/BoundedContexts/EInvoicing/Services/EInvoiceReconcileService.cs | Ciclo di riconciliazione SDI ogni 5 minuti |
Flo.BE/BoundedContexts/EInvoicing/Services/EmissionStates.cs | Insiemi esiti SDI confermati vs deterministici |
Flo.BE/BoundedContexts/EInvoicing/Services/Gateway/ | Client FatturaApi e OpenAPI.it |
cli/src/commands/instance/payments.ts | Comando operatore flo instance payments |