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...
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...
Loading...
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.
Per facilitare il lavoro di chi lavora su integrazioni e sulle API della piattaforma, forniamo una visione generale della roadmap.
Per ulteriori dettagli si veda https://gitlab.com/groups/opencity-labs/-/milestones
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.
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.
In questa area della documentazione si fornisce una descrizione mediante diagrammi dell'architettura logica della piattaforma.
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.
Nuova Area Personale del cittadino, per le pratiche generate dalla piattaforma e per le pratiche generate in altre piattaforme.
Erogazione dei servizi digitali mediante Widget React
Gestione di Documenti in Area Personale
Estensione del sistema di notifica al di fuori del flusso delle pratiche.
Pagamenti in Area Personale e integrazione con Checkout
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)
Riguardo a coding, contribuzioni, eventi, API
Nella piattaforma si fa ampio uso del Pattern Event Sourcing è quindi fondamentale che il formato degli eventi sia stabile e documentato.
Per ogni evento i producers DEVONO inserire sempre i seguenti dati:
id
identificativo unico dell'entità oggetto dell'evento
UUID
Es: in un pagamento è l'ID del pagamento, in una pratica è l'application_id etc...
event_id
identificativo unico dell'evento
UUID
Non deve esserci per nessun motivo due volte lo stesso ID
event_version
versione dell'evento
Integer or Float
E' un intero (Es: 1, 2, etc..) cambia ad ogni cambiamento NON retrocompatibile.
event_created_at
data dell'evento
ISO8601
Ricordarsi di usare il formato della timezone Europe/Rome (Es: 2022-06-22T15:11:20+02:00
)
app_id
Nome e versione dell'applicativo che ha generato l'evento
$application:$version
Utile per motivi di debug (Es: payment-dispatcher:1.0.15).
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. In condizioni normali un servizio che consuma eventi emette una riga di log a livello INFO se e solo se ha gestito l'evento e ha fatto qualcosa con quell'evento che ha avuto successo.
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. Le letter T e Z DEVONO essere espresse in maiuscolo.
Es: 2022-06-22T15:11:20+02:00
Facciamo in generale riferimento alle linee guida di Zalando, tenendo ovviamente in considerazione anche i requisiti e le raccomandazioni delle Linee Guida di Interoperabilità della PA.
i servizi DEVONO agire solo su verbi HTTP supportati, rispettarne la semantica, restituire errore sugli altri.
i metodi DELETE, GET, HEAD, OPTIONS, PUT, TRACE devono essere idempotenti (vd anche RFC7231)
le API hanno path al plurale sulle collezioni, per separare le parole si usa il kebab-case (il trattino).
nelle risposte le URL dovrebbero essere sempre espresse in modo assoluto
Le collezioni devono essere facilmente navigabili, per questo abbiamo adottato il seguente standard:
Le GET sulle risorse devono USARE laddove supportati i query parameters convenzionali:
sort
offset
limit
cursor
embed
q
fields
Su alcuni campi è utile implementare anche l'opzione di paginazione cursor-based:
GET /payments?created_since=$timeStamp
Fare riferimento alla RFC 7807
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
Quello che ci si aspetta è che si possa usare il docker-compose.yml scaricandolo direttamente sul proprio pc, possibilmente senza necessità di clonare tutto il repository.
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; Perché sia pienamente usabile è necessario che le immagini facciano riferimento al registry pubblico (nostro o meno).
usare un docker-compose.dev.yml
per aggiungere tool utili per lavorare in locale durante lo sviluppo.
Per esempio la chiave "build" per le immagini create deve essere presente nel file ".dev" e non nel file compose principale.
In questo modo scaricando il repository e rinominando il file .dev in docker-compose.override.yml
ci si mette nelle condizioni ideali per sviluppare il servizio e un docker-compose up
comporterà automaticamente l'uso in cascata dei file docker-compose.yml
e docker-compose.override.yml.
Per ulteriori dettagli consultare il manuale di Docker: Merge compose files.
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
Vanno tenute sempre in considerazione le raccomandazioni implementative di Agid, espresse nell'Allegato 4 alle Linee Guida di Interoperabilità.
Il log deve supportare almeno tre livelli: debug
, info
e error
. Una gestione matura comprende 6 livelli distinti di log
CRITICAL
Fallimento grave, con perdita di dati o che comporta lo l'uscita dal flusso di esecuzione (shutdown) del servizio per impossibilità a proseguire o perché è più safe non proseguire.
No
ERROR
Anomalia che non è possibile gestire e che avrà effetti sul risultato. 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. Attenzione a non confondere errori che si verificano su sistemi esterni e che non sono nostri errori. Date sempre per scontato che una riga di errore prodotta dovrebbe sempre essere letta da qualcuno, altrimenti meglio esporre un livello di warning o non loggare proprio. Una anomalia dovrebbe sempre dare origine a una e una sola riga di log.
Si
WARNING
Anomalia che non avrebbe dobuto presentarsi ma che è stata gestita correttamente per non comportare errori nel nostro servizio. Ad esempio ho ricevuto un evento con una versione negativa, ma l'ho ignorato. Oppure non sono riuscito a collegarmi al db la prima volta, ma solo dopo 2 tentativi.
Si
INFO
Comunica un cambiamento di stato del servizio. Un evento significativo produce una riga di log per ogni evento o chiamata ricevuta dall'applicativo. Se una transazione o un evento gestito presenta errori NON si deve produrre la riga di INFO e la riga di ERROR, ma solo la seconda.
Si
DEBUG
Questo è il livello in cui comunicare informazioni diagnostiche, non necessarie se non si sta investigando un errore specifico. Devono essere informazioni utili a comprendere errori, non semplicemente a comprendere il flusso interno del software, per il quale esiste il livello apposito (TRACE). Una informazione utile è dare tutto il contesto che permette di comprendere perché il servizio si sta comportando in un certo modo.
No
TRACE
Questo livello è inteso per tracciare il flusso interno del servizio, in modo molto dettagliato. Per esempio si può inserire un log a questo livello per capire in quale ramo del codice siamo finiti con l'esecuzione, oppure si può inserire una riga all'inizio e una alla fine di certe porzioni di codice rilevanti.
No
Non mi schiare log http e log multiriga (stacktrace o similari): se si è costretti, per esempio inviare lo stacktrace a stderr
e riservare stdout
ai log a singola riga
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à. Non è altrettanto utile annegare del json dentro un altro json: per esempio quando si logga che l'applicativo ha mandato un certo json in un certo topic di kafka... per avere il json completo si può vedere dentro kafka, nel log è sufficiente inserire l'id dell'evento.
Sul contenuto:
DEVE esserci l'istante della comunicazione in formato UTC (RFC 3339) e con separatori Z e T in maiuscolo.
DEVE contenere la URI o l'identificazione dell'operazione che si sta eseguendo o il nome dell'evento ricevuto.
Dovrebbe contenere la tipologia della chiamata, ad esempio il verbo HTTP o se si tratta di una chiamata fatta da CLI o in un azione batch.
DEVE contenere l'esito della chiamata
Se è una chiamata fatta da un client DEVE essere inserito l'IP sorgente della chiamata (attenzione a rispettare eventuali header X-FORWARDED-XXX)
Se è una chiamata fatta per conto di un essere umano è possibile inserire un riferimento all'utente, ma NON DEVE contenere dati personali in chiaro (il codice fiscale ad esempio deve essere troncato al mssimo alle prime 7 lettere). Per avere un riferimento certo inserire lo userid
della piattaforma.
Se disponibile DEVE inserire l'identificatore unico della chiamata o dell'evento (event_id
).
All'inizio della sua esecuzione ogni servizio DEVE loggare il proprio avvio dichiarando anche la versione del servizio e l'environment in cui sta girando: boat-qa, boat-prod, local
, etc...
Il microservizio DEVE integrare Sentry per il monitoraggio centralizzato degli errori.
Una volta operativa l'integrazione è necessario:
abilitare l'integrazione con sentry sul repo gitlab
inserire regole di allerta per quel microservizio, se le regole esistenti non sono sufficienti
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).
il servizio NON deve mostrare metriche che derivano ad esempio da errori 404, perché i bot che scansionano tutte le URL provocano infinite varianti che comportano la creazione di infinite metriche di errore
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.Questo applicativo viene poi schedulato nel nostro ambiente di produzione istruendo il nostro clustered cron. Per istruzioni sulla configurazione si rimanda alla documentazione di crazymax-cronjob.
in docker c'e' sempre un mapping tra esterno e interno, inutile parametrizzare questi aspetti in modo particolarmente flessibile: il controllo sul filesystem interno è sempre in mano allo sviluppatore del microservizio, quindi si può sempre usare un path interno del tipo /data
senza preoccuparsi tanto del fatto che all'esterno quel path diventerà qualcosa come /srv/data/qualcosa/altro/data.
Per le configurazioni e' importante usare sempre lo strumento configs
di docker swarm, senza ricorrere al volume mapping.
Analogamente per le credenziali o le parti segrete DEVE essere usato lo strumento dei secrets.
Ogni progetto può inserire nella CI attività di linting del codice e enforcemente dei coding styles
Ogni progetto DEVE includere la CI condivisa, nel seguente modo:
Eventuali step di test devono essere inclusi dopo questo snippet di codice e devono sfruttare la fase di build già eseguite, per esempio se viene fatta la build di un binario la si può sfruttare negli step successivi mediante un artifact.
La durata della CI non DEVE mai superare i 15 minuti e non dovrebbe mai superare i 5: questo perché soprattutto in condizioni di emergenza, fare un hotfix non può richiedere troppo tempo, è necessario che ogni step sia ben ottimizzato per ottenere questo scopo, per esempio sfruttando le build multi-stage di docker o la cache di Gitlab CI.
in caso di pubblicazione su Developers Italia, la correttezza del public-code.yml
deve essere testato con:
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.
In questa sezione viene descritto in dettaglio il json del pagamento e la definizione dettagliata dei campi
Questo documento descrive gli aspetti tecnici dell'importazione delle Pratiche Lite, uno strumento che permette di integrare le pratiche provenienti da altri gestionale.
Il documento è in fase di stesura, le informazioni in esso contenuto, i protocolli e i jsonSchema possono subire variazioni anche importanti.
La nostra piattaforma permette al cittadino di interagire sia con le pratiche generate sulla piattaforma stessa sia con pratiche generate su altri gestionali. Cittadini e Operatori potranno, da un'unica interfaccia, interagire con un elenco uniforme di pratiche.
Per realizzare questa integrazione è stato ideato il concetto di Pratiche Lite, che rappresenta una pratica con un set minimo di dati, necessari a visualizzare e interagire correttamente l'elenco delle pratiche agli utenti a differenza delle Pratiche che nascono all'interno della piattaforma che contengono tutti i dati delle pratiche. Ma non solo, è possibile visualizzare anche informazioni relative ai documenti correlati alla pratica e i pagamenti.
Sarà necessario fornirci un elenco di Pratiche Lite per esporle sulla nostra piattaforma. Questi dati potranno essere forniti tramite API ReST in formato JSON, oppure mediante file CSV.
Integrare le pratiche generare da altri gestionali nell'area personale permette al cittadino di:
consultare in un unico posto tutte le pratiche relative a un cittadino, anche se generate da altri sistemi;
seguire l'iter della pratica
consultare i dati essenziali di una pratica, come il richiedente e il beneficiario;
consultare i documenti associati alla pratica e lo stato della protocollazione;
consultare i pagamenti associati alla pratica;
dialogare con gli operatori mediante la funzione messaggi dell'Area personale.
Ma l'area personale non è solo consultazione, il cittadino, se l'integrazione lo prevede, potrà comunque interagire con le pratiche, anche se generate da sistemi esterni. Operazioni come la visualizzazione completa della pratica, la modifica, l'integrazione dei documenti o l'annullamento della richiesta, saranno eseguite direttamente sul gestionale esterno, invocando gli opportuni endpoint HTTP corrispondenti alle azioni da eseguire. Questi endpoint verranno specificati pratica per pratica all'atto dell'importazione, se non specificati
Questa è una rappresentazione schematica dei dati che è possibile inviare:
Pratica: titolo e stato della pratica
Richiedente: nome, cognome, codice fiscale e contatti del richiedente
Beneficiario : nome, cognome, codice fiscale e contatti del beneficiario
Servizio: servizio collegato alla pratica
Endpoint delle azioni: endpoint http delle azioni eseguibili sulla pratica
Documenti: documenti collegati alla pratica e stato di protocollazione
Pagamenti: pagamenti collegati alla pratica
Nei capitoli seguente verranno mostrati i tracciati dettagliati di import.
Il seguente diagramma mostra i possibili stati in cui può trovarsi una pratica:
Bozza: in questo stato la pratica non è considerata ancora completamente compilata e può essere modificabile
Inviata: la pratica è stata compilata in ogni sua parte da cittadino e definitivamente inviata. In questo stato non è più possibile modificare i dati delle pratica
Presa in carico: la pratica è stata presa in carico da un Operatore, il quale effettuerà le normali operazioni d'ufficio necessarie a gestirla
In attesa di integrazione: l'operatore ha richiesto di integrare la pratica con informazioni e/o documenti mancanti. La pratica resta in questo stato fino a quando non si ottiene una risposta dal Cittadino
Rifiutata: la pratica non è stata accettata dall'operatore che l'ha gestita
Ritirata: la pratica è stata ritirata dal cittadino prima che l'ufficio si sia pronunciata
Annullata: quando l'ufficio definisce che non si procederà ulteriormente con la pratica e non si pronuncerà su ulteriore approvazione o rifiuto
Supportiamo tre modalità di integrazione:
Modalità push tramite API: è possibile inviarci tramite apposita API i dati delle pratiche per inserire nuove pratiche o per aggiornarne lo stato. Questa modalità di aggiornamento è utile per mantenere costantemente aggiornati i dati delle pratiche tra due sistemi, avendo quindi modifiche immediate per il cittadino. Questa modalità garantisce la possibilità di creare un dialogo col cittadino.
Modalità di invio massivo (batch): permette di importare una grande quantità di dati ad ogni invocazione ma non garantisce l'esecuzione in tempo reale. È utile sopratutto per le prime importazioni, o in tutti quei casi in cui non ho necessità di avere uno stato aggiornato in tempo reale. È possibile fornire i dati nei formati CSV o JSON.
Modalità polling: il sistema esterno espone un API da invocare a scadenza regolare che permette al nostro sistema di recuperare gli aggiornamenti. Fondamentale che API dia la possibilità di recuperare gli aggiornamenti incorsi in un intervallo di tempo
aggiornamento del dato per il cittadino
in tempo reale
latenza di ore
a intervalli di tempo (minuti/ore)
gestione degli errori latenza per correggere un errore
errori gestiti in tempo reale
errori gestiti alla fine del batch
errore gestito alla fine dell'intervallo
Per effettuare un'importazione è necessario autenticarsi ed ottenere un token di autenticazione
È possibile seguire questa guida per le istruzioni di autenticazione https://docs.opencityitalia.it/developers/integrazioni/integrazioni-con-il-flusso-delle-pratiche/api-rest
Autenticazione standard nel modello 1 e 2
Modello 3: basic auth, client certificate, api key
Questa è la rotta per effettuar il singolo import di Pratica, Documenti e Pagamenti
Come è possibile vedere dal tracciato riportato nel paragrafo successivo, è possibile inviare in un solo colpo i dati relativi a pratiche, documenti e pagamenti. Questi tre elementi si considerano correlati tra di loro, quindi, documenti e pagamenti, si intendono relativi alla pratica inviata e verranno relazionati a essa.
Le pratiche verranno identificate in maniera univoca dal campo external_id che rappresenta un identificativo del sistema sorgente, se non presente, verrà creata con i dati passati.
La riconciliazione di richiedente e beneficiario (applicant e beneficiary) verrà effettuata basandosi sul codice fiscale (tax_code). Se nel sistema esiste già un utente con quel codice fiscale, verranno utilizzati i dati già presente nel database ignorando i dati di nome, cognome e contatti che vengono passati. Al contrario, se non presenti, verrà importato un nuovo utente con i dati specificati.
Lo stato della pratica segue il flusso definito nei capitoli precedenti, tuttavia non è stato implementato un controllo stringente sul flusso della pratica, in controllo dello stato della pratica è demandato al sistema sorgente.
Lo stato della pratica regola però le azioni che il cittadino potrà effettuare. Nella sezione links è possibile specificare le azioni permesse sulla pratica è l'URL da invocare al click del cittadino. Se una specifica azione non è specificata non verrà proposta al cittadino. Le azioni verranno proposte al cittadino anche in base allo stato della pratica, per esempio, una pratica in stato inviata non può più essere modificata, quindi, anche se indicata l'azione MODIFY, non verrà comunque proposta.
Questo è lo JSON Schema della richiesta
Il seguente endpoint serve per il cambio di stato di una pratica lite. Permette opzionalmente di inviare un messaggio al richiedente contestualmente al cambio stato
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
username*
String
nome utente
password*
String
password
Alcuni esempi di chiamate
Esempio di chiamata da linea di comando con il client Httpie:
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.
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.
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:
NOTA 1: per gli sviluppatori python è stata definita una libreria a cui fare riferimento per la validazione del nuovo evento, è consultabile .
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 .
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.
Nota importante
payment.reason
payment.payment.amount
payment.payment.expire_at
payment.payment.receiver
In sintesi quindi: se questi campi sono valorizzati nell'evento payment, allora li si legge da lì, altrimenti li si legge dalla configurazione inserita via API.
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.
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
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:
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
.
Logica di polling
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.
Nello specifico il polling avviene con al seguente logica:
Condizione iniziale:
Se il campo last_check_at
è nullo (non è stato effettuato alcun controllo in precedenza), la funzione termina immediatamente senza aggiornare il prossimo controllo.
Checkpoint temporali: La funzione utilizza diversi checkpoint per classificare il tempo trascorso dalla creazione della risorsa:
5 minuti
15 minuti
7 giorni
30 giorni
365 giorni
Ciascun intervallo ha una frequenza di controllo associata:
Fino a 5 minuti: Controlli ogni 1 minuto.
Fino a 15 minuti: Controlli ogni 5 minuti.
Fino a 7 giorni: Controlli ogni ora.
Fino a 30 giorni: Controlli ogni 6 ore.
Fino a 365 giorni: Controlli settimanali, sempre alla fine della settimana corrente.
Oltre 365 giorni: La risorsa è considerata scaduta, lo stato è impostato su STATUS_EXPIRED
e il prossimo controllo è disabilitato (next_check_at
impostato a None
).
Impostazione del prossimo controllo:
Se determinato, il next_check
è aggiornato al valore calcolato
Aggiornamento dello stato:
Se il tempo dalla creazione supera i 365 giorni, lo stato della risorsa viene aggiornato a STATUS_EXPIRED
.
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
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
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
: numero di giorni di validità 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
.
Alcuni dei campi che vengono popolati in fase di creazione del pagamento sull'intermediario si leggevano solo dall'evento payment. Con l'upgrade delle API (i cui aggiornamenti sono consultabili ), questi campi potrebbero non essere valorizzati nell'evento, in tal caso, vanno letti invece dalla configurazione inserita via API. I campi in questione sono:
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 , a questa url andrà aggiunto in query string un parametro payment
da valorizzare con OK
o KO
a seconda dell'esito del pagamento
Interroga la tabella PAYMENTS_DETAIL
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 )
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)
è il valore del campo id
del pagamento in formato esadecimale
Es.
id: 4a263efb-300b-437a-ab50-116fa9aff8a7
iud: 4a263efb30
0b-437aab50116fa9aff8a7
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
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
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
int
Maggiore di 0
receiver.tax_identification_number
String
Codice fiscale valido
receiver.name
String
Non vuoto, massimo 255 caratteri
collection_data
String
Tassonomia pagoPA valida
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
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
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
currency
string(3)
ISO4217
notice_code
string(50)
iud
string(50)
è il valore del campo id
del pagamento in formato esadecimale
Es.
id: 4a263efb-300b-437a-ab50-116fa9aff8a7
iud: 4a263efb30
0b-437aab50116fa9aff8a7
iuv
string(50)
split
jsonArray
online_payment_begin
UrlData
online_payment_landing
UrlData
offline_payment
UrlData
receipt
UrlData
notify
List[Notify]
update
Update
url
string
last_opened_at
Datetime
ISO8601
method
Enum
Valori permessi: GET POST
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)
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 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.
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.
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
❌
-
registration_data
transmission_type
String
✅
Valori: Inbound, Outbound
date
String (Date)
❌
Data valida in formato YYYY-MM-DD
document_number
String
❌
Numero del documento
folder
title
String
✅
Titolo descrittivo del fascicolo
id
String
❌
Identificativo del fascicolo
main_document
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
attachments
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
author
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)
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
Il campo "retry_meta" contiente le informazioni inserite dal sistema di retry. Queste dovrebbero essere ignorate da protocol proxy
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.
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 back-off 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 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
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
Integrarsi con sentry per la gestione degli errori
Esporre metriche di monitoraggio in formato prometheus, in particolare:
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
oc_payment_validation_errors_total
cluster, env, app_name
la metrica deve misurare gli errori di validazione sull'evento letto (es. l'importo è una stringa invece che un float)
oc_api_requests_total
cluster, env, method, app_name, status_code
la metrica deve monitorare le chiamate http indicandone lo status code
oc_payment_success_events_total
cluster, env, app_name
la metrica deve misurare solo gli eventi per cui il proxy ha una configurazione e che stati processati con successo
oc_payment_failed_events_total
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)
oc_payment_provider_errors_total
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
oc_payment_internal_errors_total
cluster, env, app_name
la metrica deve misurare gli eventi di pagamento validi di cui però è fallito il processing per errori interni al codice
oc_payment_provider_latency_bucket
cluster, env, app_name
istogramma che mostra la distribuzione di latenza delle risposte del provider
Inoltre il servizio deve rispettare gli standard della piattaforma 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: http://gitlab.com/opencontent/stanza-del-cittadino/efil-payment-proxy
IRIS: http://gitlab.com/opencontent/stanza-del-cittadino/iris-payment-proxy
MYPAY: http://gitlab.com/opencontent/stanza-del-cittadino/mypay-payment-proxy
PMPAY: http://gitlab.com/opencontent/stanza-del-cittadino/pmpay-payment-proxy
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.
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
Configurazione per mostrare il campo Valuta l'importanza del problema
Dalla versione 1.14.3
OC_SHOW_SEVERITY_FIELD
gestisce l'abilitazione del campo Valuta l'importanza del problema
nel form della segnalazione disservizio. Di default il campo è nascosto.
Personalizzazione campi obbligatori per la segnalazione disservizio.
Dalla versione 1.16.7
Per rendere obbligatori altri campi del form delle segnalazioni oltre a quelli già previsti di default è possibile popolarewindow.OC_REQUIRED_FIELDS
con la lista dei campi che si vuole rendere obbligatori.
lista dei campi che è possibile rendere obbligatori
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
Nelle versione precedenti alla 1.17.0 il routing del widget funzionava solo tramite # HashRouting
Aggiungendo la nuova variabile d'ambiante window.OC_BASENAME
è possibile trasformare il routing in versione BrowserRouting
Per configurare la variabile window.OC_BASENAME
c'è bisogno del path dove è configurato il widget con eventuali sottodomini se il widget è configurato in path non di primo livello.
Es. https://sito.it -> window.OC_BASENAME='/'
Es. https://sito.it/segnalazioni -> window.OC_BASENAME='/segnalazioni'
Es. https://sito.it/segnalazioni/123 -> window.OC_BASENAME='/segnalazioni/123'
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
Se si hanno altri tipi di autenticazione oltre a SPID è possibile personalizzare il titolo del popup.
Il pulsante Accedi dopo aver effettuato la login può mostrare un menù a tendina con vari 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 auto-aggiornante
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
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
Dettaglio degli stati che attraversa un pagamento nel suo ciclo di vita
CREATION_PENDING
pagamento in attesa di essere creato sull'IdP
CREATION_FAILED
pagamento di cui è fallita la creazione sull'IdP
PAYMENT_PENDING
pagamento creato sull'IdP e in attesa di essere eseguito dall'utente
PAYMENT_STARTED
procedura di pagamento iniziata dall'utente
COMPLETE
pagamento completato a seguito di conferma dalI'dP
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
Questa pagina descrive l'architettura impiegata per attuare il processo di protocollazione di una pratica
Seguendo l'approccio del 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.
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.
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
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.
Ogni microservizio deve rispettare gli
Ogni proxy deve soddisfare i requisiti suggeriti dal
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
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 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
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 pipeline
Il Protocol Proxy deve esporre le seguenti metriche:
Avere una specifica in formato OpenAPI v3
logging (4.4)
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.
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
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 :
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
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:
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
.
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
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:
Es.
Es.
Tutte le versioni del widget sono disponibili sulla
Solitamente la protocollazione di un documento digitale è accompagnata anche dalla protocollazione dei relativi allegati(menzionati nel paragrafo ) 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.
Le API della piattaforma sono documentate al seguente link:
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 con credenziali fornite tramite variabili d'ambiente(utente e password). Mentre nel secondo caso avremo una post auth all'endpoint con credenziali fornite tramite variabili d'ambiente(utente e password).
Rispettare la : in particolare per quanto riguarda
(4.2)
(4.3) optando per la scelta di snake_case
per i nomi degli attributi
risultare valido nel
Ogni richiesta sia essa appartenente alla fase 1 o alla fase 2 descritte nella sezione "" devono essere trattate in maniera atomica.
Test automatici delle API nella CI (usare , un esempio di utilizzo e un esempio di integrazione nelle CI)
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
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.
nome
descrizione
type
Tipo di disservizio (categoria)
address
Luogo (indirizzo)
fiscal_code
Codice fiscale
severity
Valuta l'importanza del problema
nome
descrizione
title
titolo
text
descrizione
channels
[channel]
nome
descrizione
type
web, email, phone, ios o android
href
link href
label
link label
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
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
La piattaforma supporta il multilinguismo dal punto di vista tecnico. Le due lingue supportate sono Italiano e Tedesco
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:
https://servizi.comune-qa.bugliano.pi.it/lang/api/doc#jobs
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
Esempio dettagliato di una integrazione di un modulo di Opencity Italia che attinge a una API protetta mediante l'API gateway opensource GovWay di Link.it
API privata, che si intende esporre mediante GovWay:
L'API risponde un dato privato se viene correttamente inserito un parametro in get tax_code che identifica il codice fiscale del cittadino che esegue la richiesta:
L'API non ha un controllo interno che permette di rispondere ad ogni cittadino solo con i suoi dati, l'API è aperta e risponde a qualunque chiamata.
In questo esempio si desidera esporre l'API privata su un indirizzo pubblico e filtrare le chiamate in modo che ogni cittadino possa consultare l'API solo per i propri dati.
L'API esposta è la seguente:
Quando viene chiamata quest'ultima url si desidera imporre un controllo sulla la presenza del token JWT e sul parametro della chiamta. La chiamata deve raggiungere l'API privata SOLO se:
il token è presente
il token è validato mediante la chiave pubblica esposta in JWKS
viene passato il parametro tax_code
in GET
il parametro tax_code corrisponde al valore username
del token
Quest'ultimo punto è il più importante perché ci da la certezza che il cittadino che sta facendo la richiesta è lo stesso che si è autenticato.
E' necessario creare una token policy, che poi verrà assegnata all'erogazione delle API
Il file jwks è stato scaricato da https://servizi.comune-qa.bugliano.pi.it/.well-known/jwks.json e inserito in un path raggiungibile da GovWay: nel nostro caso essendo GovWay installato con Docker abbiamo fatto uso della direttiva volumes
per condividere il file con il container di GovWay.
Fare attenzione inoltre che il kid
della chiave pubblica è specifico per ogni tenant e deve essere quello corretto.
Predisposta la policy deve essere applicata all'Erogazione API
Il controllo del contenuto viene fatto imponendo che l'informazione riportata nel token (${tokenInfo:username}
) e quella inserita come parametro GET della chiamata (${query:filter[tax_code]}
) abbiano lo stesso valore. Resta inteso che il valore nel token è vincolato dalla piattaforma, mentre il parametro tax_code
è scelto da chi ha progettato l'API che viene protetta.
A questo punto la configurazione è completa.
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
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'
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.
Come descritto nella sezione Single Sign-On è possibile per un utente autenticato richiedere un token di autenticazione per la propria sessione.
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
/status
No body
/online-payment/{payment_id}
5f99646d-769e-4d73-ba28-cffd30a77e74
No body
/notice/{payment_id}
5f99646d-769e-4d73-ba28-cffd30a77e74
No body
/receipt/{payment_id}
5f99646d-769e-4d73-ba28-cffd30a77e74
No body
/update/{payment_id}
5f99646d-769e-4d73-ba28-cffd30a77e74
No body
/landing/{payment_id}
5f99646d-769e-4d73-ba28-cffd30a77e74
No body
/notify-payment
No body
/tenants/schema
No body
/tenants/{tenant_id}
b212c4b4-db26-4404-8c7c-47dab99dd2e6
/tenants/{tenant_id}
b212c4b4-db26-4404-8c7c-47dab99dd2e6
No body
/tenants/{tenant_id}
b212c4b4-db26-4404-8c7c-47dab99dd2e6
No body
/tenants/{tenant_id}
b212c4b4-db26-4404-8c7c-47dab99dd2e6
No body
/tenants
No body
/services/schema
No body
/services/{service_id}
23d57b65-5eb9-4f0a-a507-fbcf3057b248
/services/{service_id}
23d57b65-5eb9-4f0a-a507-fbcf3057b248
No body
/services/{service_id}
23d57b65-5eb9-4f0a-a507-fbcf3057b248
No body
/services/{service_id}
23d57b65-5eb9-4f0a-a507-fbcf3057b248
No body
/services
No body
Metriche Prometheus
/metrics
Get Status
/status
Get local Schema
/schema
No body
Create New Tenant Configuration
/tenants/
No body
Save Service Configuration
/services/
No body