Aller au contenu

Intégration et vérification des cartes

Afficher en Markdown

Chaque association qui a activé l’adhésion publique a une page https://app.vimine.network/enroll/<slug> qui peut être placée dans une iframe sur le site de l’association :

<iframe src="https://app.vimine.network/enroll/your-slug" style="width:100%;min-height:900px;border:0"></iframe>

L’association doit indiquer le domaine du site parmi ses domaines d’intégration dans ses réglages. La page de connexion et de consentement de Se connecter avec Vimine, en revanche, ne peut pas être placée dans une iframe : elle s’affiche toujours en pleine page sur vimine.network.

?locale=en ouvre le formulaire en anglais (l’élément <vimine-enroll> le transmet de lui-même, à partir de la langue de la page).

Le formulaire peut prendre les couleurs de votre site. Une page dans une iframe ne voit pas le CSS de la page qui l’accueille : le style voyage donc dans l’adresse.

Paramètre Valeurs
scheme light, dark le thème de la page ; sans lui, celui de l’arrière-plan ou du visiteur
accent une couleur hexadécimale (1e3a5f) boutons, liens, champs actifs et nom de l’association
background une couleur hexadécimale, ou transparent le fond derrière les encadrés ; transparent laisse voir votre page
font inter, roboto, open-sans, montserrat, nunito, lora, merriweather, roboto-slab, system la police de toute la page, champs compris, parmi celles que Vimine sert lui-même (system : celle de l’appareil)
<iframe src="https://app.vimine.network/enroll/your-slug?scheme=light&accent=1e3a5f&background=transparent"
style="width:100%;min-height:900px;border:0"></iframe>

Les autres valeurs sont ignorées. Avec <vimine-enroll>, il n’y a pas d’adresse à écrire : définissez --vimine-scheme, --vimine-accent, --vimine-background et --vimine-typeface dans votre CSS (éléments). Avec transparent, choisissez le thème qui se lit sur votre page : les encadrés du formulaire ont leur propre fond, le texte en dehors repose sur le vôtre.

Une seule identité sert pour toutes les associations : une personne qui a déjà un compte n’a pas à remplir le formulaire de nouveau. Ouverte directement, la page …/enroll/<slug> reconnaît la session et affiche un seul écran — le type d’adhésion, le statuto et la notice d’information sur la vie privée de cette association, et seulement ce que cette association demande en plus (l’adresse, si elle a déclaré la recueillir). Le statuto et la notice sont acceptés à chaque fois : rien ne passe d’une association à une autre.

Dans une iframe, la session ne peut pas être lue : un navigateur tient une page encadrée à l’écart du même site ouvert directement. Le formulaire encadré propose donc Vous avez déjà un compte Vimine ? Continuer sur Vimine, qui ouvre la page dans un nouvel onglet. Si vous écrivez l’iframe à la main et utilisez sandbox, incluez allow-popups et allow-popups-to-escape-sandbox, sinon cet onglet reste dans le bac à sable ; <vimine-enroll> le fait déjà.

GET /v1/aps/{slug} renvoie le profil public de l’association — identité visuelle, types d’adhésion avec leurs cotisations, statuto en vigueur — si vous voulez construire votre propre parcours :

import { publicApi } from '@vimine/sdk';
const { aps, membership_types, statuto } = await publicApi().association('your-slug');

Aucun identifiant. Depuis une page où le membre a fait Se connecter avec Vimine, vous pouvez ensuite recueillir la demande :

const { aps, membership_types, statuto, privacy_notice } = await publicApi().association('your-slug');
await vimine.membership.request({
membership_type,
accept_statuto: { version: statuto.version },
// la notice d’information de l’association, que votre page a affichée
privacy_notice: privacy_notice && { version: privacy_notice.version, sha256: privacy_notice.sha256, locale: privacy_notice.locale },
// l’adresse, si l’association en recueille une (aps.member_address: 'optional' | 'required')
address: { line: 'Via Roma 1', postal_code: '20100', city: 'Milano', province: 'MI' }
});

Ce qu’une association demande à une personne qui adhère ne dépend pas de la porte par laquelle arrive la demande :

  • la notice d’information. Tant que l’association en a une en vigueur que la personne n’a pas encore vue, privacy_notice est obligatoire : sans elle, 422 validation_failed sur le champ privacy_notice ; avec une version qui n’est plus en vigueur, 422 privacy_notice_outdated. vimine.legal.acceptances() dit ce qui reste à confirmer ; le texte est GET /v1/aps/{slug}/privacy-notice. La prise de connaissance est conservée comme une ligne de preuve liée à cette adhésion.
  • l’adresse. aps.member_address dit si l’association la recueille : avec required, une demande sans address répond 422 validation_failed sur le champ address ; avec none, elle est ignorée.
  • qui est la personne. Quand le profil n’est pas encore identifié — ni code fiscal, ni date, lieu et pays de naissance — la demande répond 422 identification_required : complétez le profil avec vimine.me.update(...) et répétez (voir Membres sans code fiscal).

La carte numérique est un jeton signé (vmt_…) que le membre montre sous forme de code QR. Elle peut être vérifiée de deux façons.

Sans identifiants, sans données personnelles dans la réponse :

GET https://api.vimine.network/v1/verify/vmt_…
→ { "status": "active", "valid_until": "2027-03-31T22:59:59.000Z" }
ou
→ { "status": "inactive", "reason": "expired" }
ou
→ { "status": "inactive", "reason": "not_yet_valid", "valid_from": "2026-09-19T19:00:00.000Z" }

not_yet_valid : la carte est authentique et l’adhésion est active, mais l’organisation a fixé un délai entre l’admission et le moment où la carte peut être utilisée. valid_from dit quand elle commence à faire foi ; jusque-là, à l’entrée, c’est une carte qui ne compte pas. Le même instant figure dans le jeton (nbf), si bien qu’un scanner hors ligne arrive seul à la même réponse.

import { publicApi } from '@vimine/sdk';
const { status } = await publicApi().verifyCard(tokenReadFromQr);

Le jeton est signé en Ed25519 ; les clés publiques sont sur https://api.vimine.network/.well-known/vimine-card-keys.json (aussi GET /v1/card-keys, ou publicApi().cardKeys()). Un scanner vérifie trois choses avec la clé en cache : la signature, la fenêtre de validité (nbf/exp, 90 jours au plus, puis le jeton change) et que l’état dans le payload est actif — s === "active". Puis il se resynchronise en ligne quand le réseau revient. Un kid inconnu signifie que la clé a changé : rechargez les clés.

Le jeton change à chaque changement d’état, à chaque renouvellement et, de toute façon, tous les 90 jours : une vieille capture d’écran n’ouvre rien pour toujours.

Une carte n’existe que pour une adhésion active : tant que la demande est en attente (admission ou cotisation), et quand l’adhésion est suspendue ou révoquée, l’API n’émet aucun jeton — card vaut null dans GET /v1/me/membership, et GET /v1/me/membership/card, l’image, l’e-mail et le lien Google Wallet répondent 409 not_active. Vérifier s reste obligatoire : cela rejette les jetons émis avant cette règle et tout jeton qui ne déclare pas un état actif. Respectez aussi nbf : une organisation peut décider qu’une carte devient utilisable quelques heures après l’admission, et c’est là que cet instant voyage.

Le code fiscal est facultatif : une personne qui n’en a pas adhère avec sa date, son lieu et son pays de naissance (voir Membres sans code fiscal). Quand il y en a un, POST /v1/validate/fiscal-code (ou publicApi().validateFiscalCode(...)) vérifie la structure et le caractère de contrôle d’un codice fiscale italien et, si vous passez aussi les données personnelles, leur cohérence avec le code — et dit ce que le code lui-même contient :

const r = await publicApi().validateFiscalCode({ code: 'RSSMRA80A01H501U', first_name: 'Mario', last_name: 'Rossi', date: '1980-01-01', place: 'Roma', country: 'IT' });
// { valid: true, error: null, mismatches: [], hints: [], birth_date: '1980-01-01', birth_country: 'IT' }
// ou
// { valid: false, error: 'mismatch', mismatches: ['date'], hints: [], birth_date: '1980-01-01', birth_country: 'IT' }

error vaut invalid (structure ou caractère de contrôle), mismatch (un code bien formé qui est en désaccord avec les données envoyées) ou null ; hints contient names_swapped quand le code correspond une fois le nom et le prénom intervertis. Pour une personne née à l’étranger, envoyez country (ISO 3166-1 alpha-2) : son code ne contient que l’État, et place — la ville — n’est pas comparé.