Integración y verificación de carnés
El formulario de inscripción en tu sitio
Sección titulada «El formulario de inscripción en tu sitio»Cada asociación que ha activado la inscripción pública tiene una página https://app.vimine.network/enroll/<slug>
que se puede colocar en un iframe en el sitio de la asociación:
<iframe src="https://app.vimine.network/enroll/your-slug" style="width:100%;min-height:900px;border:0"></iframe>La asociación debe incluir el dominio del sitio entre sus dominios de integración en su configuración. En cambio, la página de inicio de sesión y de consentimiento de Iniciar sesión con Vimine no se puede colocar en un iframe: siempre es una página completa en vimine.network.
?locale=en abre el formulario en inglés (el elemento <vimine-enroll> lo pasa por sí solo, según el idioma de la
página).
El aspecto del formulario
Sección titulada «El aspecto del formulario»El formulario puede adoptar los colores de tu sitio. Una página en un iframe no ve el CSS de la página que la aloja, así que el estilo viaja en la dirección:
| Parámetro | Valores | |
|---|---|---|
scheme |
light, dark |
el esquema de la página; sin él, el del fondo o el del visitante |
accent |
un color hexadecimal (1e3a5f) |
botones, enlaces, campos con el foco y el nombre de la asociación |
background |
un color hexadecimal, o transparent |
el fondo detrás de las tarjetas; transparent deja ver tu página |
font |
inter, roboto, open-sans, montserrat, nunito, lora, merriweather, roboto-slab, system |
el tipo de letra de toda la página, campos incluidos, entre los que sirve el propio Vimine (system: el del dispositivo) |
<iframe src="https://app.vimine.network/enroll/your-slug?scheme=light&accent=1e3a5f&background=transparent" style="width:100%;min-height:900px;border:0"></iframe>Los demás valores se ignoran. Con <vimine-enroll> no hay ninguna dirección que escribir: define --vimine-scheme,
--vimine-accent, --vimine-background y --vimine-typeface en tu CSS (elementos). Con
transparent, elige el esquema que se lea bien sobre tu página: las tarjetas del formulario tienen su propio fondo,
el texto de fuera se apoya sobre el tuyo.
Quien ya tiene una cuenta de Vimine
Sección titulada «Quien ya tiene una cuenta de Vimine»Una sola identidad sirve para todas las asociaciones: quien ya tiene una cuenta no necesita rellenar de nuevo el
formulario. Abierta directamente, la página …/enroll/<slug> reconoce la sesión y muestra una sola pantalla — el
tipo de carné, el estatuto y la política de privacidad de esta asociación, y solo lo que esta asociación pide
además (la dirección, si ha declarado que la recoge). El estatuto y la política se aceptan cada vez: nada pasa de
una asociación a otra.
Dentro de un iframe la sesión no se puede leer: un navegador mantiene una página enmarcada separada del mismo sitio
abierto directamente. Por eso el formulario enmarcado ofrece ¿Ya tienes una cuenta de Vimine? Continuar en Vimine,
que abre la página en una pestaña nueva. Si escribes el iframe a mano y usas sandbox, incluye allow-popups y
allow-popups-to-escape-sandbox, o esa pestaña se queda dentro del sandbox; <vimine-enroll> ya lo hace.
El perfil público
Sección titulada «El perfil público»GET /v1/aps/{slug} devuelve el perfil público de la asociación — imagen, tipos de carné con sus cuotas, estatuto
en vigor — por si quieres construir una experiencia propia:
import { publicApi } from '@vimine/sdk';
const { aps, membership_types, statuto } = await publicApi().association('your-slug');Sin credenciales. Desde una página en la que el socio ha hecho Iniciar sesión con Vimine puedes luego recoger la solicitud:
const { aps, membership_types, statuto, privacy_notice } = await publicApi().association('your-slug');
await vimine.membership.request({ membership_type, accept_statuto: { version: statuto.version }, // la política de privacidad de la asociación, que tu página ha mostrado privacy_notice: privacy_notice && { version: privacy_notice.version, sha256: privacy_notice.sha256, locale: privacy_notice.locale }, // la dirección, si la asociación la recoge (aps.member_address: 'optional' | 'required') address: { line: 'Via Roma 1', postal_code: '20100', city: 'Milano', province: 'MI' }});Lo que una asociación pide a un solicitante no depende de la puerta por la que llegue la solicitud:
- la política de privacidad. Mientras la asociación tenga una en vigor que la persona aún no ha visto,
privacy_noticees obligatorio: sin él,422 validation_faileden el campoprivacy_notice; con una versión que ya no está en vigor,422 privacy_notice_outdated.vimine.legal.acceptances()dice lo que queda por confirmar; el texto está enGET /v1/aps/{slug}/privacy-notice. El acuse de recibo se guarda como una fila de prueba vinculada a esta inscripción. - la dirección.
aps.member_addressdice si la asociación la recoge: conrequired, una solicitud sinaddressresponde422 validation_faileden el campoaddress; connone, se ignora. - quién es la persona. Cuando el perfil aún no está identificado — ni código fiscal, ni fecha, lugar y país de
nacimiento — la solicitud responde
422 identification_required: completa el perfil convimine.me.update(...)y repite (consulta Socios sin código fiscal).
Verificación de carnés
Sección titulada «Verificación de carnés»El carné digital es un token firmado (vmt_…) que el socio muestra como código QR. Se puede verificar de dos
maneras.
En línea
Sección titulada «En línea»Sin credenciales, sin datos personales en la respuesta:
GET https://api.vimine.network/v1/verify/vmt_…→ { "status": "active", "valid_until": "2027-03-31T22:59:59.000Z" } o bien→ { "status": "inactive", "reason": "expired" } o bien→ { "status": "inactive", "reason": "not_yet_valid", "valid_from": "2026-09-19T19:00:00.000Z" }not_yet_valid: el carné es auténtico y la inscripción está activa, pero la entidad ha fijado un intervalo entre la
admisión y el momento en que el carné se puede usar. valid_from dice cuándo empieza a valer; hasta entonces, en la
entrada, es un carné que no vale. El mismo instante está en el token (nbf), así que un escáner sin conexión llega
solo a la misma respuesta.
import { publicApi } from '@vimine/sdk';
const { status } = await publicApi().verifyCard(tokenReadFromQr);Sin conexión
Sección titulada «Sin conexión»El token está firmado con Ed25519; las claves públicas están en
https://api.vimine.network/.well-known/vimine-card-keys.json (también GET /v1/card-keys, o
publicApi().cardKeys()). Un escáner verifica tres cosas con la clave en caché: la firma, la ventana de validez
(nbf/exp, como máximo 90 días, después el token rota) y que el estado del payload sea activo —
s === "active". Después se vuelve a sincronizar en línea cuando vuelve la red. Un kid desconocido significa
que la clave ha rotado: vuelve a cargar las claves.
El token rota con cada cambio de estado, con cada renovación y, en cualquier caso, cada 90 días: una captura de pantalla antigua no abre nada para siempre.
Un carné solo existe para una inscripción activa: mientras la solicitud está pendiente (admisión o cuota), y
cuando la inscripción está suspendida o revocada, la API no emite ningún token — card es null en
GET /v1/me/membership, y GET /v1/me/membership/card, la imagen, el correo y el enlace de Google Wallet responden
409 not_active. Comprobar s sigue siendo obligatorio: rechaza los tokens emitidos antes de esta regla y cualquier
token que no declare un estado activo. Respeta también nbf: una entidad puede decidir que un carné se pueda usar
algunas horas después de la admisión, y ese instante viaja ahí.
Código fiscal
Sección titulada «Código fiscal»El código fiscal es opcional: quien no lo tiene se inscribe con su fecha, lugar y país de nacimiento (consulta
Socios sin código fiscal). Cuando lo hay, POST /v1/validate/fiscal-code (o
publicApi().validateFiscalCode(...)) comprueba la estructura y el carácter de control de un codice fiscale
italiano y, si pasas también los datos personales, su coherencia con el código — y dice lo que contiene el propio
código:
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' }// o bien// { valid: false, error: 'mismatch', mismatches: ['date'], hints: [], birth_date: '1980-01-01', birth_country: 'IT' }error es invalid (estructura o carácter de control), mismatch (un código bien formado que no coincide con los
datos enviados) o null; hints contiene names_swapped cuando el código coincide al intercambiar apellidos y
nombre. Para una persona nacida en el extranjero envía country (ISO 3166-1 alpha-2): su código solo contiene el
país, y place — la ciudad — no se compara.