Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Per SSO si intende la possibilità per il cittadino di fare login sull'area personale o su un servizio digitale e non dovrelo ripetere se si sposta su un altro servizio digitale dello stesso Ente.
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
In questa area della documentazione si fornisce una descrizione mediante diagrammi dell'architettura logica della piattaforma.
Questo documento è rivolto a sviluppatori, tecnici e Partner Tecnologici ed è volto a fornire informazioni tecniche sull'architettura, le integrazioni, le interfacce disponibili nella piattaforma.
La piattaforma OpenCity Italia semplifica la realizzazione e la gestione di servizi digitali per cittadini e imprese, anche da smartphone (art. 7 CAD).
OpenCity Italia è open source, scaricabile da Developers Italia o disponibile come servizio pronto all'uso (Software as a Service).
La piattaforma è sviluppata con l'obiettivo di erogare servizi per centinaia di Enti contemporaneamente da una singola installazione, per questo motivo il codice della piattaforma è organizzato in microservizi indipendenti tra loro e il dialogo tra gli stessi è realizzato mediante stream di eventi piuttosto che mediante chiamate dirette alle API.
Per queste scelte l'installazione e la gestione della piattaforma in un ambiente di produzione richiede un ampio ventaglio di conoscenze:
Sistema Operativo GNU/Linux e uso della command line (CLI)
database PostgresQL, MongoDB, Redis
Docker e sistemi di orchestrazione di container
Kafka e KsqlDB
Per contribuire alla piattaforma, a seconda dell'area su cui si desidera sviluppare sono necessarie cometenze nei seguenti linguaggi di programmazione:
PHP
NodeJS
Python
Golang
Infine la piattaforma è in costante evoluzione e nuovi componenti vengono aggiunti o prendono il posto di componenti esistenti divenuti obsoleti.
Un'architettura di microservizi è costituita da un insieme di servizi ridotti autonomi.
Ogni servizio è autonomo e deve implementa una singola funzionalità all'interno di un contesto delimitato. Un contesto delimitato è una divisione naturale all'interno di un Ente e fornisce un limite esplicito all'interno del quale esiste un modello di dominio.
Ogni microservizio della piattaforma è distribuito come docker container, ha un proprio versionamento ed espone una o più porte per comunicare con l'esterno.
I microservizi sono esposti all'esterno da un web router che assolve solitamente anche il ruolo di terminatore di SSL.
Ad ogni release della piattaforma vengono aggiornati uno o più microservizi.
Un'architettura guidata dagli eventi è costituita da producer eventi che generano un flusso di eventi e consumer eventi che sono in ascolto degli eventi.
Streaming eventi: gli eventi vengono scritti in un log. Gli eventi sono rigorosamente ordinati (in una partizione) e durevoli. I client non sottoscrivono il flusso, ma un client può invece leggere da qualsiasi parte del flusso. Il client è responsabile di far avanzare la propria posizione nel flusso. Questo significa che un client può aggiungersi in qualsiasi momento e può riprodurre gli eventi.
Elaborazione del flusso di eventi. Usare una piattaforma di flussi di dati come Apache Kafka, come pipeline per inserire gli eventi e fornirli agli elaboratori di flussi. Gli elaboratori di flussi intervengono per elaborare o trasformare il flusso. Possono essere presenti più elaboratori di flussi per sottosistemi diversi nell'applicazione.
Il sistema è costituito da un core, che assolve ai compiti principali e da alcuni sottosistemi dedicati a compiti specifici: gestione dei pagamenti, protocollazione, sistema di analytics.
Il componente principale, il core, è ovviamente il frutto del lavoro dei primi anni di sviluppo della piattaforma, ed assolve ai compiti principali della stessa:
Definizione e organizzazione dei Servizi Digitali
Definizione dei Moduli on-line
Gestione delle Pratiche inviate dai cittadini e dei messaggi
Gestione degli utenti
Gestione Uffici e appuntamenti on-line su slot di tempo e orari di apertura.
Gestione Sale pubbliche e altre risorse a prenotazione flessibile
Il core assolve anche al compito di interfaccia utente, sia per i cittadini che per gli operatori e gli amministratori degli Enti.
All'interno del core sono presenti alcune integrazioni con sistemi di protocollo e di intermediari di pagamento PagoPA: si tratta delle prime integrazioni realizzate quando lo sviluppo era di tipo monolitico, successivamente sono stati creati i sottosistemi dedicati alle integrazioni con Protocolli e Pagamenti. In futuro probabilmente queste integrazioni verranno riscritte come microservizi indipendenti, alleggerendo le responsabilità del core e guadagnandone anche la semplicità del codice e la sua manutenibilità.
Il core è costituito dai seguenti microservizi:
Caddy / Symfony App (dal 2023 i due servizi sono distribuiti in una immagine unica: registry.gitlab.com/opencontent/stanza-del-cittadino/core/app
)
Nota
Form Server, implementazione del server di Form.IO: registry.gitlab.com/opencontent/stanza-del-cittadino/form-server
Form Builder, un editor multi-tenant con accesso riservato agli amministratori della piattaforma per l'editing dei componenti condivisi dei form degli Enti: registry.gitlab.com/opencontent/stanza-del-cittadino/formbuilderjs
Varnish, un cache proxy server che incrementa l'affidabilità e la scalabilità del formserver e del catalogo dei servizi: registry.gitlab.com/opencontent/varnish:latest
Gotenberg, a Docker-powered stateless API for PDF files: gotenberg/gotenberg:7
Payment Updates, ascolta gli eventi relativi ai pagamenti e aggiorna le pratiche di conseguenza: registry.gitlab.com/opencontent/stanza-del-cittadino/payment-updater
Signature Check, api usata dal componente sdc_upload di form.io per validate le firme dei documenti firmati .p7m: geopartner/signcheckwebapi:latest
Per un esempio completo di deploy dei microservizi si veda il file docker-compose.yml
nel repositoy del core, che fornisce una configurazione funzionante della piattaforma.
L'immagine Caddy / Symfony App viene usata sia per esporre la UI e le API, sia per l'esecuzione dei cron jobs, per un esempio si veda il file docker-compose
usato in ambiente di sviluppo..
Utilizza inoltre i seguenti servizi di persistenza:
PostgreSQL, per la gestione di tutti i dati e delle configurazioni della piattaforma, ad eccezione delle form. La versione usata in sviluppo è la 11 e comprende anche l'estensione PostGIS: postgis/postgis:11-3.3-alpine
MongoDB, per la persistenza dei moduli realizzati con l'editor di Form.IO: mongo:4.2
Redis, per la gestione delle sessioni degli utenti e della cache applicativa: redis:5-alpine.
Riguardo a coding, contribuzioni, eventi, API
Nella piattaforma si fa ampio uso del Pattern è quindi fondamentale che il formato degli eventi sia stabile e documentato.
Per ogni evento ci aspettiamo almeno i seguenti campi:
Campo | Valore | Formato | Annotazioni |
---|
Attenzione: se si genera un evento, a partire da un evento esistente, si sta creando una nuova entità che avrà i propri valori event_id
, event_created_at
, event_version
, app_id
: non si deve mai ricopiare questi valori dall'evento originario.
I consumatori devono essere sempre attenti alla versione di eventi che supportano: la versione deve essere esplicitamente supportata ed eventi di una versione non supportata devono essere scartati
Nei log è possibile dare evidenza del fatto che si è incontrato un evento non supportato, ma non va loggato come errore è sufficiente dare l'informazione a livello DEBUG.
Le date vanno sempre espresse nello standard ISO8601, in particolare, le date non devono contenere i millisecondi, e devono contenere il formato della timezone Europe/Rome.
Es: 2022-06-22T15:11:20+02:00
Le collezioni devono essere facilmente navigabili, per questo abbiamo adottato il seguente standard:
Le GET sulle risorse devono supportare i query parameters convenzionali:
sort
offset
limit
Su alcuni campi è utile implementare anche l'opzione di paginazione cursor-based:
GET /payments?created_since=$timeStamp
Facciamo in generale riferimento alle :
Fare riferimento alla
| identificativo unico dell'entità oggetto dell'evento | UUID | Es: in un pagamento è l'ID del pagamento, in una pratica è l'application_id etc... |
| identificativo unico dell'evento | UUID | Non deve esserci per nessun motivo due volte lo stesso ID |
| versione dell'evento | Integer or Float | Se il formato è solo un intero (Es: 1, 2, etc..) cambia ad ogni singolo cambiamento. Se si usa un float |
| data dell'evento | ISO8601 | Ricordarsi di usare il formato della timezone Europe/Rome (Es: |
| Nome e versione dell'applicativo che ha generato l'evento |
| Utile per motivi di debug (Es: payment-dispatcher:1.0.15). |
In questa sezione viene descritto in dettaglio il json del pagamento e la definizione dettagliata dei campi
L’area personale di OpenCity Italia è progettata per integrarsi con vari intermediari di pagamento utilizzati da Comuni ed enti pubblici. Questa sezione descrive i requisiti tecnici necessari per sviluppare e implementare l'integrazione tra la piattaforma e l'intermediario di pagamento PagoPA.
Le interazioni avvengono in seguito ad alcune azioni da parte dei cittadini e degli operatori:
Invio di una pratica da parte del cittadino
Approvazione di una pratica da parte di un operatore nel caso di un pagamento posticipato
Importazione di dovuti da parte dell'operatore mediante API file csv
Pagamento di un dovuto da parte del cittadino a seguito dell'importazione di quest'ultimo
Per sviluppare l'integrazione con l'intermediario di pagamento PagoPA, sono richiesti i seguenti elementi:
Documentazione Tecnica: Manuali, guide e riferimenti API.
Endpoint/Ambiente di Test: URL degli endpoint e accesso a un ambiente di test per effettuare chiamate API.
Modalità di Verifica dello Stato del Pagamento: Specifiche delle modalità di verifica dello stato del pagamento, tramite polling o notifica.
Per integrare OpenCity Italia Area Personale con l’intermediario di PagoPA, sono necessari i seguenti parametri minimi:
Parametri per la creazione di un pagamento
Parametri per la creazione di una Marca da Bollo Digitale
Restituzione del numero IUV e/o del numero avviso in fase di creazione del pagamento
Parametri per scaricare l’avviso di pagamento cartaceo
Parametri per scaricare la ricevuta telematica una volta effettuato il pagamento
API per il download degli importi dovuti (se disponibile)
L’intermediario di pagamento deve fornire documentazione dettagliata delle API, comprensiva di:
Endpoint
Metodi supportati (GET, POST, etc.)
Parametri richiesti e facoltativi
Formato dei dati (JSON, XML)
Esempi di richieste e risposte
Per garantire le comunicazioni fra la piattaforma e l’intermediario di pagamento, è necessario sbloccare gli IP delle chiamate entranti verso gli endpoint dell’intermediario di pagamento. Gli IP di OpenCity da sbloccare sono:
93.41.234.251/32
78.47.94.152/32
54.220.150.231/32
63.33.155.248/32
37.186.144.203/32
Per risolvere eventuali complicazioni, errori o bug durante l'integrazione, è fondamentale avere un contatto tecnico dedicato. Si prega di fornire i seguenti dettagli del contatto tecnico:
Nome
Telefono
Disponibilità (orari di lavoro)
Tutti i microservizi che compongono la piattaforma devo rispettare i seguenti standard e regole generali
Tutti i servizi devono rispettare i 12factor, descritti in modo anche più dettagliato nell'articolo An illustrated guide to 12 Factor Apps
Dockerfile
e docker-compose.yml
devono essere presenti nella root del repo (.dockerignore
se serve) per facilitare l'avvio in locale dell'applicativo e farne saggiare l'utilizzo;
se necessario usare un docker-compose.dev.yml
per aggiungere tool utili per lavorare in locale durante lo sviluppo.
il servizio deve gestire correttamente i segnali inviati da docker SIGTERM e SIGKILL per fare uno shutdown pulito e senza interrompere l'esecuzione in fasi critiche per la consistenza dei dati.
la configurazione *deve avvenire mediante variabili d'ambiente
la configurazione può essere fatta anche tramite un file di environment (.env
) o parametri da cli
se http, deve essere presente un /status
entrypoint che risponde 200 se va tutto bene, uno status code diverso in caso contrario
se non http, l'healthcheck può essere un controllo su un file o su un processo
l'health check, se disponibile, deve essere inserito nel Dockerfile
Il log deve supportare almeno tre livelli: debug
, info
e error
.
A debug
deve essere comprensibile il flusso logico dell'applicativo, a info
ci si aspetta di leggere 1 riga per ogni evento o chiamata ricevuta dall'applicativo, a error
invece non si deve vedere nulla se non ci sono errori: per capirsi, a ERROR, il rate dei log che si alza dovrebbe essere indice che ci sono problemi per i quali è importante attrarre l'attenzione degli amministratori.
Se sono previste chiamate http, deve avere un log stile webserver, con indicazione dell'IP chiamante (con supporto di X-Forwarded-
headers, timestamp, path servito, etc... fate riferimento allo standard ncsa o apache log format, in tutti i framework ci sono librerie che li producono gratis.
Non mi schiare log http e log multiriga (stacktrace o similari): per esempio inviare lo stacktrace a stderr
e riservare stdout
ai log http
Se possibile implementare log in formato json, ma solo se si può produrre solo del JSON, mischiare testo semplice e JSON non è di grande utilità.
sentry support
configurazione di sentry sul repo gitlab
se http, deve esistere l'entrypoint /metrics
che espone metriche in formato prometheus.
un servizio può mostrare metriche sulle chiamate (status code, timing), anche se solitamente le possiamo prendere altrove (traefik), - un servizio deve mostrare metriche specifiche dell'app, in particolare delle condizioni di errore (counters) e dei tempi di risposta di eventuali servizi esterni richiamati da questo (histograms).
sui sistemi di deploy e orchestrazione che usiamo ci sono sempre dei modi di eseguire task stile cron, inutile reimplementare quella logica dentro la propria App.
è indispensabile invece rendere il proprio software idempotente: deve essere rieseguibile più volte sugli stessi dati senza fare danno.
il modo più semplice per implementare una chiamata a tempo è implementare un comando da cli che possa essere eseguito con la stessa immagine con cui gira l'applicativo.
in docker c'e' sempre un mapping tra esterno e interno, inutile parametrizzare questi aspetti in modo particolarmente flessibile.
linting del codice e enforcemente dei coding styles
pubblicazione su Developers Italia
La piattaforma consente la creazione di servizi contenenti pagamenti. I pagamenti possono essere in forma di bolli, che non richiedono una integrazione con un sistema esterno (il cittadino registra soltanto i numeri di marche da bollo che ha acquistato in precedenza), o in forma di pagamento PagoPA.
I pagamenti PagoPA vengono creati e aggiornati mediante l'integrazione con uno degli intermediari di pagamento di PagoPA.
La piattaforma può offrire sia una esperienza completa al cittadino o all'impresa, che consente l'invio di pratiche e la loro gestione nel backend, sia farsi carico di uno solo di questi aspetti, lasciando ad altri sistemi il ruolo di interfaccia con il cittadino o a sistemi verticali il compito di gestire pratiche (pratiche edilizie o in ambito sociale)
In presenza di questa esigenza è importante che la piattaforma e il sistema terzo siano in grado di instaurare e mantenere nel tempo la relazione tra le pratiche, mantenere aggiornati lo stato della pratica o di un pagamento pendente per la stessa.
Vari strumenti nella piattaforma consentono di mantenere aggiornate le informazioni e ricevere aggiornamenti:
API ReST
Webhook
Prima di entrare nei dettagli di questi strumenti merita avere uno sguardo di insieme del flusso della pratica e di tutti gli stati che può attraversare. Nel grafico che segue il tratto continuo più forte rappresenta il flusso più frequente di una pratica, il tratto punteggiato rappresenta i flussi alternativi. I cerchi costituiscono gli stati finali di una pratica
Per una visione generale di tutti gli stati che una pratica può assumere e quali sono i cambi di stato ammessi segue un grafico più generale. Nel grafico sono evidenziati in azzurro gli stati fondamentali della pratica, mentre in grigio gli stati deprecati.
Sono un meccanismo di comunicazione tra sistemi che consentono una comunicazione in tempo reale tra due API
Le API non sono sufficienti per realizzare una buona integrazione tra due sistemi. Se due o più sistemi si devono aggiornare reciprocamente in base agli eventi che avvengono al proprio interno, è necessario che periodicamente ogni sistema interroghi gli altri per sapere se il loro stato è cambiato: questo approccio si chiama anche polling. Questo è molto oneroso e introduce un ritardo nell'aggiornamento che è tanto maggiore quanto maggiore è l'intervallo con cui un sistema interroga gli altri. D'altra parte se di intensifica la frequenza del polling si avranno maggiori costi a fronte di aggiornamenti magari poco frequenti.
Un modo più efficiente di far dialogare due sistemi è avviare una comunicazione in occasione di cambiamenti rilevanti per il sistema in ascolto e per ottenere questo, eliminando del tutto la necessità di fare polling, è possibile usare i Webhook.
Ogni webhook consente di inviare un aggiornamento a una API di un altro sistema che sta ascolto, in base agli eventi che si desidera comunicare.
Per fare test sui webhook si consiglia di utilizzare un servizio come Request Inspector.
Le API consentono di leggere e modificare informazioni sulla piattaforma in modo programmatico.
Una API ReST, nota anche come API RESTful, è un'interfaccia di programmazione delle applicazioni (API o API web) conforme ai vincoli dello stile architetturale ReST, che consente l'interazione con servizi web RESTful. Il termine REST, coniato dall'informatico Roy Fielding, è l'acronimo di REpresentational State Transfer.
La documentazione delle API ReST della piattaforma è pubblicamente disponibile in ogni Ente all'indirizzo:
Ad esempio per il comune demo della piattaforma è possibile consultarle alla pagina:
Alcune API richiedono un'autenticazione prima di essere chiamate, in questo caso un cittadino o un operatore possono usare le proprie credenziali per ottenere un token di autenticazione come segue:
POST
https://servizi.comune.bugliano.pi.it/lang/api/auth
Con questa chiamata si riceve un token di autenticazione in formato JWT, che dovrà essere inviato per tutte le successive chiamate che richiedono autenticazione come bearer token nell'header Autorization
Alcuni esempi di chiamate
Esempio di chiamata da linea di comando con il client curl:
Le API sono disponibili in due versioni, la "1" e la "2". Le capacità delle API sono le stesse per le due versioni, nella versione 2 c'e' una migliore gestione del formato JSON, quindi si consiglia l'uso della versione più recente.
È possibile specificare la versione delle Api che si vanno ad interrogare, questo è possibile in 2 modi differenti:
tramite parametro version
da passare come parametro delle GET
tramite parametro X-Accept-Version
da specificare nell' header della richiesta
La versione di default è per retrocompatibilità la 1
Dalla versione 2 le chiavi dei valori del campo data
delle applications
non sono più restituite come un insieme di strighe piatto separato dal punto (.) ma come campo json con le chiavi esplose.
Nota bene: si raccomanda di specificare sempre la versione di API utilizzata, anche se è il default (1), perche' in futuro il default potrebbe cambiare.
La configurazione dei pagamenti avviene interrogando delle API apposite fornite dal proxy di riferimento. Quest'ultimo fornisce inoltre un form come json schema sviluppato con form.io mediante il quale l'utente inserisce tali configurazioni.
Per creare un form utilizzare il seguente builder:
Panoramica dei ciclo di funzionamento dei pagamenti di Opencity Italia - Area Personale.
Un pagamento nasce da una interazione con il cittadino: il cittadino presenta una pratica per la quale l'Ente richiede un pagamento.
Il cuore dell'integrazione con un intermediario di pagamento è un microservizio apposito che dialoga con l'intermediario da un lato, implementando le chiamate specifiche in protocollo ReST o SOAP, e con il core della piattaforma dall'altro lato, mediante la lettura e la scrittura su un topic di Kafka.
In questo momento i pagamenti hanno una relazione 1 a 1 con le pratiche, di conseguenza ci sono due servizi che fanno da intermediari tra le pratiche e i pagamenti, ovvero il payment dispatcher e il payment updater.
È l’interfaccia dalla quale l’utente usufruisce di un servizio digitale, inserendo i dati di una pratica attraverso un modulo: se il servizio prevede un pagamento, i dati di questo sono presenti nella descrizione JSON della pratica e saranno utilizzati dal dispatcher per creare il pagamento.
È il microservizio che, a partire dall'evento di una pratica nel topic applications
, crea l'evento del pagamento nel topic payments
.
È il microservizio che sostituirà il Payment Dispatcher, esso, anziche leggere l'evento di una pratica dal topic applications, esporrà un API che, quando chiamata, crea l'evento del pagamento nel topic payments. Questo permette di avere una soluzione unica per gestire tutti i flussi di pagamento della piattaforma, quindi sia pagamenti creati a partire dalle pratiche, che i pagamenti importati tramite csv o API esterne.
Il microservizio legge gli eventi dal topic payments
e contestualmente aggiorna lo stato della pratica cambiando lo stato da Payment pending a Inviata.
E' il microservizio che gestisce la comunicazione e l’aggiornamento della posizione debitoria con l'Intermediario di pagamento di riferimento. Esso gestisce inoltre il salvataggio delle configurazioni tramite le quali può interagire con l'intermediario di pagamento di riferimento. Queste configurazioni sono inserite dall'operatore mediante l'interfaccia del Core, il quale comunica con il payment proxy mediante delle apposite API che quest'ultimo serve.
E' un microservizio che ascolta dal topic payments e crea un database interrogabile con SQL contenente tutti i pagamenti in stati non definitivi.
E' il microservizio che monitora lo stato dei pagamenti: periodicamente legge su KSQL l'elenco dei pagamenti aperti e invia per ognuno di questi una chiamata alla "update.url" specificata nel pagamento stesso: questa url è una url del proxy stesso che consente al proxy di interrogare l'Intermediario per avere lo stato aggiornato del pagamento.
Questo microservizio si occupa di centralizzare il pagamento online dei dovuti in un'unica API, ovvero quella del checkout di pagoPA, in questo modo si astrae dall'intermediario con cui ci si deve integrare facilitando quindi lo sviluppo dell'integrazione e omogeneizzando l'esperienza di pagamento del cittadino, che diventa quindi indipendente dall'intermediario tramite qui andrà a pagare.
Dettaglio degli stati che attraversa un pagamento nel suo ciclo di vita
STATO PAGAMENTO | DESCRIZIONE | NOTE |
---|
L'admin, dall'interfaccia di configurazione dei pagamenti della Stanza del Cittadino compila la configurazione mediante una form, il cui json schema è servito dall'API /tenants/schema
Lo schema della form sopra riportata è il seguente
Premendo poi il bottone Salva, viene eseguita una POST /tenants
servita dal proxy, con payload
Per modificare una configurazione esistente, il proxy serve l'API PUT /tenants/{tenant_id}
e PATCH /tenants/{tenant_id}
Per eliminare una configurazione esistente, il proxy serve l'API DELETE /tenants/{tenant_id}
. In questo caso l'eliminazione è una soft-delete, ovvero la configurazione viene semplicemente disattivata settando il parametro active
a false
ed eliminando la configurazione dalla memoria ma non dallo storage.
L'admin, dall'interfaccia di configurazione dei pagamenti per un servizio compila la configurazione mediante una form, il cui json schema è servito dall'API /services/schema
Lo schema della form soprariportata è il seguente
Premendo poi il bottone Salva, viene eseguita una POST /services
servita dal proxy, con payload
Per modificare una configurazione esistente, il proxy serve l'API PUT /services/{service_id}
e PATCH /services/{service_id}
Per eliminare una configurazione esistente, il proxy serve l'API DELETE /services/{service_id}
. In questo caso l'eliminazione è una soft-delete, ovvero la configurazione viene semplicemente disattivata settando il parametro active
a false
.
Le configurazioni di tenant e servizi vengono salvate con la seguente alberatura
root
|____tenant_id_1
| |____tenant.json
| |____service_id_1.json
| |____service_id_2.json
| |____.....
| |____service_id_n.json
|____tenant_id_2
| |____tenant.json
| |____service_id_1.json
| |____service_id_2.json
| |____.....
| |____service_id_n.json
|____tenant_id_n
|____tenant.json
|____service_id_1.json
|____service_id_2.json
|____.....
|____service_id_n.json
Molti aspetti implementativi dipendono più dalla normativa che da nostre scelte progettuali, in questa pagina si evidenziano gli uni e le altre.
Sono requisiti che dipendono da nostre scelte implementative, possono cambiare man mano che evolviamo la piattaforma, per supportare nuovi casi d'uso (pagamento dei dovuti) oppure perché ci accorgiamo che abbiamo commesso qualche errore nella progettazione.
Gestione di pagamenti spontanei
Gestione di pagamenti con bilancio
Gestione della notifica dello stato del pagamento da parte dell'IdP - se disponibile - o del polling se non è disponibile la notifica
Se è implementato il polling, questo deve avvenire con un esponenziale che assicuri alcuni check nel giro di pochi minuti e poi si diradi fino a un massimo di un check al giorno fino alla scadenza del pagamento o 1 anno se la scadenza è inferiore.
Gestione delle configurazioni a livello di Tenant e di Servizio
Avere una specifica in formato OpenAPI v3
Rispettare la : in particolare per quanto riguarda
(4.2)
(4.3) optando per la scelta di snake_case
per i nomi degli attributi
logging (4.4)
risultare valido nel
Sappiamo che in molti topic ci sono diversi eventi mal formati, per vari errori e assenza di uno schema registry, quindi è necessario validare gli eventi in input e opzionalmente in output (lo fanno le nostre prime implementazioni in python, ma è un requisito?)
Ci aspettiamo che se di dovesse aggiornare il formato dell'evento nel topic payments
gestiremo eventi anche in parallelo con versioni distinte, quindi ogni proxy deve Interpretare solo gli eventi che riportano il formato più recente dell'evento nel topic payments: event_version: 2.0
counter sul numero di pagamenti creati
counter sul numero di errori durante il processamento di pagamenti, differenziando gli errori interni (errori di formato dati in ingresso o validazione) da errori esterni (errori di dialogo con l'IdP)
latenza delle chiamate fatte all'IdP
Di seguito la tabella contenente le specifiche di ogni metrica
Questi proxy condividono alcune scelte implementative grazie a un apposito python-sdk sviluppato in parallelo ai proxy stessi:
le configurazioni sono salvate su disco locale o s3 secondo un albero ben definito tenant->servizio
i singoli pagamenti vengono salvati su storage fino a che il pagamento è pendente, così da poter fare il polling in autonomia.
Name | Type | Description |
---|
Esempio di chiamata da linea di comando con il client :
Campo | Tipo | Obbligatorio | Validazione |
---|
Campo | Tipo | Obbligatorio | Validazione |
---|
Campo | Tipo | Obbligatorio | Validazione |
---|
Campo | Tipo | Obbligatorio | Validazione |
---|
Campo | Tipo | Obbligatorio | Validazione |
---|
Campo | Tipo | Obbligatorio | Validazione |
---|
Campo | Tipo | Obbligatorio | Validazione |
---|
Integrarsi con per la gestione degli errori
Esporre metriche di monitoraggio in , in particolare:
Metrica | Labels | Descrizione |
---|
Inoltre il servizio deve rispettare gli in particolare per quanto riguarda la gestione dello storage: non possiamo avere vendor-lockin sulle tecnologie, quindi dobbiamo essere compatibili con file-system posix tradizionale, NFS share e almeno i due cloud-storage più diffusi S3 e Azure Blob.
EFIL:
IRIS:
MYPAY:
PMPAY:
username* | String | nome utente |
password* | String | password |
| UUID |
| UUID |
| string(50) | Ogni proxy deve implementare un tipo di pagamento. Se per esempio, il proxy in questione gestisce pagamenti pagopa allora il campo va validato verificando che esso sia valorizzato a |
| UUID |
| UUID |
| Datetime | ISO8601 |
| Datetime | ISO8601 |
| Enum | Valori permessi: CREATION_PENDING CREATION_FAILED PAYMENT_PENDING PAYMENT_STARTED PAYMENT_CONFIRMED PAYMENT_FAILED NOTIFICATION_PENDING COMPLETE EXPIRED |
| string(140) | Causale del pagamento. Non può eccedere i 140 caratteri. |
| UUID |
| PaymentData |
| Links |
| Payer |
| UUID |
| string(10) | L'attuale versione dell'evento deve essere "1.0". Ogni proxy deve validare la versione dell'evento in modo da sapere se processarlo o meno. |
| Datetime | ISO8601 |
| string(100) | Valorizzarlo nel formato |
| string(255) | Fornito dall'intermediario di pagamento. La lunghezza dunque può essere variabile |
| Datetime | ISO8601 |
| Datetime | ISO8601 E' già valorizzato a priori, quindi non viene gestito dal proxy |
| float | E' già valorizzato a priori, quindi non viene gestito dal proxy |
| string(3) | ISO4217 |
| string(50) |
| string(50) |
| string(50) |
| jsonArray |
| UrlData |
| UrlData |
| UrlData |
| UrlData |
| List[Notify] |
| Update |
| string |
| Datetime | ISO8601 |
| Enum | Valori permessi: GET POST |
| string |
| Enum | Valori permessi: GET POST |
| Datetime | ISO8601 |
| string |
| Datetime | ISO8601 |
| Datetime | ISO8601 |
| Enum | Valori permessi: GET POST |
| Enum | Valori permessi: human legal |
| string(255) |
| string(255) |
| string(255) |
| string(255) |
| string(255) |
| string(255) |
| string(255) |
| string(2) | ISO 3166-2 |
| string(2) | ISO 3166-1 alpha-2 |
| string(255) |
| cluster, env, app_name | la metrica deve misurare gli errori di validazione sull'evento letto (es. l'importo è una stringa invece che un float) |
| cluster, env, method, app_name, status_code | la metrica deve monitorare le chiamate http indicandone lo status code |
| cluster, env, app_name | la metrica deve misurare solo gli eventi per cui il proxy ha una configurazione e che stati processati con successo |
| cluster, env, app_name | la metrica deve misurare gli eventi di pagamento validi di cui però è fallito il processing per qualsiasi motivo (escluso il caso in cui non esiste una configurazione per esso) |
| cluster, env, app_name | la metrica deve misurare gli eventi di pagamento validi di cui però è fallito il processing a causa di un errore sul provider |
| cluster, env, app_name | la metrica deve misurare gli eventi di pagamento validi di cui però è fallito il processing per errori interni al codice |
| cluster, env, app_name | istogramma che mostra la distribuzione di latenza delle risposte del provider |
Il campo "retry_meta" è stato creato dal protocol proxy e contiene il topic di origine ed eventuali campi utili al protocol proxy per tenere lo stato della protocollazione
Le API fornite nella documentazione dell'intermediario vengono generalmente testate in prima battuta via Postman/Insomnia nel caso di chiamate REST o via SoapUI nel caso di chiamate SOAP
A seguito dei test delle chiamate, si procede con l'implementazione del microservizio. Per testarlo localmente fare riferimento al seguente docker-compose.yml
Se si sta facendo il deploy di un nuovo microservizio per la prima volta o si sta aggiungendo una nuova API o una nuova pagina a una interfaccia esistente, è necessario aggiungere alcuni controlli di qualità minima prima del rilascio.
Test flusso standard
Inserire la configurazione del tenant
Inserire la configurazione del servizio
Inserire un esempio di pagamento in stato CREATION_PENDING
nel topic payments
e verificare che venga correttamente creato il debito
Chiamare l'API /offline-payment/{id}
e verificare che venga scaricato correttamente l'avviso di pagamento cartaceo
Chiamare l'API /online-payment/{id}
e verificare che si venga rediretti al portale di pagamento
Una volta arrivati in fondo al pagamento online, verificare che premendo il bottone "Torna alla Home" venga chiamata correttamente l'API /landing/{id}
Chiamare l'API /update/{id}
e verificare che venga correttamente controllato lo stato del pagamento ed eventualmente aggiornato e prodotto un evento sul topic payments
Chiamare l'API /receipt/{id}
e verificare che, a pagamento avvenuto, venga scaricata correttamente la ricevuta telematica
Inserire un esempio di pagamento in stato PAYMENT_PENDING
che è già stato processato precedentemente dal proxy e verificare che venga ignorato
Inserire un esempio di pagamento in stato PAYMENT_PENDING
che non è già stato processato precedentemente dal proxy e per cui esiste una configurazione sullo storage, e verificare che venga salvato correttamente sullo storage (caso importazione dovuti)
Test flusso di errore
Modificare la configurazione del servizio in modo che sia errata (mettendo ad esempio credenziali errate)
Inserire un esempio di pagamento in stato CREATION_PENDING
nel topic payments e verificare che, a seguito del fallimento, venga prodotto un evento in stato CREATION_FAILED
nel topic payments
I pagamenti sono testabili sul nostro ambiente di qa: https://servizi.comune-qa.bugliano.pi.it/.
Si potrà accedere come utente SPID utilizzando l'utente AGID TEST DEMO.
Si potrà accedere come admin per eventualmente modificare il servizio di test e la relativa configurazione di pagamento
Si potrà accedere come operatore per testare pagamenti posticipati, i quali richiedono la presa in carico della pratica e l'approvazione di quest'ultima affinchè l'utente possa procedere con il pagamento.
La generazione di documenti varia a seconda del tipo di pratica che deve essere registrata o gestita. Ci sono diverse tipologie di pratica, ognuna delle quali corrisponde a uno stato specifico della procedura:
Creazione di una pratica da parte o per conto di un cittadino (application-request): questo si riferisce alla creazione iniziale di una pratica da parte di un cittadino o da parte di qualcuno agendo per conto del cittadino.
Invio di una richiesta di integrazioni da parte di un operatore (integration-request): questo si verifica quando un operatore richiede ulteriori informazioni o documenti relativi a una pratica.
Invio di un'integrazione da parte del cittadino (integration-response): In questo caso, il cittadino risponde alle richieste di integrazione inviando ulteriori documenti o informazioni.
Approvazione/rifiuto di una pratica da parte di un operatore (application-outcome, application-revocation): questo rappresenta l'atto di approvare o rifiutare una pratica da parte di un operatore.
Ritiro di una pratica da parte di un cittadino (application-withdraw): un cittadino può ritirare una pratica in qualsiasi momento.
Queste tipologie rappresentano diversi stati o fasi attraverso cui una pratica può passare durante il suo ciclo di vita. Poiché una pratica può attraversare più di uno di questi stati, ciò significa che saranno generati diversi documenti associati alla stessa pratica, ognuno dei quali corrisponderà a una fase specifica del processo.
La piattaforma è responsabile della gestione della protocollazione dei documenti derivanti dai diversi processi burocratici che essa offre. Il documento generato è conforme alle linee guida stabilite dall'AGID(si veda la sezione Documento digitale). Esso è soggetto a protocollazione in determinati casi. Tale processo viene eseguito attraverso l’integrazione con un sistema di protocollazione esterno. Nella seguente sezione verrà esaminato in dettaglio come tale integrazione deve essere fatta, descrivendo gli attori, i componenti coinvolti e le loro interazioni.
L’area personale del cittadino effettua diverse interazioni con il sistema di protocollo, ma tutte seguono una logica comune. Le interazioni avvengono in seguito ad alcune azioni da parte cittadini e degli operatori:
invio di una pratica da parte del cittadino
invio di una richiesta di integrazioni da parte di un operatore
invio di una integrazione da parte del cittadino
approvazione/rifiuto di una pratica
Altre azioni possibili, mediante configurazioni avanzate del servizio:
creazione di una pratica da parte di un operatore per conto di un cittadino
ritiro di una pratica da parte del cittadino
annullamento di una pratica da parte di un operatore
Per ognuna di queste operazioni viene prodotto un documento principale dotato di eventuali allegati.
Per ogni documento ci aspettiamo di poter sempre specificare:
Un oggetto
Un mittente (il soggetto che ha formato il documento)
Un destinatario (solitamente l’ufficio che deve prendere in carico la richiesta)
Data di registrazione della pratica
Tipo di trasmissione (in entrata, in uscita, interno)
Classificazione (solitamente sotto forma di indice di classificazione nell’albero del titolario, es 6.3.4)
Per ogni documento protocollato ci aspettiamo di ricevere i seguenti dati:
Il Numero di protocollo (un identificativo unico e persistente)
Data di registrazione nel protocollo
(opzionale) Una impronta digitale del documento protocollato
Tutti i documenti di una pratica sono raccolti in un fascicolo per il quale ci aspettiamo di poter specificare
L’oggetto
(opzionale) La classificazione
Per ogni integrazione sono necessarie solitamente le seguenti chiamate API o webservice:
Ricerca di un documento per Numero di protocollo
Invio di un Documento principale
Invio di allegati al documento principale
Protocollazione di un documento e dei suoi allegati: se disponibile, preferiamo avere una chiamata atomica, che protocolla il documento principale e i suoi allegati.
Ricerca di un fascicolo per Numero di fascicolo
Creazione di un fascicolo
Aggiunta di un documento ad un fascicolo esistente
Condivisione documentazione:
Documentazione delle API o dei WebService
ambiente di test (endpoint, credenziali di accesso)
Esempio di chiamate necessarie per fare una protocollazione di un documento e dei suoi allegati
Test delle chiamate al sistema di protocollo effettuate in modo manuale
Validazione della protocollazione manuale da parte dell’Ente
Implementazione e rilascio dell’integrazione in ambiente di Quality Assurance
Validazione su un servizio di test
Rilascio in ambiente di produzione
(opzionale) Validazione su un servizio di produzione e annullamento della protocollazione effettuata
L’ultimo test effettuato in ambiente di produzione non è strettamente necessario, ma nella nostra esperienza può rivelare differenze nella configurazione del sistema di protocollo che possono diventare bloccanti.
La piattaforma supporta il multilinguismo dal punto di vista tecnico. Le due lingue supportate sono Italiano e Tedesco
Descrizione in dettaglio di un documento generato dalla piattaforma
Secondo l'AGID, il documento informatico è la “rappresentazione informatica di atti, fatti o dati giuridicamente rilevanti" in contrapposizione al documento analogico ("rappresentazione non informatica di atti, fatti o dati giuridicamente rilevanti")".
La piattaforma genera documenti informatici a partire da diversi tipi di servizi offerti.
Il documento informatico ci permette di avere un'unica struttura dati a partire da diverse tipologie di servizi. Questo ci dà il vantaggio di dover gestire un’unica entità durante il processo di protocollazione.
Il documento generato dall’area personale del cittadino è stato progettato seguendo le linee guida suggerite dall’AGID. Esso è composto dai seguenti elementi:
Documento principale: descrive in dettaglio il servizio o l'oggetto a cui il documento si riferisce.
Allegati del documento principale: rappresentano l'insieme dei file che accompagnano la documento principale, fornendo ulteriori dettagli, dati o documentazione di supporto.
metadati: includono informazioni utili per la comprensione del contesto del documento informatico. Ci forniscono dettagli sulle informazioni di base, come la data di creazione, il mittente, il destinatario, e altri dettagli pertinenti. Alcuni di essi non possono mancare(vedi sezione seguente).
Nella nostra piattaforma il documento è implementato in forma di evento Kafka, in formato JSON, descritto dal seguente JSON SCHEMA.
Per una migliore visualizzazione si consiglia di usare un viewer online.
Ecco una tabella che rappresenta il tuo JSON Schema con le colonne Campo, Tipo, Obbligatorio e Validazione.
registration_data
folder
main_document
attachments
author
NOTA 1: per gli sviluppatori python è stata definita una libreria a cui fare riferimento per la validazione del nuovo evento, è consultabile qui.
NOTA 2: per gli sviluppatori python, il repository da cui prendere spunto per applicare le modifiche riportate in questa sezione e in quelle successive è consultabile qui.
Con la versione 2.0 aggiornata del pagamento sono stati aggiunti nuovi campi che arricchiscono la descrizione del pagamento, in particolare i campi sono:
type
: serve a distinguere tra un pagamento ordinario e una marca da bollo digitale, può avere PAGOPA
o STAMP
come valori
receiver
: definisce il beneficiario del pagamento, non è un campo obbligatorio ma può essere utilizzato nel caso di pagamenti con beneficiario differente dall'ente su cui viene creato il pagamento. Se viene valorizzato è obbligatorio valorizzare i campi receiver.tax_identification_number
e receiver.name
due_type
: indica la categorizzazione del dovuto sull'intermediario di pagamento, e viene valorizzato dalla configurazione del pagamento
pagopa_category
: indica la categorizzazione del dovuto su pagoPA, e viene valorizzato dalla configurazione del pagamento
document
: è un campo che serve a descrivere il documento su cui viene apposta la marca da bollo digitale, se viene valorizzato è obbligatorio valorizzare il campo document.hash
.
split
: con la versione 2.0 dell'evento, questo campo, che viene alimentato dal bilancio contenuto nella configurazione del pagamento, avrà un tipo ben definito, composto da
split.code
: indica l'identificativo della voce di bilancio
split.amount
: indica l'importo della voce di bilancio
split.meta
: oggetto contenente il resto dei campi presenti nella voce di bilancio
links.confirm
: è un campo che serve a contenere l'API per forzare il completamento di un pagamento, per il momento tutti i proxy mancano di questa API, ma il campo è stato aggiunto per utilizzi futuri.
links.cancel
: è un campo che serve a contenere l'API per forzare l'annullamento di un pagamento.
debtor
: in aggiunta al payer
è stato aggiunto un campo equivalente debtor
, e serve a distinguere tra il debitore e il pagatore di un pagamento, ad esempio è possibile che un cittadino paghi per conto di terzi e in questo caso e debitore e pagatore sono distinti. Per ora non viene valorizzato né utilizzato.
Punti aperti
Determinazione delle URL di ritorno: Come si gestiscono le URL di ritorno? È possibile utilizzare l'indirizzo del proxy per gestire i redirect?
Gestione dell'happy path del pagamento: Come gestire il percorso ideale del pagamento e cosa accade se l'utente chiude la pagina di pagamento e poi ritorna?
Compilazione e invio pratica: L'utente compila la pratica per il servizio e invia la richiesta al Core.
Verifica pagamenti: Il Core verifica se, nella fase attuale della pratica, è necessario effettuare dei pagamenti (es. pagamento anticipato o posticipato).
Creazione pagamenti: Il Core invia i dettagli al servizio di pagamento, che restituisce gli identificativi dei pagamenti creati. Questo passaggio è fondamentale, poiché il Core deve salvare i dettagli dei pagamenti all'interno della pratica, assicurandosi che siano coerenti con il flusso (anticipato o posticipato).
Restituzione della pratica: Una volta ottenuti gli ID dei pagamenti, il Core restituisce all'utente l'intera pratica aggiornata con gli identificativi.
Gestione degli errori nella creazione dei pagamenti:
Cosa accade se la creazione di un pagamento fallisce?
Il personale dell'ufficio preposto dovrebbe essere notificato in caso di fallimento.
Stato "Creation Pending": Quando un pagamento viene creato, si ottiene comunque un ID associato al pagamento, ma questo potrebbe essere in stato di "creation pending".
Politiche di retry: Si potrebbe considerare l'implementazione di politiche di retry, ma è necessario consultare il Product Manager (PM) per decidere la miglior esperienza utente (UX).
Ipotesi: Al secondo o terzo tentativo di retry (o dopo un timeout di 10 secondi), potremmo mostrare un messaggio d'errore all'utente, suggerendo di riprovare o di avvisare l'assistenza (es. con un link "Clicca qui e avvisa"). Questo sarebbe un errore a livello ERROR.
Errore di configurazione: Se si verifica un errore di configurazione errata, è possibile fare affidamento sui messaggi restituiti dall'intermediario. In questo caso, si può evitare il retry e notificare direttamente il responsabile (es. Lorello). Questo tipo di errore potrebbe essere classificato come FATAL.
Il sequence diagram sopra riportato rappresenta il flusso di creazione e gestione dei pagamenti in un'applicazione, con particolare attenzione al retry in caso di fallimento e alla gestione dello stato del pagamento. Il flusso si svolge come segue:
Inizio del processo: L'utente invia la pratica e il Core richiama il Payment Dispatcher API per creare il pagamento. Se la creazione ha successo, si procede, altrimenti si ritenta fino a 3 volte.
Attesa di aggiornamenti dal Read Model: Quando la creazione del pagamento va a buon fine, il Core si mette in attesa di aggiornamenti dal Read Model.
Retry gestito dal Retry Orchestrator: Se il Read Model restituisce lo stato CREATION_FAILED
, il Core delega il processo di retry al Retry Orchestrator, che gestisce i retry tramite Kafka. Il Retry Orchestrator tenterà di risolvere il problema creando il pagamento sull'intermediario esterno.
Risultati del Retry Orchestrator:
Se il retry riesce, il Retry Orchestrator segnalerà il successo al Core, che mostrerà all'utente lo stato PAYMENT_PENDING
per procedere con il pagamento.
Se i retry falliscono, l'utente riceverà un alert di errore.
Conclusione: L'utente vedrà le informazioni di pagamento in caso di successo o un messaggio d'errore in caso di fallimento, dopo aver superato il numero massimo di tentativi.
Gestione di pagamenti parziali: Se due pagamenti vanno a buon fine e uno fallisce, come dovrebbe comportarsi il frontend? Attualmente, il processo di checkout potrebbe risultare incompleto.
Invio degli ID di pagamento: Durante il checkout, il sistema riceve tutti gli ID dei pagamenti che devono essere completati in un’unica operazione.
Il sequence diagram sopra riportato rappresenta il flusso di creazione e gestione dei pagamenti posticipati in fase approvazione della pratica da parte dell'operatore. Il flusso si svolge come segue:
Creazione delle configurazioni: L'utente modifica e invia le configurazioni, che vengono temporaneamente salvate tramite il Payment Proxy. Se fallisce, l'errore viene mostrato all'utente.
Creazione del pagamento tramite Payment Dispatcher: Il Core invia la richiesta di creazione pagamento al Payment Dispatcher API. Se fallisce, viene avviato un retry per un massimo di 3 tentativi. Se dopo 3 tentativi non riesce, viene mostrato un errore all'utente.
Attesa di aggiornamenti dal Read Model: Quando la creazione del pagamento va a buon fine, il Core si mette in attesa di aggiornamenti dal Read Model, che monitora lo stato del pagamento.
Stato CREATION_FAILED
e Retry Orchestrator:
Se il Read Model restituisce lo stato CREATION_FAILED
, il Core delega il retry al Retry Orchestrator, che gestisce i retry attraverso i topic di Kafka.
Se il Retry Orchestrator completa con successo la creazione, il Read Model aggiorna lo stato del pagamento, e l'utente può procedere con il pagamento (PAYMENT_PENDING
).
Se anche il Retry Orchestrator fallisce dopo vari tentativi, viene restituito un errore al Core e un alert viene mostrato all'utente.
Output finale: Se il pagamento va a buon fine (PAYMENT_PENDING
), l'utente riceve le informazioni per procedere con il pagamento. In caso di errore persistente, viene mostrato un alert.
| pagamento in attesa di essere creato sull'IdP |
| pagamento di cui è fallita la creazione sull'IdP |
| pagamento creato sull'IdP e in attesa di essere eseguito dall'utente |
| procedura di pagamento iniziata dall'utente |
| pagamento completato a seguito di conferma dalI'dP |
| pagamento fallito a causa di scadenza del termine ultimo entro cui doveva essere eseguito | nei proxy sviluppati questo stato non è quasi mai stato utilizzato |
| pagamento annullato |
Descrizione dettagliata di tutti i passaggi che portano alla creazione e alla chiusura di un pagamento a partire da una pratica.
Essendo il sistema in questione costruito su un'architettura a eventi, è fondamentale che il payment proxy
controlli la versione dell'evento Payment (campo event_version
) prima di processarlo e dichiari con quale versione dell'evento quest'ultimo è compatibile. L'evento qui descritto ha versione 2.0
.
L'utente compila e invia la pratica, la quale viene inserita come evento nel topic applications
Un esempio di evento è il seguente, si faccia particolare attenzione al blocco payment_data
:
Il payment dispatcher legge l'evento della pratica dal topic applications
, e dopo aver controllato mediante query su KSQLDB
che il pagamento per tale pratica non sia già stato creato, estrae da esso i dati del pagamento, successivamente li riversa in un nuovo evento di tipo Payment
e lo inserisce nel topic payments
in stato CREATION_PENDING
, indicando come chiave dell'evento il valore del campo service_id
. Da qui in poi solo il proxy si occuperà di scrivere sul topic payments
.
Il proxy legge dal topic payments
e, se ha una configurazione attiva per quel pagamento (quindi se l'evento letto ha un tenant_id
e un service_id
per cui ha una configurazione salvata nello storage del proxy), crea una posizione debitoria sull'Intermediario di riferimento.
È possibile che la configurazione relativa al pagamento letto dal topic payments
abbia configurato al suo interno una marca da bollo digitale. In tal caso, sarà necessario, per ogni marca da bollo digitale, inserire come minimo le seguenti informazioni all'interno del payload da inviare all'intermediario di pagamento con cui ci si sta integrando (in base a poi all'intermediario potrebbero essere necessari altri campi):
Il taglio della marca da bollo o l'importo inserito nella configurazione (per ora l'unico importo supportato è quello da 16 €)
La provincia di residenza del pagatore, la quale si trova nel campo payer.country_subdivision
dell'evento payment
L'hash del documento informatico o della segnatura di protocollo cui è associata la marca da bollo digitale (per semplicità il documento in questione sarà il json dell'evento letto dal topic payments
)
Il valore del campo reason
letto dall'evento nel topic payments
o, in assenza di questo, la causale della marca da bollo inserita nella configurazione
Successivamente, sarà necessario:
Valorizzare il campo payment.amount
l'importo della marca da bollo inserita in configurazione
Valorizzare il campo payment.due_type
con il tipo di dovuto inserito nella configurazione
Valorizzare il campo payment.pagopa_category
con il valore della tassonomia di pagoPA contenuta nella configurazione (se presente)
Valorizzare il campo payment.document.hash
contenente l'hash del documento informatico o della segnatura di protocollo cui è associata la marca da bollo digitale
ATTENZIONE: questa struttura sarà presente solo sugli eventi payment con event_version
valorizzato a 2.0
, sarà necessario dunque controllare la versione dell'evento prima di processarlo
In alcuni casi è possibile che il pagamento che il proxy deve processare presenti un bilancio fisso, ovvero un bilancio i cui importi per ogni voce sono già predeterminati a priori in fase di configurazione del servizio. In tal caso il campo split
, quando letto dal proxy per la prima volta, si presenterà come un array vuoto []
, il proxy dunque:
Leggerà il campo service_id
e reperirà nel mediante esso la configurazione del servizio dentro il quale ci sono le informazioni del bilancio.
Utilizzerà le informazioni ricavate per popolare il payload di creazione della posizione debitoria sull'intermediario di riferimento e per arricchire l'evento appena letto con i dati del bilancio.
Nel proxy è configurato per il servizio A il seguente bilancio
Il bilancio da utilizzare per arricchire il campo split
del pagamento sarà:
In alcuni casi è possibile che il pagamento che il proxy deve processare presenti un bilancio variabile, ovvero un bilancio i cui importi per ogni voce vengono determinati dinamicamente a seconda del cittadino. In tal caso il campo split
, quando letto dal proxy per la prima volta, si presenterà con un payload così strutturato
oppure così nel caso in cui il cittadino sia esentato dal pagamento di una certa voce del bilancio
dove il campo code
indica un identificativo interno della voce di bilancio determinato in fase di configurazione del servizio, e il campo amount
indica l'importo per ogni voce di bilancio. Nel caso in cui il valore di amount
sia null
significa che quella riga deve essere eliminata in fase di creazione del pagamento sul gateway. Il proxy dunque:
Leggerà il campo service_id
e reperirà mediante esso la configurazione del servizio dentro il quale ci sono le informazioni del bilancio.
Per ogni voce di bilancio ricavata dalla configurazione del servizio verrà sostituito l'importo con quello letto nel campo split
del pagamento
Utilizzerà le informazioni ricavate per popolare il payload di creazione della posizione debitoria sull'intermediario di riferimento e per arricchire l'evento appena letto con i dati del bilancio.
Nel proxy è configurato per il servizio A il seguente bilancio
Nel messaggio del topic payments il campo split
è valorizzato a
Il bilancio da utilizzare per arricchire il campo split
del pagamento sarà:
Nel proxy è configurato per il servizio a il seguente bilancio
Nel messaggio del topic payments
il campo split
è valorizzato a
Il bilancio da utilizzare per arricchire il campo split
del pagamento sarà:
In caso di risposta positiva:
Il pagamento viene passato in stato PAYMENT_PENDING
modificando il campo status
,
Vengono compilate le informazioni del bilancio nel campo split
(se presenti),
Viene compilata l'url per pagare online (links.online_payment_begin.url
), la quale è data dalla concatenazione della variabile d'ambiente EXTERNAL_API_URL
alla stringa /online-payment/{payment_id}
Viene compilata l'url di ritorno dal pagamento (links.online_payment_landing.url
), la quale è data dalla concatenazione della variabile d'ambiente EXTERNAL_API_URL
alla stringa /landing/{payment_id}
Viene compilata l'url per pagare offline (links.offline_payment.url
), la quale è è data dalla concatenazione della variabile d'ambiente EXTERNAL_API_URL
alla stringa /offline-payment/{payment_id}
Viene compilata l'url della ricevuta di pagamento (links.receipt.url
), la quale è data dalla concatenazione della variabile d'ambiente EXTERNAL_API_URL
alla stringa /receipt/{payment_id}
Viene compilata l'url per richiedere l'aggiornamento del pagamento (links.update.url
), la quale è data dalla concatenazione della variabile d'ambiente INTERNAL_API_URL
alla stringa /update/{payment_id}
Nota: a differenza dell'altre url, questa viene valorizzata utilizzando una variabile d'ambiente separata in quanto deve essere possibile chiamarla pubblicamente, questo perchè è compito del solo payments-poller
richiedere periodicamente l'aggiornamento del pagamento.
Viene compilata l'url per forzare l'annullamento di un pagamento (links.cancel.url
), la quale è data dalla concatenazione della variabile d'ambiente EXTERNAL_API_URL
alla stringa /payments/{payment_id}
. Viene inoltre compilato il metodo con cui chiamare la url (links.cancel.method
) con il valore PATCH
.
Viene segnato il timestamp di aggiornamento dell'evento di pagamento (updated_at
)
Viene infine salvato il pagamento sullo storage e scritto il corrispondente evento aggiornato sul topic payments
, indicando come chiave dell'evento il valore del campo service_id
.
In caso di risposta negativa, il pagamento viene passato in stato CREATION_FAILED
, viene salvato il pagamento sullo storage e viene scritto l'evento aggiornato sul topic payments
Nel core intanto, la UI del cittadino monitora lo stato del pagamento interrogando una tabella su KSQLDB
creata a partire dal topic payments, e se in stato PAYMENT_PENDING
, vengono mostrate all'utente le opzioni per pagare
Verra richiamata l'url {EXTERNAL_API_URL}/online-payment/{payment_id}
servita dal proxy
Il proxy richiederà all'intermediario di pagamento il link per pagare
Sarà segnato il timestamp di apertura del link (links.online_payment_begin.last_opened_at
)
Sarà segnato il timestamp di aggiornamento dell'evento (updated_at
)
Il pagamento verrà salvato sullo storage
L'evento aggiornato verrà scritto sul topic payments
, indicando come chiave dell'evento il valore del campo service_id
Verrà restituito il link ritornato allo step 2 mediante il quale l'utente verrà rediretto al portale dell'intermediario
Al termine della procedura di pagamento, l'utente verrà fatto ritornare sul dettaglio della pratica mediante l'url di landing specificata in fase di creazione della posizione debitoria (links.online_payment_landing.url
) allo step 5, a questa url andrà aggiunto in query string un parametro payment
da valorizzare con OK
o KO
a seconda dell'esito del pagamento
Contemporaneamente, essendo che si passa per il proxy, verrà segnato il timestamp in cui l'utente ha aperto il link di ritorno (links.online_payment_landing.last_opened_at
)
Il pagamento verrà portato in stato PAYMENT_STARTED
Sarà segnato il timestamp di aggiornamento dell'evento (updated_at
)
Il pagamento verrà salvato sullo storage
L'evento aggiornato verrà scritto sul topic payments
, indicando come chiave dell'evento il valore del campo service_id
.
Verra richiamata l'url {EXTERNAL_API_URL}/offline-payment/{payment_id}
servita dal proxy
Il proxy richiederà all'intermediario di pagamento il pdf dell'avviso di pagamento
Sarà segnato il timestamp di apertura del link (links.offline_payment.last_opened_at
)
Sarà segnato il timestamp di aggiornamento dell'evento (updated_at
)
Il pagamento verrà salvato sullo storage
L'evento aggiornato verrà scritto sul topic payments
, indicando come chiave dell'evento il valore del campo service_id
Verrà eseguito il download del pdf ritornato allo step 1
È possibile che l'operatore abbia necessità di annullare un pagamento mediante l'opzione di registrazione del pagamento, ciò può accadere ad esempio se il cittadino, dopo aver creato il pagamento, decide di non concluderlo, e di pagare invece mediante altri canali creando un nuovo pagamento diverso da quello presente sulla piattaforma. Per evitare quindi di lasciare il pagamento pendente per un tempo indefinito, e per permettere l'avanzamento della pratica all'interno della quale è stato creato tale pagamento, sarà possibile annullare quest'ultimo.
Gli step sono i seguenti:
Verra richiamata l'url PATCH {EXTERNAL_API_URL}/payments/{payment_id}
servita dal proxy con payload {"status": "CANCELED"}
Il proxy, dopo aver letto il pagamento dallo storage, segnerà il campo status
a CANCELED
Sarà segnato il timestamp di apertura del link (links.offline_payment.last_opened_at
)
Sarà segnato il timestamp di aggiornamento dell'evento (updated_at
)
Il pagamento verrà salvato sullo storage
L'evento aggiornato verrà scritto sul topic payments
, indicando come chiave dell'evento il valore del campo service_id
In alcuni casi è possibile che i pagamenti non siano generati a partire dall'area personale, ma siano invece importati da una fonte esterna. In questo caso gli eventi relativi a tali pagamenti saranno inseriti nel topic payments
già in status PAYMENT_PENDING
e conterranno al loro interno tutte le informazioni che il proxy normalmente inserisce quando fa passare un pagamento generato dall'area personale dallo status CREATION_PENDING
allo status PAYMENT_PENDING
.
In tal caso tuttavia, questi pagamenti non saranno presenti sullo storage del proxy, dal momento che questa è un'operazione che normalmente farebbe il proxy. Di conseguenza, nel momento in cui dovesse venir letto un evento in stato PAYMENT_PENDING
, ci fosse una configurazione attiva per quest'ultimo (ciò si determina controllando che ci sia sullo storage una configurazione con tenant_id
e service_id
equivalenti a quelli contenuti nel pagamento) ma non esistesse sullo storage, bisogna semplicemente salvare il pagamento sullo storage.
Nel core intanto, questo tipo di dovuti saranno mostrati nella sezione "Pagamenti" insieme a tutti i pagamenti (importati e non) generati dal cittadino, i dati qui presenti sono estratti mediante interrogazione di una tabella su KSQLDB
creata a partire dal topic payments.
Cliccando su "Vedi dettaglio" si andrà sul dettaglio dello specifico pagamento dove saranno mostrate le opzioni per pagare (online, offline)
Il payments poller nel frattempo:
Interroga KSQLDB
per ottenere lo stato dei pagamenti, e se questi sono in stato PAYMENT_PENDING
o PAYMENT_STARTED
, eseguirà una chiamata http all'url {INTERNAL_API_URL}/update/{payment_id}
servita dal proxy (quella di aggiornamento menzionata allo step 5)
Il proxy a sua volta recupera dallo storage il pagamento, interroga l'intermediario di riferimento per verificare lo stato del pagamento, e in caso lo aggiorna in stato COMPLETE
o PAYMENT_STARTED
(questa casistica può verificarsi nel caso in cui l'utente paghi ma poi chiuda il browser anziche cliccare sul bottone contenente la landing url)
Se il pagamento viene portato in stato COMPLETE
, si dovrà salvare, se presente come informazione ritornata dall'intemediario di riferimento, il timestamp di avvenuto pagamento (payment.paid_at
), e l'id della transazione (payment.transaction_id
)
Il proxy segna il timestamp in cui è stato aperto il link di aggiornamento (links.update.last_opened_at
)
Il proxy segna il timestamp di aggiornamento del pagamento (updated_at
)
Il proxy salva il pagamento sullo storage e lo scrive sul topic payments
, indicando come chiave dell'evento il valore del campo service_id
.
Nota 1: più passa il tempo, più è probabile che il pagamento non venga eseguito dall'utente, di conseguenza ad ogni chiamata del poller verso il proxy, viene incrementato l'intervallo di tempo tra una chiamata e l'altra, ciò viene fatto in base al campo links.update.next_check_at.
Nota 2: non tutti i proxy aggiornano i pagamenti mediante update, alcuni vengono aggiornati predisponendo un'apposita API che l'intermediario di pagamento PagoPA chiama inviando la notifica di completamento del pagamento. Questa viene quindi elaborata dal proxy il quale aggiorna il pagamento portandolo in stato COMPLETE
, segna il timestamp di aggiornamento del pagamento (updated_at
), lo salva sullo storage e lo scrive sul topic payments
, indicando come chiave dell'evento il valore del campo service_id
.
Il payment updater monitora lo stato dei pagamenti leggendo dal topic payments
, e contestualmente aggiorna lo stato della pratica, se il pagamento è in statoPAYMENT_STARTED
, aggiorna lo stato della pratica a "In attesa dell'esito di pagamento", se invece il pagamento è in stato COMPLETE
, aggiorna lo stato della pratica a "Accettata" se il pagamento è posticipato, o Inviata se il pagamento è immediato.
Gli stati per i quali passa un pagamento durante il suo ciclo di vita sono riassunti nella seguente tabella
Tutti i proxy devono:
Validare gli eventi in input e output
Integrazione con sentry
Healthcheck
Esporre metriche di monitoraggio in formato prometheus
Ogni microservizio deve rispettare gli standard della piattaforma
Ogni proxy deve soddisfare i requisiti suggeriti dal 12 factors manifest
Esposizione di un endpoint/status
per l'healthcheck: esso deve essere restituire 200 se il sistema è "healthy" e 503 in caso si presentino un problemi di connessione con kafka
Esposizione delle metriche mediante l'endpoint /metrics
con Prometheus utilizzando la convenzione da loro suggerita
Terminazione pulita del servizio (timeout di 15 secondi)
Esposizione di un endpoint /docs
per la consultazione della documentazione delle API esposte dal servizio secondo lo standard OpenAPI
Integrazione con Sentry per l'error reporting
Il sistema di logging deve rispettare il formato Apache e descrivere nei tre livelli debug, error e info. Ogni log, quando possibile, deve loggare il remote_id
e l'id
del documento processato
Il README deve seguire le linee guida della pubblicazione del software open source
il nome del progetto e del servizio devono essere nella seguente forma : "Protocol Proxy <Nome del Protocollo Specifico>"
Inserire il file .gitlab-ci.yml
riportando in esso questa pipeline
Il Protocol Proxy deve esporre le seguenti metriche:
Metrica | Labels | Descrizione |
---|---|---|
Avere una specifica in formato OpenAPI v3
Rispettare la Linee Guida di interoperabilità: in particolare per quanto riguarda
il formato dei dati (4.2)
raccomandazioni sul naming (4.3) optando per la scelta di snake_case
per i nomi degli attributi
logging (4.4)
risultare valido nel validatore ufficiale OA3
Il servizio deve essere multi tentant e multi servizio: deve quindi essere in grado di gestire le confiugurazioni per più tenant a cui possono essere attribuiti più servizi con il corrente sistema di protocollazione.
Ogni richiesta sia essa appartenente alla fase 1 o alla fase 2 descritte nella sezione "workflow sistema di protocollazione" devono essere trattate in maniera atomica.
Lo storage deve essere compatibile con Aws s3, Azure e local file system.
Il servizio deve essere configurabile attraverso le seguenti variabili d'ambiente.
Se le varibili senza valore di default non vengono settate, il servizio deve notificare la mancata impostazione attraverso un log di livello error e terminare in maniera pulita la propria esecuzione.
Se si sta facendo il deploy di un nuovo microservizio per la prima volta o si sta aggiungendo una nuova API o una nuova pagina a una interfaccia esistente, è necessario aggiungere alcuni controlli di qualità minima prima del rilascio.
Test flusso standard
Inserire la configurazione del tenant
Inserire la configurazione del servizio
Inserire un esempio di documento non protocollato nel topic documents e verificare che venga correttamente protocollato
Inserire un esempio di documento protocollato nel topic documents e verificare che venga ignorato
Inserire un esempio di documento non protocollato nel topic documents per cui non esiste una configurazione di tenant e/o di servizio e verificare che venga ignorato
Test flusso di errore
Modificare la configurazione del servizio in modo che sia errata (mettendo ad esempio credenziali errate)
Inserire un esempio di documento non protocollato nel topic documents e verificare che, a seguito del fallimento, venga prodotto un evento nel topic di retry
Correggere la configurazione del servizio
Inserire il documento prodotto nel topic di retry all'interno del topic documents e verificare che venga correttamente protocollato
Il Protocol Proxy interagisce con la piattaforma in due fasi distinte ma complementari:
Abilitazione e configurazione del sistema di protocollazione
Protocollazione dei documenti
In questa fase l'amministratore della piattaforma configura un servizio, decidendo quale sistema di protocollazione abilitare per la protocollazione dei relativi servizi.
Cliccando sul bottone "abilita" la piattaforma contatta il Protocol proxy relativo tramite chiamate REST API. Con la prima chiamata, la piattaforma richiede la configurazione del form schema che verrà presentato all'Amministratore. Attraverso tale form è possibile inserire le configurazioni relative al tenant e al servizio corrente. Tali configurazioni verranno inviate via REST HTTPS al Protocol Proxy che si occuperà della persistenza di tali configurazioni.
Una volta che tale processo si è concluso, il Protocol Proxy è in grado, sfruttando tali configurazioni, di interagire con il sistema di protocollazione esterno.
Le configurazioni di protocollazione possono essere create, modificate e disabilitate. Per questo motivo il Protocol Proxy deve esporre chiamate API CRUD (dettagliate nella pagina seguente).
Prima di eseguire qualsiasi operazione, il protocollo proxy deve verificare la versione dell'evento (event_version
) ed elaborare esclusivamente la versione 1, per la quale è abilitato. La versione dell'evento da considerare deve essere un valore fissato nel codice. Tutti gli eventi con event_version
diversa da 1 devono essere scartati.
In questo diagramma, viene descritto il workflow di una pratica (ma il processo è uguale per qualsiasi altro tipo di richiesta) presentata da un utente e presa in carico da un operatore(le altre casistiche descritte nella pagina "Architettura del sistema di protocollazione" seguono lo stesso processo).
Una volta che la pratica è stata presa in carico dell'operatore essa deve essere protocollata. La piattaforma produce un evento di tipo documento sul topic "documents". Il Protocol Proxy, consuma tale evento:
Per poter protocollare correttamente il documento, è fondamentale verificare l'esistenza delle configurazioni relative al tenant e al servizio pertinenti. Questa operazione può essere eseguita attraverso la verifica del campo document.RemoteCollection.ID
, che identifica il servizio, e document.tenantId
, che identifical'ID del tenant. Questi campi dovrebbero essere confrontati con gli ID delle configurazioni corrispondenti precedentemente salvate nello storage. Nel caso in cui tali configurazioni non siano disponibili, il documento dovrà essere semplicemente ignorato, procedendo all'evento successivo.
Una volta assicuratosi di dover protocollare il documento in esame, il Protocol Proxy deve verificare che il documento non sia già stato protocollato. Un evento è protocollato quando l'oggetto "document_number" è valorizzato con almeno il numero di protocollo. Nel caso il documento sia già stato protocollato, l'evento va semplicemente ignorato.
Una volta verificato che il documento sia da protocollare, è necessario modificare il campo status da DOCUMENT_CREATED
o REGISTRATION_FAILED
o PARTIAL_REGISTRATION
a REGISTRATION_PENDING
, poi si produce l'evento sul topic documents
Una volta che si rilegge lo stesso evento prodotto allo step precedente, e una volta superate le precedenti verifiche, Protocol Proxy può interagire con il sistema di protocollazione esterno per la protocollazione del documento.
Se la protocollazione va a buon fine, il documento verrà modificato con i dati di protocollazione (vedi sezione successive), verrà inoltre sovrascritto il campo stato settandolo a REGISTRATION_COMPLETE
, infine, sarà necessario aggiornare anche il campo updated_at
con il timestamp corrente (time.now()
) in formato ISO 8601
(es: 2023-11-10T10:54:44+00:00
).
Il Protocol Proxy produce un nuovo evento di tipo document sul topic documents con in aggiunta le informazioni di protocollazione (valorizzando l'oggetto registration_data
).
A questo punto la piattaforma, in ascolto sullo stesso topic, provvederà ad aggiornare il suo stato interno.
Quando viene generato un evento, il protocollo proxy deve assegnare i seguenti valori:
app_id
: <nome del servizio>:<versione corrente del servizio>
. Es: protocol-proxy-fake:1.1.1
event_created_at
: es: 2023-11-23T14:14:23+00:00
event_id
: stringa UUID autogenerata che identifica univocamente l'evento
In un sistema a eventi che utilizza il pattern event sourcing, è possibile che alcuni eventi non vengano processati correttamente a causa di errori temporanei come per esempio una comunicazione interrotta tra il Protocol Proxy e il sistema di protocollazione esterno. In questo caso basterebbe tentare nuovamente il consumo dello stesso evento più volte finché il problema non viene risolto.
La piattaforma offre questo tipo di possibilità attraverso l'implementazione di un meccanismo di retry che legge da un topic (retry_queue) specifico tutti gli eventi falliti e li reinserisce nei vari topic di origine in base a una politica di retry scelta a monte.
Il servizio che si occupa di questo meccanismo è in grado di gestire eventi eterogenei provenienti da diversi topic. Questo è possibile solo se il servizio è a conoscenza del topic di origine in cui reinserire gli eventi da processare nuovamente.
Per questo motivo, qualsiasi servizio si voglia avvalere del meccanismo di retry, deve modificare l'evento fallito inserendo il topic in cui si vuole che sia reinserito e poi produrre l'evento nel topic di retry impostato tramite variabile d'ambiente.
I metadati da inserire nell'evento sono nella seguente forma:
Quando l'oggetto sarà reinserito nel topic "document" dal Retry Orchestrator, l'oggetto "retry_meta" avrà dei campi aggiuntivi utili per attuare la politica di retry. Tali campi, sono gestiti e utili unicamente al Retry Orchestrator. Il Protocol Proxy non si deve quindi preoccupare di tale oggetto una volta inserito l'oggetto "retry_meta" con il campo "original_topic" valorizzato una tantum.
Nel caso in cui non si riesca a protocollare la pratica per un errore da parte del provider per esempio, o nel caso in cui nessuno delle chiamate al protocollo vadano a buon fine, è necessario sovrascrivere il campo status
a REGISTRATION_FAILED
.
Nel caso in cui la protocollazione richieda diversi step (come per esempio protocollazione del documento principale e a seguito la protocollazione dei vari allegati) e questa venga interrotta a metà, l'evento dovrà essere inserito nella coda di retry assime alle informazioni relative allo stato corrente in modo da riprendere la protocollazione da dove è stata interrotta. Specificamente il documento va messo nello stato PARTIAL_REGISTRATION
.
In tale circostanza, è possibile inserire liberamente tutti i campi rilevanti per il monitoraggio dello stato all'interno dell'oggetto retry_meta
. Questa flessibilità consente di adattare i dati alle specifiche esigenze, garantendo un controllo accurato del processo di protocollazione e prevenendo duplicazioni delle operazioni.
Solitamente la protocollazione di un documento digitale è accompagnata anche dalla protocollazione dei relativi allegati(menzionati nel paragrafo #struttura) e dalla fascicolazione degli stessi. Dal punto di vista del Protocol Proxy, la protocollazione del documento, di tutti i suoi allegati e la fascicolazione è considerata come un unica operazione atomica. Questo implica che il processo di protocollazione è considerato completo solo quando tutte le chiamate relative al documento principale, agli eventuali allegati e alla eventuale fascicolazione si concludono con successo. Se anche solo una chiamata dovesse fallire, questo costituirà una condizione sufficiente per interrompere il processo di protocollazione e avviare il meccanismo di retry descritto nel paragrafo precedente.
Il documento può avere i seguenti stati:
DOCUMENT_CREATED
: stato iniziale in cui si trova il documento quando viene prodotto dal document dispatcher
REGISTRATION_PENDING
: stato in cui viene prodotto il documento dal protocol proxy non appena viene trovato in stato DOCUMENT_CREATED
, PARTIAL_REGISTRATION
o REGISTRATION_FAILED
e presenta una configurazione attiva per esso
REGISTRATION_COMPLETE
: stato in cui viene prodotto il documento dal protocol proxy dopo essere stato protocollato correttamente
PARTIAL_REGISTRATION
: stato in cui viene prodotto il documento dal protocol proxy nel caso in cui si ha molteplici chiamate da fare e una di questa fallisce (per esempio non vengono caricati tutti gli allegati)
REGISTRATION_FAILED
: stato in cui viene prodotto il documento dal protocol proxy nel caso in cui la protocollazione fallisce totalmente
Di seguito la rappresentazione grafica
La Fascicolazione di un documento rappresenta l'attribuzione dello stesso ad una unità archivistica – “il fascicolo” - che raggruppa un insieme di documenti appartenenti al medesimo procedimento. Non tutti i sistemi di protocollazione offrono questa funzionalità. In questo caso la valorizzazione del campo "folder_number" deve essere discussa e definita a seconda dei casi.
Durante la fase di protocollazione, è possibile che il Protocol Proxy debba utilizzare le API della piattaforma per ottenere le informazioni necessarie per eseguire il processo di protocollazione. Un esempio pratico di ciò riguarda la protocollazione degli allegati. Il sistema di protocollazione richiede che il base64 del file allegato sia incluso nel payload della chiamata. Tuttavia, nel documento digitale sono presenti solo i link per scaricare i file. Pertanto, sarà compito del Protocol Proxy autenticarsi sulla piattaforma per recuperare il Token JWS e successivamente chiamare l'API specifica per ottenere il file necessario da cui calcolare il Base64.
Il token può essere recuperato con la seguente chiamata:
Username : admin
Password: admin
Basepath: estratto dal campo "Url" di ogni allegato
Endpoint : /lang/api/auth
method: POST
header Content-Type: application/json
payload : { "username": "admin", "password": "admin" }
Response: 200 -> {"token": "eyDIJeiojf...."}
Response: 401 -> { "code": 401, "message": "Credenziali non valide." }
Response 400 -> HTML body
Le API della piattaforma sono documentate al seguente link:
https://servizi.comune-qa.bugliano.pi.it/lang/api/doc
Calcolo del basepath per recuperare il token
L'url per API auth deve essere estratta da campo url
di ogni attachment:
quindi dal seguente documento avremo una post auth all'endpoint https://servizi.comune-di-vicenza.it/lang/api/auth
con credenziali fornite tramite variabili d'ambiente(utente e password). Mentre nel secondo caso avremo una post auth all'endpoint https://servizi.comune-di-bugliano.it/lang/api/auth
con credenziali fornite tramite variabili d'ambiente(utente e password).
NOTA BENE: nel seguente esempio abbiamo un array di attachments in cui due url appartengono a due comuni diversi. Questo nel mondo reale non può succedere. Il base url è lo stesso per tutto il documento(cioè un documento appartiene ad un solo comune). Sono stati utilizzati due comuni diversi allo scopo di sottolineare la parte di url che cambia nella costruzione dell'endpoint della API di autenticazione.
La configurazione di tenant e servizi avviene interrogando delle API apposite fornite dal Protocol Proxy di riferimento. Quest'ultimo fornisce inoltre un form come json schema sviluppato con form.io mediante il quale l'utente inserisce tali configurazioni.
Per creare un form utilizzare il seguente builder: https://formio.github.io/formio.js/app/builder
L'admin, dall'interfaccia di configurazione dei protocolli della piattaforma compila la configurazione mediante una form, il cui json schema è servito dall'API /v1/schema
del Protocol Proxy
Lo schema della form sopra riportata è il seguente:
Premendo poi il bottone Salva, viene eseguita una POST /services
servita dal Protocol Proxy, con payload
Per modificare una configurazione esistente, il proxy serve l'API PUT /services/{service_id}
e PATCH /services/{service_id}
Per eliminare una configurazione esistente, il proxy serve l'API DELETE /services/{service_id}
. In questo caso l'eliminazione è una soft-delete, ovvero la configurazione viene semplicemente disattivata settando il parametro active
a false
.
la configurazione del tenant avviene in maniera nascosta durante la configurazione del servizio. Sarà la piattaforma a chiamare le API del Protocol Proxy. La loro implementazione è descritta nella sezione seguente
Le configurazioni di tenant e servizi vengono salvate con la seguente alberatura
root
|____tenants
| |____tenantid1.json
| |____tenantid2.json
| |____tenantid3.json
| |____.....
| |____tenantidn.json
|____services
| |____serviced1.json
| |____serviced2.json
| |____serviced3.json
| |____.....
| |____servicedn.json
Questa pagina descrive l'architettura impiegata per attuare il processo di protocollazione di una pratica
Seguendo l'approccio del C4Model viene descritta l'architettura utilizzata per attuare il processo di protocollazione. Partendo da una panoramica ad alto livello si va sempre più in dettaglio nell'architettura fino a raggiungere il secondo livello del C4 model.
Per poter attuare il processo di protocollazione, l'area personale del cittadino si avvale dei sistemi di protocollazione esterni utilizzati dai diversi tenant. È fondamentale notare che la piattaforma è un servizio SaaS di tipo multi-tenant.
L’area personale del cittadino effettua diverse interazioni con il sistema di protocollo, ma tutte seguono una logica comune. Le interazioni avvengono in seguito ad alcune azioni da parte cittadini e degli operatori:
Invio di una pratica da parte del cittadino
Invio di una richiesta di integrazioni da parte di un operatore
Invio di una integrazione da parte del cittadino
Approvazione/rifiuto di una pratica
Altre azioni possibili, mediante configurazioni avanzate del servizio:
Creazione di una pratica da parte di un operatore per conto di un cittadino
Ritiro di una pratica da parte del cittadino
Annullamento di una pratica da parte di un operatore
Per ognuna di queste operazioni viene prodotto un documento. A partire dall'entità documento si costruiscono le richieste per i sistemi di protocollazioni esterni.
Il processo di protocollazione è stato realizzato seguendo i principi del Domain Driven Design utilizzando il pattern Event Sourcing.
Di conseguenza si è scelto di separare il dominio dei servizi dal dominio dei documenti. In questo modo, i documenti sono indipendenti dalla loro fonte di origine e possono essere generati sia per esempio dalle pratiche, sia da altre strutture dati diverse senza che il sistema di protocollazione debba subire modifiche o integrazioni.
La piattaforma è multi tenant e multi servizio: ogni tenant può selezionare un diverso sistema di protocollazione per ogni diverso servizio che offre. Questo significa che da qualche parte la piattaforma deve tenere traccia delle configurazioni di protocollo scelte per un determinato tenant e i servizi abilitati per il sistema scelto. Inoltre va tenuta traccia anche delle configurazioni specifiche di ogni sistema di protocollazione.
Per tenere traccia di tali configurazioni è necessario un meccanismo di storage persistente(il cui tipo è da definire a seconda dei casi), mentre la scelta su chi detiene la responsabilità di gestire tali configurazioni è stata attribuita al Protocol Proxy.
Questa decisione è stata dettata dal fatto che ogni sistema di protocollazione funziona in maniera diversa, e quindi ognuno di essi necessita di una configurazione specifica. Affidando la responsabilità di gestire le configurazioni al Protocol Proxy, si evita di dover modificare, per ogni nuova integrazione, il Core Application, aggirando inoltre il problema di uno sviluppo coordinato con un terzo sistema come il Protocol Proxy.
Si può dedurre quindi che esiste un mapping 1:1 tra i Protocol proxy e i sistemi di protocollazione esterni.
Opencity Italia integra l'autenticazione Spid/CIE mediante il componente dedicato OpenLogin, dialogando con esso con protocollo oAuth2. Lo stesso componente può essere usato da altri applicativi.
Una autenticazione con SSO tra Opencity Italia e un applicativo in uso allo stesso Ente può essere agevolmente realizzata se può essere garantito il supporto per il protocollo .
Vediamo uno schema generale degli attori in gioco:
Vediamo un flusso di autenticazione di un cittadino che fa login prima sull'area personale e successivamente sull'applicativo di terze parti:
L'applicativo di terze parti dovrà essere dotato di un opportuno client oAuth2. Ipotiziamo che le url coinvolte siano le seguenti e mostriamo una tipica configurazione del client.
URL dell'area personale: https://servizi.comune.bugliano.pi.it
URL della pagina di login (OpenLogin): http://login.comune.bugliano.pi.it
URL dell'applicativo di terze parti: http://myapp.company.com
Per attivare un nuovo client è necessario fornire:
La URL da autorizzare come redirct uri del client
La URL di logout
L'attivazione comprende i seguenti dati
In questa pagina viene fatta una breve descrizione delle API per la gestione dei Jobs (processi asincroni)
La piattaforma consente la possibilità da parte di sistemi esterni di creare/modificare/visualizzare ed eliminare oggetti Job
Ad esempio nel nostro ambiente di demo è possibile consultare le API dedicate a questo scopo:
Questi speciali oggetti sono dei processi onerosi che vengono creati e poi eseguiti in modo asincrono dalla piattaforma.
Esempio di interazione tra un sistema esterno e le API dei jobs:
In fase di creazione del Job viene specificato tramite il parametro type il tipo di processo (type) che verrà eseguito dal sistema.
In base al tipo di processo creato i parametri attachment e args possono cambiare. Attualmente l'unico processo disponibile è import_dovuti
Per poter creare un Job bisogna eseguire una chiamata POST all' endpoint api/jobs
Il payload per la creazione di un Job è così composto
Dove:
attachment -> è il file in base64 che verrà processato
type -> è il tipo di processo che deve essere eseguito
args -> sono dei parametri necessari al tipo di processo
L' API rispondere con un codice HTTP 201
e con l'id del Job creato
Per poter recuperare informazioni su Job bisogna eseguire una chiamata GET all' endpoint api/jobs/{id}
Il payload di risposta delle API è così composto:
Dove:
status -> è lo stato attuale del job e può assumere i seguenti valori pending, cancelled, running, finished, failed
output -> l'output finale del processo, può contenere un errore in caso di processo failed
running_output -> l'output corrente (Es. la riga del file che si sta processando)
Uno degli utilizzi più significativi che si può fare con i Job è l'importazione di contenuti come ad esempio l'importazione dei dovuti
In questa pagina viene fornito un esempio di chiamata API per l'importazione dovuti
Come descritto nella sezione precedente tramite le API dei jobs è possibile creare un processo di importazione dovuti.
Per poter procedere correttamente all'importazione bisogna prima creare un servizio a cui i dovuti devono essere collegati.
Questa operazione può essere fatta tramite interfaccia nella sezione admin, il servizio creato dovrà essere di tipo "Servizio di pagamento dovuti"
Per il servizio appena creato dovrà essere configurato anche il gateway di pagamento
Per poter creare un Job di importazione dovuti bisogna eseguire una chiamata POST all' endpoint api/jobs
Il payload per la creazione di un Job è così composto
Dove:
attachments -> è il file csv contenente i dovuti da importare
Il file csv deve acere un template specifico di questo tipo:
service_id -> è un parametro specifico del tipo di job import_dovuti ed indica su quale servizio devono essere agganciati i dovuti importati
Le API fornite nella documentazione dell'intermediario vengono generalmente testate in prima battuta via Postman/Insomnia nel caso di chiamate REST o via SoapUI nel caso di chiamate SOAP
A seguito dei test delle chiamate, si procede con l'implementazione del microservizio. Per l'ambiente di sviluppo viene utilizzato Docker e Docker compose come da esempio nel seguente repository :
La piattaforma Opencity Italia supporta questa possibilità mediante una tecnica che permette di convertire una sessione basata su cookie in un token JWT.
Questo consente di usare l'autenticazione senza necessariamente usare lo stesso cookie:
integrarsi con altri applicativi di terze parti che possono usare un client JWT;
fare chiamate cross-origin (CORS) autenticate dove non è semplice o possibile sfruttare lo stesso cookie di autenticazione;
Attenzione: il requisito perché questa tecnica funzioni è che i servizi si trovino tutti sotto lo stesso dominio di secondo livello. Ad esempio: - servizi.comune.bugliano.pi.it - www.comune.bugliano.pi.it - altro.bugliano.pi.it
Come molti applicativi web anche Opencity Italia usa i cookie per implementare la sessione utente, il cookie si ottiene semplicemente l'indirizzo di accesso degli utenti: ad esempio per il nostro Ente di demo è
A questo punto nel proprio brower è possibile rintracciare facilmente il cookie della piattaforma.
Il cookie di sessione viene inviato in modo trasparente all'utente dal browser al sito che lo ha generato, e questo garantisce che l'utente abbia sempre una sessione valida quando naviga nella sua area personale.
Per fare chiamate alle API però è necessario convertire il Cookie in Token JWT, per farlo esiste una apposita API, nel nostro esempio:
Per simulare la chiamata con Postman ad esempio:
Il token JWT può essere usato adesso per fare chiamate alle API che richiedono una autenticazione, ad esempio posso usare l'interfaccia Swagger delle API come segue.
Faccio click in alto a destra sul comando Authorize
A questo punto posso chiamare le API che richiedono autenticazione come la /applications
All'interno del token sono presenti le seguenti informazioni:
In questa sezione viene descritta la modalità di integrazione con un servizio di terze parti protetto da autenticazione tramite il token JWT dell'utente.
Dataset e micro servizi esterni alla piattaforma possono essere facilmente integrati tramite il flusso di autenticazione basato su JWT (JSON Web Token) e JWKS (JSON Web Key Set)
Questo flusso di autenticazione prevede alcuni passaggi chiave e interazioni tra:
client (browser dell'utente)
area personale
micro servizio esterno
Di seguito il processo punto per punto:
Il client invia una richiesta all'area personale per ottenere un JWT, spesso includendo le credenziali dell'utente o altre forme di autorizzazione.
Questa operazione può essere effettuata facendo una chiamata alle API:
oppure come utente non autenticato effettuando una chiamata POST alle API
L'area personale autentica il client, genera un JWT e lo firma con una chiave privata.
Il token viene quindi restituito al client.
Il client invia una richiesta al micro servizio esterno per accedere a una risorsa protetta, includendo il JWT nell'intestazione Authorization.
Per verificare la validità del token il micro servizio esterno deve:
Estrarre il JWT dalla richiesta
Recuperare il JWKS dall'area personale, il JWKS contiene le chiavi pubbliche necessarie per verificare la firma del JWT.
Verificare la firma del JWT e convalidare le sue richieste
L'area personale espone per ogni sua istanza il JWKS all'indirizzo:
https://instance_url/.well-known/jwks.json
Ad esempio:
In base alla validità del JWT il micro servizio esterno concede o nega l'accesso alla risorsa richiesta.
Oltre alla validità del token si possono verificare le affermazioni in esso contenute.
Il payload del token prodotto dall'area personale presenta delle informazioni utili a capire se quello specifico utente oltre ad avere un token valido ha i permessi per ottenere la risorsa richiesta.
Di seguito un esempio di payload del token
I widget dell'area personale sono una serie di componenti js/html/css che sono inseribili in una qualunque pagina web. In particolare alcuni di questi sono servizi built-in che i comuni devono offrire
Widget generali:
Login Box
Satisfy
I servizi built-in implementati sono:
Richiedi assistenza
Segnala problema in città
Prenota appuntamento
Pagamenti dovuti
I servizi sono presenti in ogni tenant fin dalla sua creazione e non sono modificabili dagli amministratori. Inoltre possono generare pratiche del tutto simili a quelle dei servizi tradizionali, ma il modulo è standard e come amministratore non è possibile modificarlo.
Ogni tenant (ente) punta in automatico alla sua versione widget compatibile in modo automatico
Tutti i widget possono essere caricati con bootstrap-italia 2 se il sito che lo contiene non carica già una versione di bts2 oppure senza
Esempio
Se in una pagina ci sono più widget lo script con le variabili si inserisce una volta
Codice da inserire nella pagina per il caricamento del widget, sostituire <domain> con il link dell'area personale dell’ente es: https://servizi.comune.bugliano.pi.it
Il widget restituisce come pagina di default /
oppure /#
la pagina di invio nuova segnalazione
Sotto la path /#/segnalazioni
restituisce la pagina della lista di segnalazioni Sotto la path /#/segnalazioni/{id}
restituisce la pagine del dettaglio di una segnalazione
Dalla versione 1.5.0 è stata aggiunta una nuova variabile per limitare la ricerca degli indirizzi tramite zone, configurando un bounding_box (x1,y1,x2,y2) x = longitudine, y = latitudine
Dalla versione 1.6.0 sono state aggiunte due variabili d'ambiante per la configurazione del provider delle mappe basato su nominatim
di default il provider è configurato su nominatim.openstreetmap.org
.
OC_MAP_SEARCH_PROVIDER
configura il provider di ricerca es. ricerca indirizzi da select
All'occorrenza può essere cambiato con un'altro provider custom basato su nominatim
OC_MAP_REVERSE_PROVIDER
configura il provider di ricerca inversa, ovvero restituisce l'indirizzo da punto su mappa, es. quando sposto un marker da mappa mi restituisce un indirizzo testuale.
All'occorrenza può essere cambiato con un'altro provider custom basato su nominatim
OC_SHOW_SEVERITY_FIELD
gestisce l'abilitazione del campo Valuta l'importanza del problema
nel form della segnalazione disservizio. Di default il campo è nascosto. Dalla versione 1.14.3
Configurazione per mostrare il campo Valuta l'importanza del problema
Dalla versione 1.14.3
Configurazione per mostrare il campo Valuta l'importanza del problema
Dalla versione 1.14.3
Dalla versione 1.11.0 è stata aggiunta la variabile per rendere modificabile la lista delle categorie
Categorie con canali esterni (dalla versione 1.12.3)
Per le categorie configurate con link esterni, il widget invita l'utente a proseguire tramite i canali esterni e impedisce la prosecuzione della compilazione e dell'invio della segnalazione.
I link esterni vanno configurati nel json richiamato tramite window.OC_CATEGORIES_URL
popolando all'interno della categoria, il valore external_ref
(opzionale) con i seguenti campi:
channel
esempio:
Configurazione apertura dettaglio segnalazione (dalla versione 1.16.0)
Al termine della compilazione e di invio di una segnalazione se l'utente si è autenticato è presente un link per andare al dettaglio della segnalazione.
Di default il link punta al dettaglio della segnalazione sull'area personale, in alternativa è possibile configurare l'apertura del dettaglio rimanendo nel widget tramite variabile window.OC_SHOW_DETAIL_ON_WEBSITE=true
Sui servizi built-in:
Richiedi assistenza
Segnala problema in città
Prenota appuntamento
è stata aggiunta la possibilità di auto generasi un breadcrums semplice del tipo Home / Segnalazioni
dove Home
è la homepage del sito e segnalzioni
è la route del widget, aggiungendo la variabile:
window.OC_RENDER_BREADCRUMB=true
se la variabile non è presente o ha valore false
la breadcrums non verrà abilitata
Per superare la validazione della norma "C.SI.5.2 DOMINIO ISTITUZIONALE" del Regolamento AgID è necessario impostare la variabile window.OC_SPID_BUTTON = 'false'. In questo modo anche il pulsante accedi all'Area Personale indirizzerà al dominio istituzionale.
Il pulsante Accedi dopo aver effettuato la login può mostrare un menù a tendina con avari voci che ti ridirigono all'area personale, per abilitarlo omettere o impostare come da variabile sotto a false
Per disabilitarlo e vedere solo al voce do logout impostare la variabile a true
Consigliamo di utilizzare sempre l'ultima versione aggiornata. Tutte le versioni che puntano alla stanza si aggiornano automaticamente.
Includere la versione con BI 2 se il sito contenitore non usa BI 2 - versione autoaggiornante
Se si vuole usare una versione CDN utilizzare lo questo script
Includere la versione senza stili se il sito contenitore usa BI 2 - versione auto aggiornante
Se si vuole usare una versione CDN utilizzare lo questo script
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
STATO PAGAMENTO | DESCRIZIONE | NOTE |
---|---|---|
Nome | Default | Descrizione |
---|---|---|
In base alla , si viene rediretti su un provider di autenticazione (tipicamente un sistema che permette il login con Spid/CIE), si eseguono tutti gli step necessari e infine si torna sull'area personale con una sessione valida.
Come descritto nella sezione è possibile per un utente autenticato richiedere un token di autenticazione per la propria sessione.
Tutte le versioni del widget sono disponibili sulla
title
String
✅
MaxLength: 255, descrizione chiara del documento, massimo 160 caratteri spazi inclusi
id
String (UUID)
✅
Formato: UUID, identificativo univoco
app_id
String
✅
-
event_created_at
String (DateTime)
✅
Formato: DateTime
event_id
String (UUID)
✅
Formato: UUID
event_version
Integer
✅
Valore di default: 1
external_id
String, Null
❌
-
registration_data
Object, Null
❌
Informazioni di protocollo
folder
Object, Null
❌
Informazioni sul fascicolo
status
String
✅
Valori: DOCUMENT_CREATED, REGISTRATION_PENDING, REGISTRATION_FAILED, PARTIAL_REGISTRATION, REGISTRATION_COMPLETE
type
String
✅
Tassonomia di Istanza: application-request, integration-request, integration-response, etc.
remote_id
String (UUID), Null
❌
-
remote_collection
Object, Null
❌
-
topics
Array[String]
❌
MinItems: 0
short_description
String
✅
MaxLength: 255
description
String, Null
❌
-
main_document
Object
✅
-
image_gallery
Array[Object]
❌
MinItems: 0
has_organization
String (URI), Null
❌
-
attachments
Array[Object]
❌
MinItems: 0
distribution_license_id
String, Null
❌
-
related_public_services
Array[Object]
❌
-
valid_from
String (DateTime), Null
❌
-
valid_to
String (DateTime), Null
❌
-
removed_at
String (DateTime), Null
❌
-
expire_at
String (DateTime), Null
❌
-
more_info
String, Null
❌
-
normative_requirements
Array[URI]
❌
-
related_documents
Array[URI]
❌
-
life_events
Array[String]
❌
MinItems: 0
business_events
Array[String]
❌
MinItems: 0
allowed_readers
Array[String]
❌
MinItems: 0
tenant_id
String (UUID)
✅
Uuid del tenant
owner_id
String (UUID)
✅
Uuid del proprietario del documento
document_url
String (URI), Null
❌
-
created_at
String (DateTime)
✅
-
updated_at
String (DateTime)
✅
-
author
Array[Object]
❌
MinItems: 0
source_type
String
✅
Valori: tenant, user
recipient_type
String
✅
Valori: tenant, user
last_seen
String (DateTime), Null
❌
-
transmission_type
String
✅
Valori: Inbound, Outbound
date
String (Date)
❌
Data valida in formato YYYY-MM-DD
document_number
String
❌
Numero del documento
title
String
✅
Titolo descrittivo del fascicolo
id
String
❌
Identificativo del fascicolo
name
String
✅
Nome del file
description
String
✅
Descrizione del documento
mime_type
String
✅
Formato MIME, es: application/pdf
url
String (URI)
✅
URL del documento
md5
String
❌
Hash MD5 del file
filename
String
✅
Nome del file salvato sul server
name
String
✅
Nome del file allegato
description
String
✅
Descrizione dell'allegato
mime_type
String
✅
Formato MIME, es: application/pdf
url
String (URI)
✅
URL dell'allegato
md5
String
❌
Hash MD5 del file
filename
String
✅
Nome del file salvato sul server
type
String
✅
Valori: human, legal
tax_identification_number
String
✅
Codice fiscale
name
String
✅
Nome dell'autore
family_name
String
✅
Cognome dell'autore
street_name
String
✅
Indirizzo
postal_code
String
✅
Codice postale
String
✅
Formato: email valido
role
String
✅
Ruolo (es: sender, receiver)
id
UUID
user_id
UUID
type
string(50)
Ogni proxy deve implementare un tipo di pagamento. Se per esempio, il proxy in questione gestisce pagamenti pagopa allora il campo va validato verificando che esso sia valorizzato a PAGOPA
. In caso contrario si scarta l'evento e si emette un log di errore.
tenant_id
UUID
service_id
UUID
created_at
Datetime
ISO8601
updated_at
Datetime
ISO8601
status
Enum
Valori permessi: CREATION_PENDING CREATION_FAILED PAYMENT_PENDING PAYMENT_STARTED PAYMENT_CONFIRMED PAYMENT_FAILED NOTIFICATION_PENDING
COMPLETE EXPIRED
reason
string(140)
Causale del pagamento. Non può eccedere i 140 caratteri.
remote_id
UUID
payment
PaymentData
links
Links
payer
Payer
Soggetto Versante
debtor
Debtor
Soggetto Pagatore
event_id
UUID
event_version
string(10)
L'attuale versione dell'evento deve essere "1.0". Ogni proxy deve validare la versione dell'evento in modo da sapere se processarlo o meno.
event_created_at
Datetime
ISO8601
app_id
string(100)
Valorizzarlo nel formato <nome-proxy>:<versione-proxy>
Es.
iris-payment-proxy:1.3.0
type
Enum
Valori permessi:
PAGOPA
nel caso in cui si tratto di un dovuto standard
STAMP
nel caso in cui si tratta di un dovuto di tipo marca da bollo digitale
transaction_id
string(255)
Fornito dall'intermediario di pagamento. La lunghezza dunque può essere variabile
paid_at
Datetime
ISO8601
expire_at
Datetime
ISO8601 E' già valorizzato a priori, quindi non viene gestito dal proxy
amount
float
E' già valorizzato a priori, quindi non viene gestito dal proxy
reason
string(140)
Causale del pagamento. Non può eccedere i 140 caratteri.
currency
string(3)
ISO4217
notice_code
string(50)
iud
string(50)
iuv
string(50)
receiver
Receiver
Destinatario o beneficiario del versamento/dovuto
due_type
string(256)
Tipo di dovuto secondo la classificazione dell'intermediario di riferiemento
pagopa_category
string(12)
Tipo di dovuto secondo la tassonomia ufficiale di pagoPA
document
Document
Contiene le informazioni riguardanti il documento informatico o la segnatura di protocollo cui è associata la marca da bollo digitale
split
List[PaymentDataSplit]
tax_identification_number
string(255)
name
string(255)
iban
string(34)
ISO 13616 (obbligatorio solo dal secondo beneficiario in poi)
street_name
string(255)
building_number
string(255)
postal_code
string(255)
town_name
string(255)
country_subdivision
string(2)
ISO 3166-2
country
string(2)
ISO 3166-1 alpha-2
id
UUID
Identificativo del documento
ref
string
Url per scaricare il documento
hash
string
Hash digest del documento informatico
code
string(50)
amount
float
meta
json
online_payment_begin
UrlData
online_payment_landing
UrlData
offline_payment
UrlData
receipt
UrlData
notify
List[Notify]
update
Update
confirm
UrlData
cancel
UrlData
url
string
last_opened_at
Datetime
ISO8601
method
Enum
Valori permessi:
GET
POST
PUT
PATCH
DELETE
url
string
method
Enum
Valori permessi: GET POST
sent_at
Datetime
ISO8601
url
string
last_check_at
Datetime
ISO8601
next_check_at
Datetime
ISO8601
method
Enum
Valori permessi: GET POST
type
Enum
Valori permessi: human legal
tax_identification_number
string(255)
name
string(255)
family_name
string(255)
street_name
string(255)
building_number
string(255)
postal_code
string(255)
town_name
string(255)
country_subdivision
string(2)
ISO 3166-2
country
string(2)
ISO 3166-1 alpha-2
email
string(255)
type
Enum
Valori permessi: human legal
tax_identification_number
string(255)
name
string(255)
family_name
string(255)
street_name
string(255)
building_number
string(255)
postal_code
string(255)
town_name
string(255)
country_subdivision
string(2)
ISO 3166-2
country
string(2)
ISO 3166-1 alpha-2
email
string(255)
CREATION_PENDING
pagamento in attesa di essere creato sul provider di riferimento
CREATION_FAILED
pagamento di cui è fallita la creazione sul provider di riferimento
PAYMENT_PENDING
pagamento creato sul provider di riferimento e in attesa di essere eseguito dall'utente
PAYMENT_STARTED
procedura di pagamento iniziata dall'utente
COMPLETE
pagamento completato a seguito di conferma dal provider di riferimento
PAYMENT_FAILED
pagamento fallito a causa di scadenza del termine ultimo entro cui doveva essere eseguito
nei proxy sviluppati questo stato non è quasi mai stato utilizzato
CANCELED
pagamento annullato forzatamente da un operatore
oc_document_validation_errors_total
cluster, env, app_name
la metrica deve misurare gli errori di validazione sull'evento letto (es. il transmission_type
contiene un valore diverso da Inbound
o Outbound
)
oc_api_requests_total
cluster, env, method, app_name, status_code
la metrica deve monitorare le chiamate http indicandone lo status code
oc_document_success_events_total
cluster, env, app_name
la metrica deve misurare solo gli eventi per cui il proxy ha una configurazione e che sono stati processati con successo
oc_document_failed_events_total
cluster, env, app_name
la metrica deve misurare gli eventi validi di cui però è fallito il processing per qualsiasi motivo (escluso il caso in cui non esiste una configurazione per esso)
oc_document_provider_errors_total
cluster, env, app_name
la metrica deve misurare gli eventi validi di cui però è fallito il processing a causa di un errore sul provider
oc_document_internal_errors_total
cluster, env, app_name
la metrica deve misurare gli eventi validi di cui però è fallito il processing per errori interni al codice
oc_document_provider_latency_bucket
cluster, env, app_name
istogramma che mostra la distribuzione di latenza delle risposte del provider
ENVIRONMENT
local
Indica l'ambiente di sviluppo (locale, dev, prod, ecc.) utilizzato da Sentry.
DEBUG
true
...
SENTRY_ENABLED
false
...
SENTRY_DSN
nessun valore
Endpoint per il monitoraggio di Sentry.
KAFKA_SERVER
kafka:9092
Lista di lunghezza variabile i cui elementi devono essere separati da virgola. lista degli Indirizzi dei broker Kafka per connettersi al cluster.
KAFKA_CONSUMER_GROUP
<nome_del_servizio>
Consumer group per Kafka.
KAFKA_CONSUMER_TOPIC
documents
Identifica il topic da cui consumare gli eventi Kafka.
KAFKA_PRODUCER_TOPIC
documents
Identifica il topic a cui inviare gli eventi Kafka.
KAFKA_RETRY_TOPIC
nessun valore
topic in cui produrre gli eventi consumati dal meccanismo di retry
SERVER_ADDRESS_PORT
0.0.0.0:8080
Indica l'indirizzo e la porta utilizzati per l'healthcheck.
CACHE_EXPIRATION
5m
...
CACHE_EVICTION
10m
...
STORAGE_TYPE
local
Tipo di storage dei pagamenti: s3, azure, local
STORAGE_ENDPOINT
nessun valore
Indirizzo di accesso allo storage.
STORAGE_ACCESS_S3_KEY
nessun valore
Chiave di accesso allo storage.
STORAGE_KEY_S3_ACCESS_SECRET
nessun valore
Chiave segreta di accesso allo storage.
STORAGE_S3_REGION
nessun valore
Location del cloud storage
STORAGE_BUCKET
nessun valore
Nome dello storage
STORAGE_BASE_PATH
"/data/"
Basepath dello storage
STORAGE_AZURE_ACCOUNT
nessun valore
Chiave dello storage AZURE
STORAGE_AZURE_KEY
nessun valore
Password dello storage AZURE
STORAGE_LOCAL_PATH
/data/
SDC_AUTH_TOKEN_USER
nessun valore
utente autenticazione per recuperare il token dalla piattaforma
SDC_AUTH_TOKEN_PASSWORD
nessun valore
password autenticazione per recuperare il token dalla piattaforma
nome | descrizione |
title | titolo |
text | descrizione |
channels | [channel] |
nome | descrizione |
type | web, email, phone, ios o android |
href | link href |
label | link label |
Il campo "retry_meta" contiente le informazioni inserite dal sistema di retry. Queste dovrebbero essere ignorate da protocol proxy
Successful Response
Successful Response
Successful Response
Successful Response
"23d57b65-5eb9-4f0a-a507-fbcf3057b248"
Successful Response
"5f99646d-769e-4d73-ba28-cffd30a77e74"
Successful Response
"5f99646d-769e-4d73-ba28-cffd30a77e74"
Successful Response
"5f99646d-769e-4d73-ba28-cffd30a77e74"
Successful Response
"5f99646d-769e-4d73-ba28-cffd30a77e74"
Successful Response
"5f99646d-769e-4d73-ba28-cffd30a77e74"
Successful Response
"b212c4b4-db26-4404-8c7c-47dab99dd2e6"
Successful Response
"23d57b65-5eb9-4f0a-a507-fbcf3057b248"
Successful Response
"b212c4b4-db26-4404-8c7c-47dab99dd2e6"
Successful Response
"b212c4b4-db26-4404-8c7c-47dab99dd2e6"
Successful Response
"b212c4b4-db26-4404-8c7c-47dab99dd2e6"
Successful Response
Successful Response
Successful Response
"23d57b65-5eb9-4f0a-a507-fbcf3057b248"
Successful Response
"23d57b65-5eb9-4f0a-a507-fbcf3057b248"
Successful Response
L'aggiunta di un tema grafico richiede una modifica al Core della piattaforma, in queste pagine si da indicazione dei temi presenti e di come predisporre una Merge-Request per aggiungerne uno nuovo.
Step preparativi alla modifica:
Clonare il repository https://gitlab.com/opencity-labs/area-personale/core
Posizionarsi sul branch master
Creare un nuovo branch di nome add-theme-$themename e spostarsi sul nuovo branch con il comando git checkout -b "add-theme-$themename"
sostituendo con il nome del nuovo tema che si vuole creare (per le indicazioni sui nomi dei temi vedere sotto)
Creare una MR sul branch che effettui squash di tutti i commit del branch ed elimini il branch una volta mergiato come di seguito:
Successivamente creare un nuovo file in ./assets/styles
copiando il contenuto di seguito che dovrebbe coincidere con il file default.scss.
Il nome del file deve coincidere con il nome che avrà il tema e avere l'estensione .scss (l nome del tema può contenere solo lettere e il carattere `-`, deve rispondere cioè alla espressione regolare [a-z][a-z0-9-]+. Es: se si vuole creare il tema "amaranto" aggiungiamo un file amaranto.scss
):
Nel file cambiare le opzioni ed i colori a proprio piacimento
Si compila il nuovo tema su webpack aggiungendo nel file webpack.config.js StyleEntry come di seguito: .addStyleEntry('amaranto', './assets/styles/amaranto.scss')
(Il nome deve coincidere con il nome del file creato in precedenza). I colori devono tenere conto della linea guida Accessibilità.
Si aggiunge alla piattaforma il nuovo tema aggiornando nel file src/Model/Tenant/ThemeOptions.php la costante THEMES
e inserendo il nuovo tema come di seguito: 'Amaranto' => 'amaranto'
Con la versione 2 delle API di configurazione sono stati introdotti dei campi da introdurre obbligatoriamente in ogni integrazione.
I campi da introdurre obbligatoriamente a livello di configurazione del tenant sono:
id
: identificativo univoco del tenant
name
: nome del tenant
tax_identification_number
: codice fiscale dell'ente
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
I campi da introdurre obbligatoriamente a livello di configurazione del pagamento sono:
payment_type
: tipo di pagamento, può assumere i valori pagopa
o stamp
, serve a distinguere tra un pagamento ordinario (es. TARI) e una marca da bollo digitale. Questa distinzione è necessaria in quanto in alcuni intermediare i metodi di creazione di un pagamento di una marca da bollo digitale differiscono da quelli di creazione di un pagamento ordinario.
remote_collection.id
: identificativo della collezione di provenienza della configurazione di pagamento.
remote_collection.type
: tipo di collezione di provenienza della configurazione di pagamento, può assumere valori quali application
, service
o altro.
amount
: importo del pagamento configurato.
reason
: causale del pagamento configurato
expire_at
: data di scadenza del pagamento
receiver.tax_identification_number
: codice fiscale del beneficiario del pagamento, da usare in caso di pagamenti multibeneficiario
receiver.name
: nome del beneficiario del pagamento, da usare in caso di pagamenti multibeneficiario
collection_data
: tassonomia di pagoPA dei pagamenti
L'admin, dall'interfaccia di configurazione dei pagamenti della Stanza del Cittadino compila la configurazione mediante una form, il cui json schema è servito dall'API /tenants/schema
Lo schema della form sopra riportata è il seguente
Premendo poi il bottone Salva, viene eseguita una POST /tenants
servita dal proxy, con payload
Per modificare una configurazione esistente, il proxy serve l'API PUT /tenants/{tenant_id}
e PATCH /tenants/{tenant_id}
Per eliminare una configurazione esistente, il proxy serve l'API DELETE /tenants/{tenant_id}
. In questo caso l'eliminazione è una soft-delete, ovvero la configurazione viene semplicemente disattivata settando il parametro active
a false
ed eliminando la configurazione dalla memoria ma non dallo storage.
L'admin, dall'interfaccia di configurazione dei pagamenti per un servizio compila la configurazione mediante una form, il cui json schema è servito dall'API /configs/schema
Lo schema della form soprariportata è il seguente
Premendo poi il bottone Salva, viene eseguita una POST /configs
servita dal proxy, con payload
Per ottenere una lista di configurazioni di pagamento (fino a un massimo di 5), il proxy serve l'API GET /configs?config_id=config_id1&config_id=config_id2
Per modificare una configurazione esistente, il proxy serve l'API PUT /configs/{config_id}
e PATCH /configs/{config_id}
Per eliminare una configurazione esistente, il proxy serve l'API DELETE /configs/{config_id}
. In questo caso l'eliminazione è una soft-delete, ovvero la configurazione viene semplicemente disattivata settando il parametro active
a false
.
Metriche Prometheus
Successful Response
Get local Schema
Successful Response
Disable Tenant Configuration
Successful Response
Disable Service Configuration
Successful Response
Get Tenant Form Schema
Successful Response
Get Service Form Schema
Successful Response
Create New Tenant Configuration
Successful Response
Edit Existing Tenant Configuration
Successful Response
Get Service Status
Successful Response
Create New Service Configuration
Successful Response
Create New Tenant Configuration
Successful Response
Campo | Tipo | Obbligatorio | Validazione |
---|---|---|---|
id
UUID
Identificativo univoco
name
String
Non vuoto, massimo 255 caratteri
tax_identification_number
String
Codice fiscale valido
payment_type
Enum
Valori: pagopa
, stamp
remote_collection.id
UUID
Identificativo univoco
remote_collection.type
String
Valori: application, service, altro
amount
float
Maggiore di 0
reason
String
Non vuoto, massimo 255 caratteri
expire_at
DateTime
Data futura valida
receiver.tax_identification_number
String
Codice fiscale valido
receiver.name
String
Non vuoto, massimo 255 caratteri
collection_data
String
Tassonomia pagoPA valida
Successful Response
Successful Response
Successful Response
Successful Response
Successful Response
Successful Response
Successful Response
Successful Response
"23d57b65-5eb9-4f0a-a507-fbcf3057b248"
Successful Response
"b212c4b4-db26-4404-8c7c-47dab99dd2e6"
Successful Response
"b212c4b4-db26-4404-8c7c-47dab99dd2e6"
Successful Response
"23d57b65-5eb9-4f0a-a507-fbcf3057b248"
Successful Response
"b212c4b4-db26-4404-8c7c-47dab99dd2e6"
Successful Response
"b212c4b4-db26-4404-8c7c-47dab99dd2e6"
Successful Response
Successful Response
"23d57b65-5eb9-4f0a-a507-fbcf3057b248"
Successful Response
Lista id delle configurazioni di pagamento
Successful Response
"23d57b65-5eb9-4f0a-a507-fbcf3057b248"
Successful Response
Successful Response