Passa al contenuto principale

Configurazione Pagamenti e Fatturazione Elettronica

Flo espone due superfici indipendenti rivolte al denaro, entrambe operate per tenant:

SuperficieDove vive la configurazioneGate
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​

FlagEffetto
enable_online_paymentsAbilita 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_bookingsFlusso prenotazioni; il checkout prenotazioni lo controlla per primo.
enable_einvoicingBounded context fatturazione elettronica. Con flag spento, ogni endpoint /api/v1/einvoicing/* risponde 404 (la feature è nascosta, non solo disabilitata).
enable_subscriptionsOfferte piani studio e checkout commerce; senza di esso l'endpoint offerte restituisce lista vuota.
enable_financeSezione sidebar Finanza (pagamenti, rimborsi, incassi/report).
  • Il checkout prenotazioni (POST /api/v1/payments/booking-checkout) richiede enable_bookings, enable_einvoicing ed enable_online_payments; se uno è spento restituisce l'errore tradotto di disabilitazione.
  • Il checkout piani studio (POST /api/v1/commerce/studio-plan-checkout) richiede enable_subscriptions e enable_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-flags abilita enable_online_payments ed enable_einvoicing in 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​

VariabileValore
PAYMENT__PROVIDERstripe (predefinito) oppure paypal
PAYMENT__STRIPE__SECRETKEYChiave segreta Stripe (sk_live_ / sk_test_)
PAYMENT__STRIPE__WEBHOOKSECRETSegreto di firma webhook Stripe (whsec_)

La readiness Stripe richiede sia la chiave segreta sia il segreto webhook.

PayPal​

VariabileValore
PAYMENT__PROVIDERpaypal
PAYMENT__PAYPAL__ENVIRONMENTsandbox oppure live; vuoto significa live. Qualsiasi altro valore è rifiutato invece di puntare silenziosamente a live
PAYMENT__PAYPAL__CLIENTID / PAYMENT__PAYPAL__CLIENTSECRETCredenziali OAuth
PAYMENT__PAYPAL__WEBHOOKIDID webhook PayPal usato per la verifica della firma

La readiness PayPal richiede client id, client secret e webhook id.

Chiavi condivise​

VariabileValore
PAYMENT__SUCCESSURL / PAYMENT__CANCELURLRedirect dopo il checkout (predefinito: URL frontend)
PAYMENT__INVOICESELLER__NAMEDenominazione legale del cedente per le fatture emesse
PAYMENT__INVOICESELLER__VATNUMBERPartita IVA del cedente
PAYMENT__INVOICESELLER__FISCALCODECodice fiscale del cedente
PAYMENT__INVOICESELLER__REGIMEFISCALECodice regime fiscale (es. RF01)
PAYMENT__INVOICESELLER__ADDRESS / __STREETNUMBERIndirizzo
PAYMENT__INVOICESELLER__POSTALCODE / __CITY / __PROVINCE / __COUNTRYCAP, città, sigla provincia, nazione (deve essere IT)
PAYMENT__INVOICESELLER__EMAILEmail 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):

ChiavePredefinitoEffetto
Payment:HoldMinutes30Durata del blocco (hold) di checkout
Payment:MaxActiveHoldsPerUser3Massimo di hold attivi concorrenti per utente
Payment:PaidReviewHoldMinutes1440Hold conservato per un ordine in attesa di revisione operatore
Payment:InvoiceSeries / Payment:InvoiceTimeZoneFLO / —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​

ProviderEndpoint
StripePOST /api/v1/payments/stripe/webhook
PayPalPOST /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 segreto whsec_, letto dall'header Stripe-Signature; firme più vecchie di 5 minuti sono rifiutate. Eventi supportati: checkout.session.completed (solo quando payment_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-signature con PAYMENT__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​

SuperficieEndpoint / comando
LivenessGET /health
Readiness completaGET /health/ready
Commerce readiness (JSON sicuro)GET /health/commerce
API readiness adminGET /api/v1/payments/admin/readiness (Admin)
Impostazioni fatturazione elettronica + probeGET/PUT /api/v1/einvoicing/settings, POST /api/v1/einvoicing/settings/test-connection (Admin)
CLI operatoreflo instance health <id> (controllo Commerce readiness), flo instance payments <id>

I controlli di readiness sono:

Chiave controlloPassa quando
paymentProviderLe credenziali del provider configurato sono presenti (stripe o paypal)
einvoicingCredentialsLe credenziali del provider sono memorizzate e decifrabili
invoiceSellerL'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 ClientSubscription per 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}/review con ConfirmPayment, FinalizeBooking, Release o RefundPayment; POST /api/v1/commerce/admin/orders/{id}/review con ConfirmPayment, FinalizeFulfillment, Release o RefundPayment. Liste/dettagli ordini sono esposti sotto /api/v1/payments/admin/orders e /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:

ServizioCiclo
BookingPaymentBackgroundServiceScade gli ordini in sospeso, finalizza gli ordini pagati, elabora la coda fatture
CommerceOrderBackgroundServiceStesso, 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:

AzioneEndpoint
Riprova una fattura prenotazioniPOST /api/v1/payments/orders/{id}/invoice/retry
Riprova una fattura commercePOST /api/v1/commerce/admin/orders/{id}/invoice/retry
Riconcilia una fattura replicataPOST /api/v1/einvoicing/fatture/{id}/sync
Riconcilia l'intera replicaPOST /api/v1/einvoicing/fatture/sync

Fatturazione elettronica (SDI)​

Modello dei provider​

ProviderCredenzialiNote
FatturaApiUsername + passwordToken gateway in cache per (ambiente, username); cache invalidata al cambio credenziali
OpenApiToken API (bearer)Invoice API di OpenAPI.it
LocalnessunaRighe 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):

ProviderPredefinitoOverride
FatturaApihttps://fattura-elettronica-api.it/ws2.0/{test|prod}EInvoicing:BaseUrl
OpenApihttps://test.invoice.openapi.com / https://invoice.openapi.comEInvoicing: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 solo passwordConfigured / 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​

  1. Un ordine pagato finalizzato accoda esattamente un job di fattura (sorgente Booking o CommerceOrder) con snapshot immutabile del cedente e numero fattura riservato.

  2. 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.

  3. Gli esiti sono classificati centralmente:

    ClasseStati SDIGestione
    ConfermatoINVI, PREN, CONS, NONC, ACCE, DECOJob Submitted, email accodata
    Rifiuto deterministicoERRO, RIFIJob Failed, riprovabile da un admin dopo correzione
    Esito ignoto / risposta persaqualsiasi altroJob NeedsReview, mai riprovato automaticamente
  4. 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 job NeedsReview senza id remoto richiede un flag esplicito providerOutcomeConfirmed prima di essere riaperto.

  5. Il reinvio manuale (POST /api/v1/einvoicing/fatture/{id}/resend) è consentito solo per righe ERRO/RIFI o NEEDS_REVIEW e avvia un nuovo tentativo idempotente.

  6. 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 con POST /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 erroreSignificato
errors.eInvoicing.notConfiguredNessuna credenziale provider utilizzabile
errors.eInvoicing.authenticationFailedIl gateway ha rifiutato le credenziali
errors.eInvoicing.gatewayUnavailableErrore di trasporto verso il gateway
errors.eInvoicing.gatewayRejectedIl gateway ha rifiutato la richiesta
errors.eInvoicing.structuredInvoiceInvalidIl payload FatturaPA non ha superato la validazione
errors.eInvoicing.sendFailedFallimento di invio deterministico (sicuro riprovare dopo correzione)
errors.eInvoicing.emissionOutcomeUnknownEsito perso/ignoto — riconciliare da remoto prima di riprovare
errors.eInvoicing.resendNotAllowedLa riga non è in stato reinviabile
errors.eInvoicing.invoiceImportInvalidL'XML importato non è un documento FatturaPA valido
errors.eInvoicing.invoiceNumberAlreadyUsedCollisione sul numero riservato
errors.eInvoicing.creditNoteSourceUnavailable / ...CreationFailedLa nota di credito non può essere creata dalla fattura sorgente
errors.eInvoicing.permissionDeniedIl chiamante non è Admin (o manca la policy di vista fatturazione elettronica di finanza)

Riferimento codice​

FileScopo
Flo.BE/Controllers/PaymentsController.csReadiness, checkout prenotazioni, webhook provider, ordini admin e retry
Flo.BE/Controllers/CommerceController.csOfferte/checkout piani studio, reconcile cliente, ordini commerce admin
Flo.BE/Services/Payments/StripePaymentGateway.cs / PayPalPaymentGateway.csAdattatori provider, parsing webhook e verifica firma
Flo.BE/Services/Payments/OnlinePaymentGatewayResolver.csSelezione provider dalla configurazione tenant
Flo.BE/Services/Payments/PaymentReadinessService.csI tre controlli di readiness
Flo.BE/Services/Payments/BookingPaymentService.cs / CommerceOrderService.csMacchine a stati ordini, hold, finalizzazione, rimborsi, job fatture
Flo.BE/Services/Payments/SubscriptionRefundService.csRimborsi sottoscrizioni + bozza TD04
Flo.BE/Services/Payments/BookingPaymentBackgroundService.cs / CommerceOrderBackgroundService.csCicli in background ogni 30 secondi
Flo.BE/BoundedContexts/EInvoicing/Controllers/FattureController.csReplica fatture, compositore, import, resend, sync, PDF
Flo.BE/BoundedContexts/EInvoicing/Controllers/EInvoicingSettingsController.csCRUD impostazioni e probe di connessione
Flo.BE/BoundedContexts/EInvoicing/Services/EInvoicingSettingsService.csCredenziali cifrate, default cedente
Flo.BE/BoundedContexts/EInvoicing/Services/FattureService.csMacchine a stati emit / import / resend / sync
Flo.BE/BoundedContexts/EInvoicing/Services/EInvoiceReconcileService.csCiclo di riconciliazione SDI ogni 5 minuti
Flo.BE/BoundedContexts/EInvoicing/Services/EmissionStates.csInsiemi esiti SDI confermati vs deterministici
Flo.BE/BoundedContexts/EInvoicing/Services/Gateway/Client FatturaApi e OpenAPI.it
cli/src/commands/instance/payments.tsComando operatore flo instance payments