Aller au contenu

evenements-partenaires

Événement (AsyncAPI) — version 0.1.0. Producteur : diapason.

Ce que la plateforme pousse vers le serveur d’un teneur de compte quand un fait métier survient : un versement exécuté, une part d’avoirs devenue disponible, une opération collective déclinée, une transmission de population contrôlée. C’est le pendant des deux contrats REST — ce qui doit être poussé relève des événements, jamais d’un sondage de l’interface, et sans eux un portail interrogerait la situation d’un épargnant en boucle pour découvrir qu’elle n’a pas changé. Diapason ne détient rien et ne décide rien. Ces faits naissent dans les domaines ; la passerelle les republie dans son propre vocabulaire, celui des deux contrats REST. Un événement porte donc des identifiants publiés et des valeurs décidées — jamais une échéance de lot, un identifiant de mesure de conformité ou un paramètre de règle. Le teneur n’est pas nommé non plus : un déploiement ne sert qu’un tenant, et le destinataire sait qui il est. C’est le premier mécanisme du programme qui sorte vers un hôte que nous ne maîtrisons pas.

asyncapi: 3.0.0
info:
title: diapason — les événements servis aux teneurs de compte
version: 0.1.0
description: >-
**Ce que la plateforme pousse vers le serveur d'un teneur de compte** quand un fait
métier survient : un versement exécuté, une part d'avoirs devenue disponible, une
opération collective déclinée, une transmission de population contrôlée. C'est le
pendant des deux contrats REST — ce qui doit être poussé relève des événements, jamais
d'un sondage de l'interface, et sans eux un portail interrogerait la situation d'un
épargnant en boucle pour découvrir qu'elle n'a pas changé.
**Diapason ne détient rien et ne décide rien.** Ces faits naissent dans les domaines ; la
passerelle les republie dans son propre vocabulaire, celui des deux contrats REST. Un
événement porte donc des **identifiants publiés et des valeurs décidées** — jamais une
échéance de lot, un identifiant de mesure de conformité ou un paramètre de règle.
**Le teneur n'est pas nommé non plus** : un déploiement ne sert qu'un tenant, et le
destinataire sait qui il est. Aucun attribut ne transporte le tenant, comme aucun chemin
ne le porte.
C'est le premier mécanisme du programme qui sorte vers un hôte que nous ne maîtrisons pas.
x-ruptures: []
x-producteurs:
- diapason
# Les serveurs des teneurs de compte ne sont pas recensables : un
# retrait de version majeure relève du préavis contractuel, jamais d'un décompte.
x-consommateurs: []
defaultContentType: application/cloudevents+json
servers:
serveurDuTeneur:
host: '{hote-du-teneur}'
protocol: https
description: >-
**C'est le teneur de compte qui héberge le point de réception**, et la passerelle qui
appelle : la livraison est une requête `POST` sortante vers l'URL que le teneur a
enregistrée. L'URL, le secret de signature et les canaux souscrits sont attachés au client
OAuth du teneur et déclarés hors de ce contrat.
variables:
hote-du-teneur:
default: reception.teneur.exemple
description: L'hôte que le teneur expose pour recevoir les événements.
channels:
epargnant:
address: /diapason/v1/evenements/epargnant
title: Les faits qui concernent un épargnant
description: >-
Le canal que souscrit le portail des épargnants. Le `subject` de chaque événement est
l'identifiant publié de l'épargnant concerné : il désigne la ressource, il n'autorise
rien — vérifier que la personne à qui le portail montre le fait en est bien la
titulaire reste une obligation du teneur.
messages:
versementExecute:
$ref: '#/components/messages/versementExecute'
arbitrageExecute:
$ref: '#/components/messages/arbitrageExecute'
operationRefusee:
$ref: '#/components/messages/operationRefusee'
avoirsDevenusDisponibles:
$ref: '#/components/messages/avoirsDevenusDisponibles'
documentMisADisposition:
$ref: '#/components/messages/documentMisADisposition'
entreprise:
address: /diapason/v1/evenements/entreprise
title: Les faits qui concernent une entreprise
description: >-
Le canal que souscrit le portail des correspondants. Le `subject` de chaque événement
est l'identifiant publié de l'entreprise concernée.
messages:
operationCollectiveDeclinee:
$ref: '#/components/messages/operationCollectiveDeclinee'
operationCollectiveExecutee:
$ref: '#/components/messages/operationCollectiveExecutee'
transmissionDePopulationTraitee:
$ref: '#/components/messages/transmissionDePopulationTraitee'
restitutionMiseADisposition:
$ref: '#/components/messages/restitutionMiseADisposition'
operations:
livrerLesEvenementsDeLEpargnant:
action: send
channel:
$ref: '#/channels/epargnant'
summary: La passerelle livre au serveur du teneur les faits qui concernent ses épargnants.
description: >-
**Au moins une fois, jamais exactement une fois.** Le même événement peut être livré
plusieurs fois : le destinataire déduplique sur l'attribut `id`, qui ne change pas
d'une livraison à l'autre.
**Aucun ordre n'est garanti** — ni entre canaux, ni entre deux faits d'une même
ressource. L'extension `sequence` est strictement croissante **par sujet** : elle
permet de reconnaître un doublon, un message en retard ou un manque. Un destinataire
qui présume l'ordre sans le vérifier se trompera ; celui qui doute relit l'état par
le contrat REST, qui fait foi.
**Le teneur répond `2xx` pour accuser réception**, et rien d'autre n'est interprété
comme un succès. Tout autre code, comme une absence de réponse, déclenche des
réessais espacés par un retrait exponentiel ; après épuisement, le canal est **mis en
sommeil** et le teneur en est averti hors bande. Un canal en sommeil ne perd pas les
faits survenus entre-temps : il les rattrape à sa reprise, dans un ordre qui reste non
garanti.
messages:
- $ref: '#/channels/epargnant/messages/versementExecute'
- $ref: '#/channels/epargnant/messages/arbitrageExecute'
- $ref: '#/channels/epargnant/messages/operationRefusee'
- $ref: '#/channels/epargnant/messages/avoirsDevenusDisponibles'
- $ref: '#/channels/epargnant/messages/documentMisADisposition'
livrerLesEvenementsDeLEntreprise:
action: send
channel:
$ref: '#/channels/entreprise'
summary: La passerelle livre au serveur du teneur les faits qui concernent les entreprises qu'il sert.
description: >-
Mêmes garanties que le canal de l'épargnant : au moins une fois, aucun ordre garanti,
`sequence` croissante par sujet, accusé `2xx`, réessais puis mise en sommeil.
messages:
- $ref: '#/channels/entreprise/messages/operationCollectiveDeclinee'
- $ref: '#/channels/entreprise/messages/operationCollectiveExecutee'
- $ref: '#/channels/entreprise/messages/transmissionDePopulationTraitee'
- $ref: '#/channels/entreprise/messages/restitutionMiseADisposition'
components:
messages:
versementExecute:
name: diapason.versement.execute
title: Versement exécuté
summary: >-
Un versement volontaire est investi — c'est la fin de la chaîne qu'un `202` avait
ouverte. Le portail cesse ici de suivre l'opération.
contentType: application/cloudevents+json
payload:
$ref: '#/components/schemas/versementExecute'
headers:
$ref: '#/components/schemas/enTetesDeLivraison'
arbitrageExecute:
name: diapason.arbitrage.execute
title: Arbitrage exécuté
summary: La composition a changé — aucun fonds n'est sorti.
contentType: application/cloudevents+json
payload:
$ref: '#/components/schemas/arbitrageExecute'
headers:
$ref: '#/components/schemas/enTetesDeLivraison'
operationRefusee:
name: diapason.operation.refusee
title: Opération refusée
summary: >-
Une opération engagée par le portail n'ira pas à son terme. Le motif est **décidé**
et destiné à être affiché ; il ne nomme jamais une mesure de conformité.
contentType: application/cloudevents+json
payload:
$ref: '#/components/schemas/operationRefusee'
headers:
$ref: '#/components/schemas/enTetesDeLivraison'
avoirsDevenusDisponibles:
name: diapason.avoirs.devenus-disponibles
title: Avoirs devenus disponibles
summary: >-
Une part d'avoirs a cessé d'être indisponible. Le fait est servi **décidé** — un
montant, un dispositif — sans dire ce qui le levait : une échéance atteinte et une
contrainte tombée produisent le même fait, parce qu'un portail n'a pas à les
distinguer pour prévenir son porteur.
contentType: application/cloudevents+json
payload:
$ref: '#/components/schemas/avoirsDevenusDisponibles'
headers:
$ref: '#/components/schemas/enTetesDeLivraison'
documentMisADisposition:
name: diapason.document.mis-a-disposition
title: Document mis à disposition
summary: Un relevé, un avis d'opéré ou un document d'information clé est consultable.
contentType: application/cloudevents+json
payload:
$ref: '#/components/schemas/documentMisADisposition'
headers:
$ref: '#/components/schemas/enTetesDeLivraison'
operationCollectiveDeclinee:
name: diapason.operation-collective.declinee
title: Opération collective déclinée
summary: >-
La répartition est arrêtée et déclinée en opérations individuelles. C'est le point
où le correspondant sait que sa déclaration a produit ses effets.
contentType: application/cloudevents+json
payload:
$ref: '#/components/schemas/operationCollectiveDeclinee'
headers:
$ref: '#/components/schemas/enTetesDeLivraison'
operationCollectiveExecutee:
name: diapason.operation-collective.executee
title: Opération collective exécutée
summary: L'exécution est achevée — les avoirs sont investis chez les bénéficiaires.
contentType: application/cloudevents+json
payload:
$ref: '#/components/schemas/operationCollectiveExecutee'
headers:
$ref: '#/components/schemas/enTetesDeLivraison'
transmissionDePopulationTraitee:
name: diapason.transmission-de-population.traitee
title: Transmission de population traitée
summary: >-
Le contrôle d'une transmission est terminé. Le fait porte le décompte des écarts,
pas les écarts eux-mêmes : ils se lisent au contrat REST, qui les tient à jour.
contentType: application/cloudevents+json
payload:
$ref: '#/components/schemas/transmissionDePopulationTraitee'
headers:
$ref: '#/components/schemas/enTetesDeLivraison'
restitutionMiseADisposition:
name: diapason.restitution.mise-a-disposition
title: Restitution mise à disposition
summary: >-
Une restitution prévue par la convention de tenue de compte est arrêtée et
consultable.
contentType: application/cloudevents+json
payload:
$ref: '#/components/schemas/restitutionMiseADisposition'
headers:
$ref: '#/components/schemas/enTetesDeLivraison'
schemas:
enTetesDeLivraison:
type: object
description: >-
Les en-têtes HTTP de la livraison. La liaison est la **liaison HTTP de CloudEvents
en mode structuré** : l'enveloppe entière est le corps JSON, et ces en-têtes ne
portent que ce qui doit être lu avant de faire confiance au corps.
required: [Content-Type, Diapason-Signature, Diapason-Signature-Horodatage]
properties:
Content-Type:
type: string
const: application/cloudevents+json; charset=utf-8
Diapason-Signature:
type: string
description: >-
La signature du corps — HMAC-SHA256, clé partagée avec le teneur à
l'enregistrement, encodée en hexadécimal. **Le destinataire la vérifie avant
de lire le corps** : sans elle, n'importe qui connaissant l'URL pourrait
fabriquer un fait et le faire afficher à un épargnant.
Diapason-Signature-Horodatage:
type: string
format: date-time
description: >-
L'instant de la signature, couvert par elle. Un destinataire rejette une
livraison trop ancienne : c'est ce qui empêche de rejouer indéfiniment une
livraison interceptée.
socleDEvenement:
type: object
description: >-
L'enveloppe **CloudEvents 1.0.2** commune à tous les messages. Les consommateurs
sont tolérants : un attribut inconnu s'ignore, et l'apparition d'un attribut
nouveau n'est pas une rupture.
required: [specversion, id, source, type, subject, time, sequence, versioncontrat, data]
properties:
specversion:
type: string
const: '1.0'
id:
type: string
minLength: 1
description: >-
L'identifiant unique du fait — **la clé de déduplication**. Il ne change pas
d'une livraison à l'autre, et deux livraisons du même `id` désignent le même
fait, jamais deux faits semblables.
source:
type: string
format: uri
description: >-
L'origine de l'événement : l'URI de la passerelle qui l'émet. Elle ne nomme
jamais le domaine où le fait est né.
type:
type: string
description: Le type du fait, dans la liste fermée des messages de ce contrat.
subject:
type: string
minLength: 1
description: >-
La ressource concernée — l'identifiant publié de l'épargnant sur le canal de
l'épargnant, de l'entreprise sur celui de l'entreprise. Il **désigne**, il
n'autorise pas.
time:
type: string
format: date-time
description: L'instant de survenance du fait (UTC) — jamais celui de la livraison.
sequence:
type: integer
minimum: 1
description: >-
Extension CloudEvents : le rang du fait **pour ce sujet**, strictement
croissant. Un trou signale un manque, un rang déjà vu un doublon, un rang
inférieur au dernier connu un message en retard.
versioncontrat:
type: string
description: Extension — la version sémantique du présent contrat.
correlationid:
type: string
description: >-
Extension — l'identifiant de corrélation de l'écriture qui a déclenché ce fait,
restitué tel que le teneur l'avait fourni. Absent quand le fait ne vient
d'aucune écriture du portail : une échéance qui tombe n'a pas d'appelant.
datacontenttype:
type: string
const: application/json
data:
type: object
description: La charge utile, propre à chaque type de fait.
versementExecute:
allOf:
- $ref: '#/components/schemas/socleDEvenement'
- type: object
properties:
type: { const: diapason.versement.execute }
data:
type: object
required: [operation, dispositif, montant_net_ct, execute_le]
properties:
operation:
type: string
description: La référence servie par le `202` de la demande de versement.
dispositif: { type: string }
montant_net_ct:
type: integer
description: >-
Le net investi, en centimes — servi tel qu'il est porté, jamais
recalculé.
execute_le: { type: string, format: date }
arbitrageExecute:
allOf:
- $ref: '#/components/schemas/socleDEvenement'
- type: object
properties:
type: { const: diapason.arbitrage.execute }
data:
type: object
required: [operation, dispositif, execute_le]
properties:
operation: { type: string }
dispositif: { type: string }
execute_le: { type: string, format: date }
operationRefusee:
allOf:
- $ref: '#/components/schemas/socleDEvenement'
- type: object
properties:
type: { const: diapason.operation.refusee }
data:
type: object
required: [operation, nature, motif, refusee_le]
properties:
operation: { type: string }
nature:
type: string
description: La nature de l'opération refusée, dans la nomenclature du contrat REST.
motif:
type: string
description: >-
Le motif **décidé**, destiné à être affiché tel quel au porteur. Il ne
nomme jamais une mesure de conformité, et ne livre aucun paramètre dont
un portail reconstituerait la règle.
refusee_le: { type: string, format: date }
avoirsDevenusDisponibles:
allOf:
- $ref: '#/components/schemas/socleDEvenement'
- type: object
properties:
type: { const: diapason.avoirs.devenus-disponibles }
data:
type: object
required: [dispositif, montant_ct, disponible_depuis]
properties:
dispositif: { type: string }
montant_ct:
type: integer
description: Le montant devenu disponible, en centimes, à la date du fait.
disponible_depuis: { type: string, format: date }
documentMisADisposition:
allOf:
- $ref: '#/components/schemas/socleDEvenement'
- type: object
properties:
type: { const: diapason.document.mis-a-disposition }
data:
type: object
required: [document, type_de_document, emis_le]
properties:
document:
type: string
description: L'identifiant servi par la liste des documents du contrat REST.
type_de_document:
type: string
enum:
- RELEVE_DE_SITUATION
- AVIS_D_OPERE
- DOCUMENT_D_INFORMATION_CLE
- REGLEMENT_DU_DISPOSITIF
emis_le: { type: string, format: date }
dispositif: { type: string }
operationCollectiveDeclinee:
allOf:
- $ref: '#/components/schemas/socleDEvenement'
- type: object
properties:
type: { const: diapason.operation-collective.declinee }
data:
type: object
required: [operation, dispositif, beneficiaires, declinee_le]
properties:
operation:
type: string
description: La référence servie par le `202` de la déclaration.
dispositif: { type: string }
beneficiaires:
type: integer
description: Le nombre de bénéficiaires de la répartition arrêtée.
montant_reparti_ct: { type: integer }
declinee_le: { type: string, format: date-time }
operationCollectiveExecutee:
allOf:
- $ref: '#/components/schemas/socleDEvenement'
- type: object
properties:
type: { const: diapason.operation-collective.executee }
data:
type: object
required: [operation, dispositif, execute_le]
properties:
operation: { type: string }
dispositif: { type: string }
execute_le: { type: string, format: date }
anomalies:
type: integer
description: >-
Le nombre d'anomalies retenues ; leur nature se lit au contrat REST.
Zéro signifie que rien n'attend le teneur.
transmissionDePopulationTraitee:
allOf:
- $ref: '#/components/schemas/socleDEvenement'
- type: object
properties:
type: { const: diapason.transmission-de-population.traitee }
data:
type: object
required: [transmission, mouvements_recus, mouvements_appliques, ecarts, traitee_le]
properties:
transmission:
type: string
description: La référence servie par le `202` de la transmission.
mouvements_recus: { type: integer }
mouvements_appliques: { type: integer }
ecarts:
type: integer
description: >-
Le **décompte** des écarts. Les écarts eux-mêmes se lisent au contrat
REST : les republier ici les figerait dans une charge utile pendant que
le teneur les corrige.
traitee_le: { type: string, format: date-time }
restitutionMiseADisposition:
allOf:
- $ref: '#/components/schemas/socleDEvenement'
- type: object
properties:
type: { const: diapason.restitution.mise-a-disposition }
data:
type: object
required: [restitution, type_de_restitution, arretee_le]
properties:
restitution: { type: string }
type_de_restitution:
type: string
enum: [CAMPAGNE, ENCOURS, FRAIS]
arretee_le: { type: string, format: date-time }
retenue:
type: boolean
description: >-
Vrai quand le contenu sera retenu à la lecture — population sous le
plancher d'agrégation du teneur. Le portail sait ainsi qu'il ne sert à
rien de proposer l'ouverture.