Ir al contenido

Integración y verificación de carnés

Ver como Markdown

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

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.

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_notice es obligatorio: sin él, 422 validation_failed en el campo privacy_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á en GET /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_address dice si la asociación la recoge: con required, una solicitud sin address responde 422 validation_failed en el campo address; con none, 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 con vimine.me.update(...) y repite (consulta Socios sin código fiscal).

El carné digital es un token firmado (vmt_…) que el socio muestra como código QR. Se puede verificar de dos maneras.

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);

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

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.