Un pagamento in dettaglio

Descrizione dettagliata di tutti i passaggi che portano alla creazione e alla chiusura di un pagamento a partire da una pratica.

Premessa

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.

Step 1

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:

{
  "app_version": "2.6.0-rc.7",
  "attachments": [],
  "authentication": {
    "authentication_method": "cps/cns",
    "certificate": "DQpSZXN1bHQgZ29lcyBoZXJlLi4uDQpCYXNlNjQNCg0KQmFzZTY0IGlzIGEgZ2VuZXJpYyB0ZXJtIGZvciBhIG51bWJlciBvZiBzaW1pbGFyIGVuY29kaW5nIHNjaGVtZXMgdGhhdCBlbmNvZGUgYmluYXJ5IGRhdGEgYnkgdHJlYXRpbmcgaXQgbnVtZXJpY2FsbHkgYW5kIHRyYW5zbGF0aW5nIGl0IGludG8gYSBiYXNlIDY0IHJlcHJlc2VudGF0aW9uLiBUaGUgQmFzZTY0IHRlcm0gb3JpZ2luYXRlcyBmcm9tIGEgc3BlY2lmaWMgTUlNRSBjb250ZW50IHRyYW5zZmVyIGVuY29kaW5nLg==",
    "certificate_issuer": "FAKE_issuerdn",
    "certificate_subject": "FAKE_subjectdn",
    "instant": "2000-01-01T00-00Z",
    "session_id": "abc123abc123abc123abc123abc123abc123abc123",
    "session_index": "abc123abc123abc123abc123abc123abc123abc123",
    "spid_code": null,
    "spid_level": null
  },
  "backoffice_data": null,
  "compiled_modules": [],
  "created_at": "2022-12-06T17:43:42+01:00",
  "creation_time": 1670345022,
  "data": {
    "applicant.data.Born.data.natoAIl": "1976-09-01T00:00:00+02:00",
    "applicant.data.Born.data.place_of_birth": "Ponte di Piave",
    "applicant.data.address.data.address": "Via Gramsci, 1",
    "applicant.data.address.data.county": "PI",
    "applicant.data.address.data.house_number": "",
    "applicant.data.address.data.municipality": "Bugliano",
    "applicant.data.address.data.postal_code": "56056",
    "applicant.data.completename.data.name": "Vittorino",
    "applicant.data.completename.data.surname": "Coliandro",
    "applicant.data.email_address": "raffaele.luccisano@opencontent.it",
    "applicant.data.fiscal_code.data.fiscal_code": "CLNVTR76P01G822Q",
    "applicant.data.gender.data.gender": "maschio",
    "payment_amount": "1,34"
  },
  "event_created_at": "2022-12-06T17:43:51+01:00",
  "event_id": "01e86cd4-8263-4ac0-a229-8cada5916eae",
  "event_version": 2,
  "flow_changed_at": "2022-12-06T17:43:51+01:00",
  "id": "a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e",
  "integrations": [],
  "latest_status_change_at": "2022-12-06T17:43:51+01:00",
  "latest_status_change_time": 1670345031,
  "links": [],
  "meetings": [],
  "outcome": null,
  "outcome_attachments": [],
  "outcome_file": null,
  "outcome_motivation": null,
  "outcome_protocol_document_id": null,
  "outcome_protocol_number": null,
  "outcome_protocol_numbers": null,
  "outcome_protocol_time": null,
  "outcome_protocolled_at": null,
  "path": "/applications",
  "payment_data": {
    "amount": "1.34",
    "expire_at": "2023-03-06T17:43:51+01:00",
    "landing": {
      "method": "GET",
      "url": "https://servizi.comune.bugliano.pi.it/x/it/pratiche/a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e/detail"
    },
    "notify": {
      "method": "POST",
      "url": "https://servizi.comune.bugliano.pi.it/x/api/applications/a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e/payment"
    },
    "reason": "a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e - CLNVTR76P01G822Q",
    "split": []
  },
  "payment_type": "efil",
  "protocol_document_id": null,
  "protocol_folder_code": null,
  "protocol_folder_number": null,
  "protocol_number": null,
  "protocol_numbers": [],
  "protocol_time": null,
  "protocolled_at": null,
  "service": "pagamento-immediato-efil",
  "service_group_name": null,
  "service_id": "daa0f528-b582-4d1c-9691-e226ac443424",
  "service_name": "Pagamento immediato EFIL",
  "source_type": "http",
  "status": "1500",
  "status_name": "status_payment_pending",
  "subject": null,
  "submission_time": null,
  "submitted_at": null,
  "tenant": "60e35f02-1509-408c-b101-3b1a28109329",
  "tenant_id": "60e35f02-1509-408c-b101-3b1a28109329",
  "timestamp": "2022-12-06T16:43:51.829146577Z",
  "user": "1c340b05-0808-4dbe-ad12-81ebbf3b6abf",
  "user_compilation_notes": null,
  "user_name": "Vittorino Coliandro",
  "x-forwarded-for": "172.31.73.11"
}

Step 2

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.

{
  "id": "2cc87ee5-3f57-4f20-a703-e6718c21b95b",
  "user_id": "1c340b05-0808-4dbe-ad12-81ebbf3b6abf",
  "type": "PAGOPA",
  "tenant_id": "60e35f02-1509-408c-b101-3b1a28109329",
  "service_id": "daa0f528-b582-4d1c-9691-e226ac443424",
  "created_at": "2022-12-06T17:43:52+01:00",
  "updated_at": "2022-12-06T17:43:52+01:00",
  "status": "CREATION_PENDING",
  "reason": "a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e - CLNVTR76P01G822Q",
  "remote_id": "a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e",
  "payment": {
    "transaction_id": null,
    "paid_at": null,
    "expire_at": "2023-03-06T17:43:51+01:00",
    "amount": 1.34,
    "currency": "EUR",
    "notice_code": null,
    "iud": "2cc87ee53f574f20a703e6718c21b95b",
    "iuv": null,
    "receiver": null,
    "due_type": null,
    "pagopa_category": "9/12129/TS",
    "document": null,
    "split": []
  },
  "links": {
    "online_payment_begin": {
      "url": null,
      "last_opened_at": null,
      "method": "GET"
    },
    "online_payment_landing": {
      "url": "https://servizi.comune.bugliano.pi.it/x/it/pratiche/a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e/detail",
      "last_opened_at": null,
      "method": "GET"
    },
    "offline_payment": {
      "url": null,
      "last_opened_at": null,
      "method": "GET"
    },
    "receipt": {
      "url": null,
      "last_opened_at": null,
      "method": "GET"
    },
    "notify": [
      {
        "url": "https://servizi.comune.bugliano.pi.it/x/api/applications/a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e/payment",
        "method": "POST",
        "sent_at": null
      }
    ],
    "update": {
      "url": null,
      "last_check_at": null,
      "next_check_at": null,
      "method": "GET"
    },
    "confirm": {
      "url": null,
      "last_opened_at": null,
      "method": null
    },
    "cancel": {
      "url": null
      "last_opened_at": null,
      "method": null
    }    
  },
  "payer": {
    "type": "human",
    "tax_identification_number": "CLNVTR76P01G822Q",
    "name": "Vittorino",
    "family_name": "Coliandro",
    "street_name": "Via Gramsci, 1",
    "building_number": "",
    "postal_code": "56056",
    "town_name": "Bugliano",
    "country_subdivision": "PI",
    "country": "IT",
    "email": "raffaele.luccisano@opencontent.it"
  },
  "debtor": {
    "type": "human",
    "tax_identification_number": "BNRMHL75C06G702B",
    "name": "Michelangelo",
    "family_name": "Buonarroti",
    "street_name": "Cesare Battisti",
    "building_number": "",
    "postal_code": "38010",
    "town_name": "Bugliano",
    "country_subdivision": "PI",
    "country": "IT",
    "email": "lorenzo.bertoli@opencitylabs.it"
  },
  "event_id": "c3ecf316-ca76-4bf3-ab72-26b941a5ce4f",
  "event_version": "2.0",
  "event_created_at": "2022-12-06T17:43:52+01:00",
  "app_id": "payment-dispatcher:1.1.3"
}

Step 3

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.

Step 3 - Marca da Bollo Digitale

È 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):

  1. Il taglio della marca da bollo o l'importo inserito nella configurazione (per ora l'unico importo supportato è quello da 16 €)

  2. La provincia di residenza del pagatore, la quale si trova nel campo payer.country_subdivision dell'evento payment

  3. 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)

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

Step 3 - Bilancio fisso

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:

  1. Leggerà il campo service_id e reperirà nel mediante esso la configurazione del servizio dentro il quale ci sono le informazioni del bilancio.

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

Esempio

Nel proxy è configurato per il servizio A il seguente bilancio

{
  "split": [
    {
      "split_id": "c_1",
      "split_type": "Tipo c1",
      "split_code": "Codice c1",
      "split_description": "Descrizione c1",
      "split_amount": "16.00",
      "split_budget_chapter": "Capitolo di bilancio c1",
      "split_assessment": "Accertamento c1"
    },
    {
      "split_id": "c_2",
      "split_type": "Tipo c2",
      "split_code": "Codice c2",
      "split_description": "Descrizione c12",
      "split_assessment": "0.50",
      "split_budget_chapter": "Capitolo di bilancio c2",
      "split_assessment": "Accertamento c2"
    }
  ]
}

Il bilancio da utilizzare per arricchire il campo split del pagamento sarà:

{
  "split": [
    {
      "code": "c_1",
      "amount": "16.00",
      "meta": {
        "split_type": "Tipo c1",
        "split_code": "Codice c1",
        "split_description": "Descrizione c1",
        "split_budget_chapter": "Capitolo di bilancio c1",
        "split_assessment": "Accertamento c1"
      }
    },
    {
      "code": "c_2",
      "amount": "0.50",
      "meta": {
        "split_type": "Tipo c2",
        "split_code": "Codice c2",
        "split_description": "Descrizione c12",
        "split_budget_chapter": "Capitolo di bilancio c2",
        "split_assessment": "Accertamento c2"
      }
    }
  ]
}

Step 3 - Bilancio variabile

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

[
  {
    "code": "c_1",
    "amount" : "14.00",
    "meta": {}
  },
  {
    "code": "c_2",
    "amount" : "2.50",
    "meta": {}
  }
]

oppure così nel caso in cui il cittadino sia esentato dal pagamento di una certa voce del bilancio

[
  {
    "code": "c_1",
    "amount" : null,
    "meta": {}
  },
  {
    "code": "c_2",
    "amount" : "0.50",
    "meta": {}
  }
]

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:

  1. Leggerà il campo service_id e reperirà mediante esso la configurazione del servizio dentro il quale ci sono le informazioni del bilancio.

  2. Per ogni voce di bilancio ricavata dalla configurazione del servizio verrà sostituito l'importo con quello letto nel campo split del pagamento

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

Esempio 1

Nel proxy è configurato per il servizio A il seguente bilancio

{
  "split": [
    {
      "split_id": "c_1",
      "split_type": "Tipo c1",
      "split_code": "Codice c1",
      "split_description": "Descrizione c1",
      "split_amount": "16.00",
      "split_budget_chapter": "Capitolo di bilancio c1",
      "split_assessment": "Accertamento c1"
    },
    {
      "split_id": "c_2",
      "split_type": "Tipo c2",
      "split_code": "Codice c2",
      "split_description": "Descrizione c12",
      "split_assessment": "0.50",
      "split_budget_chapter": "Capitolo di bilancio c2",
      "split_assessment": "Accertamento c2"
    }
  ]
}

Nel messaggio del topic payments il campo split è valorizzato a

[
  {
    "code": "c_1",
    "amount" : "14.00",
    "meta": {}
  },
  {
    "code": "c_2",
    "amount" : "2.50",
    "meta": {}
  }
]

Il bilancio da utilizzare per arricchire il campo split del pagamento sarà:

{
  "split": [
    {
      "code": "c_1",
      "amount": "14.00",
      "meta": {
        "split_type": "Tipo c1",
        "split_code": "Codice c1",
        "split_description": "Descrizione c1",
        "split_budget_chapter": "Capitolo di bilancio c1",
        "split_assessment": "Accertamento c1"
      }
    },
    {
      "code": "c_2",
      "amount": "2.50",
      "meta": {
        "split_type": "Tipo c2",
        "split_code": "Codice c2",
        "split_description": "Descrizione c12",
        "split_budget_chapter": "Capitolo di bilancio c2",
        "split_assessment": "Accertamento c2"
      }
    }
  ]
}

Esempio 2

Nel proxy è configurato per il servizio a il seguente bilancio

{
  "split": [
    {
      "split_id": "c_1",
      "split_type": "Tipo c1",
      "split_code": "Codice c1",
      "split_description": "Descrizione c1",
      "split_amount": "16.00",
      "split_budget_chapter": "Capitolo di bilancio c1",
      "split_assessment": "Accertamento c1"
    },
    {
      "split_id": "c_2",
      "split_type": "Tipo c2",
      "split_code": "Codice c2",
      "split_description": "Descrizione c12",
      "split_assessment": "0.50",
      "split_budget_chapter": "Capitolo di bilancio c2",
      "split_assessment": "Accertamento c2"
    }
  ]
}

Nel messaggio del topic payments il campo split è valorizzato a

[
  {
    "code": "c_1",
    "amount" : null,
    "meta": {}
  },
  {
    "code": "c_2",
    "amount" : "0.50",
    "meta": {}
  }
]

Il bilancio da utilizzare per arricchire il campo split del pagamento sarà:

{
  "split": [
    {
      "code": "c_2",
      "amount": "0.50",
      "meta": {
        "split_type": "Tipo c2",
        "split_code": "Codice c2",
        "split_description": "Descrizione c12",
        "split_budget_chapter": "Capitolo di bilancio c2",
        "split_assessment": "Accertamento c2"
      }
    }
  ]
}

Step 4

In caso di risposta positiva:

  1. Il pagamento viene passato in stato PAYMENT_PENDING modificando il campo status,

  2. Vengono compilate le informazioni del bilancio nel campo split (se presenti),

  3. 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}

  4. 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}

  5. 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}

  6. 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}

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

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

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

  9. Viene segnato il timestamp di aggiornamento dell'evento di pagamento (updated_at)

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

{
  "id": "2cc87ee5-3f57-4f20-a703-e6718c21b95b",
  "user_id": "1c340b05-0808-4dbe-ad12-81ebbf3b6abf",
  "type": "PAGOPA",
  "tenant_id": "60e35f02-1509-408c-b101-3b1a28109329",
  "service_id": "daa0f528-b582-4d1c-9691-e226ac443424",
  "created_at": "2022-12-06T17:43:52+01:00",
  "updated_at": "2022-12-06T17:43:53+01:00",
  "status": "PAYMENT_PENDING",
  "reason": "a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e - CLNVTR76P01G822Q",
  "remote_id": "a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e",
  "payment": {
    "transaction_id": null,
    "paid_at": null,
    "expire_at": "2023-03-06T17:43:51+01:00",
    "amount": 1.34,
    "currency": "EUR",
    "notice_code": "302872234000000343",
    "iud": "2cc87ee53f574f20a703e6718c21b95b",
    "iuv": "02872234000000343",
    "receiver": null,
    "due_type": "TARI",
    "pagopa_category": "9/12129/TS",
    "document": null,
    "split": []
  },
  "links": {
    "online_payment_begin": {
      "url": "https://efil-proxy-qa.boat.opencontent.io/online-payment/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_opened_at": null,
      "method": "GET"
    },
    "online_payment_landing": {
      "url": "https://servizi.comune.bugliano.pi.it/x/it/pratiche/a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e/detail",
      "last_opened_at": null,
      "method": "GET"
    },
    "offline_payment": {
      "url": "https://efil-proxy-qa.boat.opencontent.io/notice/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_opened_at": null,
      "method": "GET"
    },
    "receipt": {
      "url": "https://efil-proxy-qa.boat.opencontent.io/receipt/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_opened_at": null,
      "method": "GET"
    },
    "notify": [
      {
        "url": "https://servizi.comune.bugliano.pi.it/x/api/applications/a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e/payment",
        "method": "POST",
        "sent_at": null
      }
    ],
    "update": {
      "url": "http://efil-proxy-qa.boat-backplane.opencontent.io/update/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_check_at": null,
      "next_check_at": "2022-12-06T17:43:53+01:00",
      "method": "GET"
    },
    "confirm": {
      "url": null,
      "last_opened_at": null,
      "method": "PATCH"
    },
    "cancel": {
      "url": "https://efil-proxy-qa.boat.opencontent.io/payments/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_opened_at": null,
      "method": "PATCH"
    } 
  },
  "payer": {
    "type": "human",
    "tax_identification_number": "CLNVTR76P01G822Q",
    "name": "Vittorino",
    "family_name": "Coliandro",
    "street_name": "Via Gramsci, 1",
    "building_number": "",
    "postal_code": "56056",
    "town_name": "Bugliano",
    "country_subdivision": "PI",
    "country": "IT",
    "email": "raffaele.luccisano@opencontent.it"
  },
  "debtor": {
    "type": "human",
    "tax_identification_number": "BNRMHL75C06G702B",
    "name": "Michelangelo",
    "family_name": "Buonarroti",
    "street_name": "Cesare Battisti",
    "building_number": "",
    "postal_code": "38010",
    "town_name": "Bugliano",
    "country_subdivision": "PI",
    "country": "IT",
    "email": "lorenzo.bertoli@opencitylabs.it"
  },
  "event_id": "aa716d45-f56c-4b38-8c62-668a48818a04",
  "event_version": "2.0",
  "event_created_at": "2022-12-06T17:43:53+01:00",
  "app_id": "efil-payment-proxy-qa:1.2.2"
}

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

{
  "id": "7037158b-f990-46d9-9377-1392adb81186",
  "user_id": "1c340b05-0808-4dbe-ad12-81ebbf3b6abf",
  "type": "PAGOPA",
  "tenant_id": "60e35f02-1509-408c-b101-3b1a28109329",
  "service_id": "b2c33452-a741-4841-acc1-69a2c007e398",
  "created_at": "2022-11-22T17:39:38+01:00",
  "updated_at": "2022-11-22T17:39:38+01:00",
  "status": "CREATION_FAILED",
  "reason": "84a4bc4b-6eaf-4ea2-b4bc-0c4d73b05c6c - CLNVTR76P01G822Q",
  "remote_id": "84a4bc4b-6eaf-4ea2-b4bc-0c4d73b05c6c",
  "payment": {
    "transaction_id": null,
    "paid_at": null,
    "expire_at": "2023-02-20T17:39:37+01:00",
    "amount": 1,
    "currency": "EUR",
    "notice_code": null,
    "iud": "7037158bf99046d993771392adb81186",
    "iuv": null,
    "receiver": null,
    "due_type": null,
    "pagopa_category": null,
    "document": null,
    "split": []
  },
  "links": {
    "online_payment_begin": {
      "url": null,
      "last_opened_at": null,
      "method": "GET"
    },
    "online_payment_landing": {
      "url": "https://servizi.comune.bugliano.pi.it/x/it/pratiche/84a4bc4b-6eaf-4ea2-b4bc-0c4d73b05c6c/detail",
      "last_opened_at": null,
      "method": "GET"
    },
    "offline_payment": {
      "url": null,
      "last_opened_at": null,
      "method": "GET"
    },
    "receipt": {
      "url": null,
      "last_opened_at": null,
      "method": "GET"
    },
    "notify": [
      {
        "url": "https://servizi.comune.bugliano.pi.it/x/api/applications/84a4bc4b-6eaf-4ea2-b4bc-0c4d73b05c6c/payment",
        "method": "POST",
        "sent_at": null
      }
    ],
    "update": {
      "url": null,
      "last_check_at": null,
      "next_check_at": null,
      "method": "GET"
    },
    "confirm": {
      "url": null,
      "last_opened_at": null,
      "method": "PATCH"
    },
    "cancel": {
      "url": "https://efil-proxy-qa.boat.opencontent.io/payments/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_opened_at": null,
      "method": "PATCH"
    }
  },
  "payer": {
    "type": "human",
    "tax_identification_number": "CLNVTR76P01G822Q",
    "name": "Vittorino",
    "family_name": "Coliandro",
    "street_name": "Via Gramsci, 1",
    "building_number": "",
    "postal_code": "56056",
    "town_name": "Bugliano",
    "country_subdivision": "PI",
    "country": "IT",
    "email": "raffaele.luccisano@opencontent.it"
  },
  "debtor": {
    "type": "human",
    "tax_identification_number": "BNRMHL75C06G702B",
    "name": "Michelangelo",
    "family_name": "Buonarroti",
    "street_name": "Cesare Battisti",
    "building_number": "",
    "postal_code": "38010",
    "town_name": "Bugliano",
    "country_subdivision": "PI",
    "country": "IT",
    "email": "lorenzo.bertoli@opencitylabs.it"
  },
  "event_id": "8b3452d8-bd5d-4380-b8a6-7e4f551b09d8",
  "event_version": "2.0",
  "event_created_at": "2022-11-22T17:39:39+01:00",
  "app_id": "pmpay-payment-proxy:1.0.0-rc.2"
}

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

Step 4 - "Paga online"

  1. Verra richiamata l'url {EXTERNAL_API_URL}/online-payment/{payment_id} servita dal proxy

  2. Il proxy richiederà all'intermediario di pagamento il link per pagare

  3. Sarà segnato il timestamp di apertura del link (links.online_payment_begin.last_opened_at)

  4. Sarà segnato il timestamp di aggiornamento dell'evento (updated_at)

  5. Il pagamento verrà salvato sullo storage

  6. L'evento aggiornato verrà scritto sul topic payments, indicando come chiave dell'evento il valore del campo service_id

  7. Verrà restituito il link ritornato allo step 2 mediante il quale l'utente verrà rediretto al portale dell'intermediario

  8. Al termine della procedura di pagamento, l'utente verrà fatto ritornare sul dettaglio della pratica mediante l'url di landing specificata in fase di creazione della posizione debitoria (links.online_payment_landing.url) allo step 5, a questa url andrà aggiunto in query string un parametro payment da valorizzare con OK o KO a seconda dell'esito del pagamento

  9. 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)

  10. Il pagamento verrà portato in stato PAYMENT_STARTED

  11. Sarà segnato il timestamp di aggiornamento dell'evento (updated_at)

  12. Il pagamento verrà salvato sullo storage

  13. L'evento aggiornato verrà scritto sul topic payments, indicando come chiave dell'evento il valore del campo service_id.

{
  "id": "2cc87ee5-3f57-4f20-a703-e6718c21b95b",
  "user_id": "1c340b05-0808-4dbe-ad12-81ebbf3b6abf",
  "type": "PAGOPA",
  "tenant_id": "60e35f02-1509-408c-b101-3b1a28109329",
  "service_id": "daa0f528-b582-4d1c-9691-e226ac443424",
  "created_at": "2022-12-06T17:43:52+01:00",
  "updated_at": "2022-12-06T17:44:51+01:00",
  "status": "PAYMENT_STARTED",
  "reason": "a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e - CLNVTR76P01G822Q",
  "remote_id": "a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e",
  "payment": {
    "transaction_id": "7b32b299-67db-4f84-9419-f61962c3e202",
    "paid_at": null,
    "expire_at": "2023-03-06T17:43:51+01:00",
    "amount": 1.34,
    "currency": "EUR",
    "notice_code": "302872234000000343",
    "iud": "2cc87ee53f574f20a703e6718c21b95b",
    "iuv": "02872234000000343",
    "receiver": null,
    "due_type": "TARI",
    "pagopa_category": "9/12129/TS",
    "document": null,
    "split": []
  },
  "links": {
    "online_payment_begin": {
      "url": "https://efil-proxy-qa.boat.opencontent.io/online-payment/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_opened_at": "2022-12-06T17:43:56+01:00",
      "method": "GET"
    },
    "online_payment_landing": {
      "url": "https://servizi.comune.bugliano.pi.it/x/it/pratiche/a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e/detail",
      "last_opened_at": "2022-12-06T17:44:51+01:00",
      "method": "GET"
    },
    "offline_payment": {
      "url": "https://efil-proxy-qa.boat.opencontent.io/notice/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_opened_at": null,
      "method": "GET"
    },
    "receipt": {
      "url": "https://efil-proxy-qa.boat.opencontent.io/receipt/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_opened_at": null,
      "method": "GET"
    },
    "notify": [
      {
        "url": "https://servizi.comune.bugliano.pi.it/x/api/applications/a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e/payment",
        "method": "POST",
        "sent_at": null
      }
    ],
    "update": {
      "url": "http://efil-proxy-qa.boat-backplane.opencontent.io/update/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_check_at": null,
      "next_check_at": "2022-12-06T17:43:53+01:00",
      "method": "GET"
    },
    "confirm": {
      "url": null,
      "last_opened_at": null,
      "method": "PATCH"
    },
    "cancel": {
      "url": "https://efil-proxy-qa.boat.opencontent.io/payments/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_opened_at": null,
      "method": "PATCH"
    } 
  },
  "payer": {
    "type": "human",
    "tax_identification_number": "CLNVTR76P01G822Q",
    "name": "Vittorino",
    "family_name": "Coliandro",
    "street_name": "Via Gramsci, 1",
    "building_number": "",
    "postal_code": "56056",
    "town_name": "Bugliano",
    "country_subdivision": "PI",
    "country": "IT",
    "email": "raffaele.luccisano@opencontent.it"
  },
  "debtor": {
    "type": "human",
    "tax_identification_number": "BNRMHL75C06G702B",
    "name": "Michelangelo",
    "family_name": "Buonarroti",
    "street_name": "Cesare Battisti",
    "building_number": "",
    "postal_code": "38010",
    "town_name": "Bugliano",
    "country_subdivision": "PI",
    "country": "IT",
    "email": "lorenzo.bertoli@opencitylabs.it"
  },
  "event_id": "dcd203c7-a6e8-4084-b09b-f700e8fa94d8",
  "event_version": "2.0",
  "event_created_at": "2022-12-06T17:44:51+01:00",
  "app_id": "efil-payment-proxy-qa:1.2.2"
}

Step 4 - "Paga offline"

  1. Verra richiamata l'url {EXTERNAL_API_URL}/offline-payment/{payment_id} servita dal proxy

  2. Il proxy richiederà all'intermediario di pagamento il pdf dell'avviso di pagamento

  3. Sarà segnato il timestamp di apertura del link (links.offline_payment.last_opened_at)

  4. Sarà segnato il timestamp di aggiornamento dell'evento (updated_at)

  5. Il pagamento verrà salvato sullo storage

  6. L'evento aggiornato verrà scritto sul topic payments, indicando come chiave dell'evento il valore del campo service_id

  7. Verrà eseguito il download del pdf ritornato allo step 1

Esempio di avviso di pagamento generato

Step 4 - Annulla pagamento

È 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:

  1. Verra richiamata l'url PATCH {EXTERNAL_API_URL}/payments/{payment_id} servita dal proxy con payload {"status": "CANCELED"}

  2. Il proxy, dopo aver letto il pagamento dallo storage, segnerà il campo status a CANCELED

  3. Sarà segnato il timestamp di apertura del link (links.offline_payment.last_opened_at)

  4. Sarà segnato il timestamp di aggiornamento dell'evento (updated_at)

  5. Il pagamento verrà salvato sullo storage

  6. L'evento aggiornato verrà scritto sul topic payments, indicando come chiave dell'evento il valore del campo service_id

Step 4 - Dovuti importati da fonte esterna all'Area personale

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)

Step 5

Il payments poller nel frattempo:

  1. Interroga KSQLDB per ottenere lo stato dei pagamenti, e se questi sono in stato PAYMENT_PENDING o PAYMENT_STARTED, eseguirà una chiamata http all'url {INTERNAL_API_URL}/update/{payment_id} servita dal proxy (quella di aggiornamento menzionata allo step 5)

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

    1. 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)

  3. Il proxy segna il timestamp in cui è stato aperto il link di aggiornamento (links.update.last_opened_at)

  4. Il proxy segna il timestamp di aggiornamento del pagamento (updated_at)

  5. Il proxy salva il pagamento sullo storage e lo scrive sul topic payments, indicando come chiave dell'evento il valore del campo service_id.

Nota 1: più passa il tempo, più è probabile che il pagamento non venga eseguito dall'utente, di conseguenza ad ogni chiamata del poller verso il proxy, viene incrementato l'intervallo di tempo tra una chiamata e l'altra, ciò viene fatto in base al campo links.update.next_check_at.

Nota 2: non tutti i proxy aggiornano i pagamenti mediante update, alcuni vengono aggiornati predisponendo un'apposita API che l'intermediario di pagamento PagoPA chiama inviando la notifica di completamento del pagamento. Questa viene quindi elaborata dal proxy il quale aggiorna il pagamento portandolo in stato COMPLETE, segna il timestamp di aggiornamento del pagamento (updated_at), lo salva sullo storage e lo scrive sul topic payments, indicando come chiave dell'evento il valore del campo service_id .

{
  "id": "2cc87ee5-3f57-4f20-a703-e6718c21b95b",
  "user_id": "1c340b05-0808-4dbe-ad12-81ebbf3b6abf",
  "type": "PAGOPA",
  "tenant_id": "60e35f02-1509-408c-b101-3b1a28109329",
  "service_id": "daa0f528-b582-4d1c-9691-e226ac443424",
  "created_at": "2022-12-06T17:43:52+01:00",
  "updated_at": "2022-12-07T09:47:35+01:00",
  "status": "COMPLETE",
  "reason": "a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e - CLNVTR76P01G822Q",
  "remote_id": "a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e",
  "payment": {
    "transaction_id": "7b32b299-67db-4f84-9419-f61962c3e202",
    "paid_at": null,
    "expire_at": "2023-03-06T17:43:51+01:00",
    "amount": 1.34,
    "currency": "EUR",
    "notice_code": "302872234000000343",
    "iud": "2cc87ee53f574f20a703e6718c21b95b",
    "iuv": "02872234000000343",
    "receiver": null,
    "due_type": "TARI",
    "pagopa_category": "9/12129/TS",
    "document": null,
    "split": []
  },
  "links": {
    "online_payment_begin": {
      "url": "https://efil-proxy-qa.boat.opencontent.io/online-payment/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_opened_at": "2022-12-06T17:43:56+01:00",
      "method": "GET"
    },
    "online_payment_landing": {
      "url": "https://servizi.comune.bugliano.pi.it/x/it/pratiche/a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e/detail",
      "last_opened_at": "2022-12-06T17:44:51+01:00",
      "method": "GET"
    },
    "offline_payment": {
      "url": "https://efil-proxy-qa.boat.opencontent.io/notice/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_opened_at": null,
      "method": "GET"
    },
    "receipt": {
      "url": "https://efil-proxy-qa.boat.opencontent.io/receipt/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_opened_at": null,
      "method": "GET"
    },
    "notify": [
      {
        "url": "https://servizi.comune.bugliano.pi.it/x/api/applications/a1bbcafb-ee78-4cd1-9b8e-7ddeb434d84e/payment",
        "method": "POST",
        "sent_at": null
      }
    ],
    "update": {
      "url": "http://efil-proxy-qa.boat-backplane.opencontent.io/update/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_check_at": "2022-12-07T09:47:35+01:00",
      "next_check_at": "2022-12-07T10:47:35+01:00",
      "method": "GET"
    },
    "confirm": {
      "url": null,
      "last_opened_at": null,
      "method": "PATCH"
    },
    "cancel": {
      "url": "https://efil-proxy-qa.boat.opencontent.io/payments/2cc87ee5-3f57-4f20-a703-e6718c21b95b",
      "last_opened_at": null,
      "method": "PATCH"
    } 
  },
  "payer": {
    "type": "human",
    "tax_identification_number": "CLNVTR76P01G822Q",
    "name": "Vittorino",
    "family_name": "Coliandro",
    "street_name": "Via Gramsci, 1",
    "building_number": "",
    "postal_code": "56056",
    "town_name": "Bugliano",
    "country_subdivision": "PI",
    "country": "IT",
    "email": "raffaele.luccisano@opencontent.it"
  },
  "debtor": {
    "type": "human",
    "tax_identification_number": "BNRMHL75C06G702B",
    "name": "Michelangelo",
    "family_name": "Buonarroti",
    "street_name": "Cesare Battisti",
    "building_number": "",
    "postal_code": "38010",
    "town_name": "Bugliano",
    "country_subdivision": "PI",
    "country": "IT",
    "email": "lorenzo.bertoli@opencitylabs.it"
  },
  "event_id": "1a745224-37bb-4843-a85f-3011764395a6",
  "event_version": "2.0",
  "event_created_at": "2022-12-07T09:47:35+01:00",
  "app_id": "efil-payment-proxy-qa:1.2.2"
}

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.

TABELLA STATI PAGAMENTO

Gli stati per i quali passa un pagamento durante il suo ciclo di vita sono riassunti nella seguente tabella

STATO PAGAMENTODESCRIZIONENOTE

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

REQUISITI

Tutti i proxy devono:

  • Validare gli eventi in input e output

  • Integrazione con sentry

  • Healthcheck

  • Esporre metriche di monitoraggio in formato prometheus

Last updated

Logo

Documentazione Opencity Italia