--- type: SPEC asset_id: SPEC-REB-RClub-NumeracionTarjetas-v01 version: v01 status: Superseded por SPEC-REB-RClub-NumeracionTarjetas-v02 (decisión Opción A ratificada 2026-06-11) superseded_by: SPEC-REB-RClub-NumeracionTarjetas-v02 owner: Victor Heredia sherpa_owner: Jay fecha_creacion: 2026-06-11 fecha_ultima_actualizacion: 2026-06-11 intellbank: IB-REB-Rebelocity subbank: — tipo_largo: Especificación técnica — Esquema de numeración de tarjetas de membresía multi-comunidad (Card PAN) para la Plataforma de Comunidades Colaborativas audiencia: Equipo de desarrollo + producto (EmpowerLabs) proposito: Definir la estructura del número de tarjeta (PAN) que identifica de forma única, estable y verificable a cada miembro de cualquier comunidad colaborativa de la plataforma (Tribus RRHH · Rebelocity Club · Beneficios para Trabajadores), inspirada en el estándar de tarjetas de crédito (ISO/IEC 7812 + Luhn), con capacidad ≥ 1,000,000 de tarjetas por comunidad y campos de control: comunidad emisora, nivel, grupo/empresa, tipo de afiliado, canal de afiliación y secuencial. nota_scope: Esquema transversal a la plataforma. Se archiva en IB-REB porque Rebelocity Club es la comunidad piloto; aplica por igual a TRIBU y a la comunidad de beneficios. Candidato a copia/referencia en PB-EL-Plataforma. referencias_canonicas: - DC-REB-RClub-SistemaPuntosMembresias-v01 (motor Points v2 · tabla user_cards.card_number) - RFI-EL-PlataformaComunidades-AfiliacionTarjetaPuntos-v01 (capa de afiliación + tarjeta + puntos) - PLAN-EL-DesarrolloPlataformaComunidades-v01 (sprint dev · tarjeta de marca privada) - ISO/IEC 7812-1 (numeración de emisores / PAN / Luhn) fuentes_externas: - ISO/IEC 7812 — Wikipedia (estructura IIN/PAN, MII, longitud 8 dígitos) - ISO/IEC 7812-1:2006 (Annex B — fórmula de Luhn módulo 10) --- ## Asset Header - **Asset ID:** SPEC-REB-RClub-NumeracionTarjetas-v01 - **Version:** v01 - **Status:** Draft — pendiente de ratificación de las tablas de códigos (§4) - **Owner:** Victor Heredia - **Sherpa:** Jay - **Ratificador:** — - **IntellBank:** IB-REB-Rebelocity - **Tipo:** SPEC — Especificación técnica - **Propósito:** Esquema de numeración de tarjetas de membresía multi-comunidad. - **Última actualización:** 2026-06-11 --- # SPEC · Numeración de Tarjetas de Membresía (Card PAN) ## Plataforma de Comunidades Colaborativas — esquema multi-comunidad --- ## TL;DR — la decisión en una línea Adoptar un **PAN de 16 dígitos** (formato familiar de tarjeta de crédito, agrupado `XXXX XXXX XXXX XXXX`), con **prefijo de red propietario `9`**, que codifica **comunidad emisora · nivel · grupo/empresa · tipo de afiliado · canal de afiliación · secuencial de 6 dígitos** y cierra con un **dígito verificador Luhn**. Capacidad: **1,000,000 de tarjetas por cada combinación comunidad+grupo** (hasta mil millones por comunidad). Reemplaza el campo actual `user_cards.card_number VARCHAR(12)` por `VARCHAR(16)`. --- ## 0. Por qué inspirarse en la tarjeta de crédito (y qué SÍ y qué NO copiamos) Las tarjetas de crédito siguen el estándar **ISO/IEC 7812**, que define el *Primary Account Number* (PAN) en tres bloques: 1. **IIN / BIN** (Issuer Identification Number): identifica al emisor. El primer dígito es el **MII** (Major Industry Identifier). Desde 2017 el IIN pasó de 6 a **8 dígitos**. 2. **Identificador individual de cuenta**: el número del titular dentro del emisor. 3. **Dígito verificador**: calculado sobre todos los anteriores con la **fórmula de Luhn (módulo 10)**. **Qué copiamos** (porque resuelve exactamente lo que pides): - La idea de un **bloque de emisor al inicio** → en nuestro caso la **comunidad** (Tribus RRHH, Rebelocity Club, Beneficios). Esto permite integrar comunidades nuevas sin colisiones y enrutar/segmentar por prefijo. - El **dígito verificador Luhn** al final → detecta el 100% de los errores de un solo dígito y la mayoría de las transposiciones de dígitos adyacentes. Es lo que evita que un número tecleado mal "cuele". - El **formato de 16 dígitos en bloques de 4** → legible, imprimible/embozable y reconocible por el usuario. **Qué NO copiamos** (importante): - **No es una tarjeta de pago.** No tramita dinero por las redes bancarias. Por eso usamos el prefijo **`9`**, que en ISO/IEC 7812 está reservado para *uso nacional/privado* y **nunca colisiona** con Visa (4), Mastercard (5), Amex (3) ni Discover (6). Nuestro número es auto-identificable como "tarjeta privada de la plataforma". - **No registramos un IIN real** (eso es un trámite pagado ante ANSI/organismo nacional y solo hace falta si algún día las tarjetas se aceptan como medio de pago interbancario). Si ese caso llega, se migra el prefijo a un IIN registrado sin tocar el resto del diseño. --- ## 1. Estructura propuesta — 16 dígitos ``` 9 30 0 045 2 2 087654 7 │ │ │ │ │ │ │ │ P1 P2-3 P4 P5-7 P8 P9 P10-15 P16 ``` Agrupado para mostrar/imprimir: **`9300 0452 2087 6547`** | Pos | Dígitos | Campo | Rango | Qué codifica | |---|---|---|---|---| | **P1** | 1 | **Red propietaria** | `9` (fijo) | Marca todas nuestras tarjetas como privadas. No colisiona con redes de pago. | | **P2–P3** | 2 | **Comunidad emisora** | `00`–`99` | El "emisor". Hasta 100 comunidades. Ej.: `10` Tribus RRHH · `20` Rebelocity Club · `30` Beneficios Trabajadores. | | **P4** | 1 | **Nivel de tarjeta (a la emisión)** | `0`–`9` | `0` Básica/Normal · `1` Clásica · `2` Oro/Premium · `3` Platino · `4` LED/Black… ⚠️ Ver §3. | | **P5–P7** | 3 | **Grupo / Empresa** | `000`–`999` | Patrocinador/empresa afiliante. `000` = individual/B2C (sin grupo). Hasta 999 grupos por comunidad. | | **P8** | 1 | **Tipo de afiliado** | `0`–`9` | `0` Titular adulto · `1` Menor de edad · `2` Tercera edad · `3` Condición especial/PcD · `4` Beneficiario/dependiente… | | **P9** | 1 | **Canal de afiliación** | `0`–`9` | Categoría de quién/cómo se afilió: `0` Directo/web · `1` Promotor de campo · `2` Empresa/RRHH · `3` Evento · `4` Alianza… El afiliador específico (alta cardinalidad) vive en BD. | | **P10–P15** | 6 | **Secuencial del miembro** | `000000`–`999999` | Correlativo dentro de (comunidad + grupo). **1,000,000** por bucket. | | **P16** | 1 | **Dígito verificador (Luhn)** | `0`–`9` | Checksum módulo 10 sobre P1–P15. | **Lectura del bloque emisor (análogo al BIN):** los primeros **3 dígitos `9` + comunidad** son el "BIN" de la plataforma. Si más adelante quieres un BIN más largo estilo banca, P4 puede absorberse al bloque emisor sin cambiar la longitud total. --- ## 2. Por qué 16 dígitos y no 12 El campo actual en BD (`user_cards.card_number`) es de **12 dígitos**, pero 12 no alcanzan para todos los atributos que pides **más** un secuencial de 1M **más** Luhn: ``` comunidad(2) + grupo(3) + secuencial(6) + Luhn(1) = 12 → NO queda espacio para nivel, tipo de afiliado ni canal. ``` Con **16** entra todo con holgura y se mantiene el formato reconocible de tarjeta. Alternativa intermedia de **14** es viable si decides sacar `nivel` y `canal` del número (ver §3), pero **se recomienda 16** por consistencia visual y margen futuro. Implica una migración menor de `VARCHAR(12) → VARCHAR(16)`. --- ## 3. Decisión de diseño que debes ratificar — atributos mutables en el número El propio sistema Points v2 dice que **el tier sube automáticamente** según los puntos de vida (`TierService.recalculateTier()`). Es decir, **el nivel de un miembro CAMBIA con el tiempo** (Básica → Oro → Platino). Lo mismo ocurre, parcialmente, con el tipo de afiliado (un **menor** cumple 18 y pasa a **adulto**). Un PAN es **inmutable**: una vez impreso/embozado/escaneado, no debe cambiar. Por eso meter un atributo que cambia (nivel) dentro del número es, en rigor, un anti-patrón: el número impreso quedaría "mintiendo" tras un ascenso. Hay dos formas de resolverlo. **Ambas son válidas; es tu decisión:** **Opción A — recomendada (número 100% estable).** P4 (nivel) **sale** del PAN. El nivel vive solo en BD (`user_cards.tier_code`) y en el arte/diseño visual de la tarjeta (que sí puede actualizarse). El PAN codifica únicamente identidad estable. Resultado: PAN de **15 dígitos** (o se reasigna ese dígito a ampliar grupo/secuencial). El tipo de afiliado se congela "al momento de emisión". **Opción B — literal a lo que pediste (número con nivel).** Se mantiene P4 como **"nivel a la emisión"** (snapshot). El número no rastrea ascensos; el nivel vigente siempre se lee de BD y del arte. Sirve como dato histórico de con qué nivel entró el miembro. Esta SPEC está escrita sobre la Opción B para entregarte exactamente lo que describiste, pero **dejando la marca de que el valor vigente manda desde BD**. > **Regla de oro independientemente de la opción:** la **fuente de verdad** del nivel y del estatus vigente es siempre la base de datos (`user_cards` / `user_points`), nunca el número impreso. El PAN identifica; la BD describe el estado actual. --- ## 4. Tablas de códigos (a ratificar por Victor) Estos catálogos son decisiones de negocio. Propuesta inicial — **confírmalos o ajústalos**: ### 4.1 Comunidad emisora (P2–P3) | Código | Comunidad | Org en BD (`affiliated_organizations.org_code`) | |---|---|---| | `10` | Tribus RRHH | `TRIBU` | | `20` | Rebelocity Club | `CLUB` | | `30` | Beneficios para Trabajadores | _(nueva — crear)_ | | `40` | Mi Playbook (MPB) | `MPB` | | `90`–`99` | _Reservado pruebas/sandbox_ | — | ### 4.2 Nivel de tarjeta (P4) | Código | Nivel | Tier equivalente (Points v2) | |---|---|---| | `0` | Básica / Normal | `NORMAL` | | `1` | Clásica | `CLASICA` | | `2` | Oro / Premium | `ORO` | | `3` | Platino | `PLATINO` | | `4` | LED / Black (top) | _(definir)_ | ### 4.3 Tipo de afiliado (P8) | Código | Tipo | |---|---| | `0` | Titular adulto (estándar) | | `1` | Menor de edad | | `2` | Tercera edad | | `3` | Condición especial / PcD | | `4` | Beneficiario / dependiente | | `5`–`9` | _Reservado_ | ### 4.4 Canal de afiliación (P9) | Código | Canal | |---|---| | `0` | Directo / web | | `1` | Promotor de campo | | `2` | Empresa / RRHH | | `3` | Evento | | `4` | Alianza / partner | | `5`–`9` | _Reservado_ | > El **afiliador específico** (qué promotor, qué empleado de RRHH) es de **alta cardinalidad** y **no cabe** en un solo dígito. Se guarda en BD como FK (`affiliations.affiliated_by_user_id`) y P9 solo registra la **categoría** del canal. Si necesitas atribución comercial fina (comisiones por promotor), se resuelve en BD, no en el número. --- ## 5. Capacidad — ¿aguanta 1 millón de tarjetas? Sí, con holgura. - **Secuencial de 6 dígitos = 1,000,000** miembros por cada combinación **(comunidad + grupo)**. - La comunidad de **Beneficios para Trabajadores** se organiza por **empresa** (grupo): cada empresa tiene su propio bucket de 1M. Con 999 grupos posibles → **~1,000 millones** de tarjetas por comunidad. - Las comunidades **B2C** (Rebelocity Club individual) usan grupo `000`: 1M tarjetas en ese bucket. Si alguna superara 1M individuales, se habilitan buckets de overflow (`000`, luego `001`…) reservados a B2C. | Escenario | Bucket | Capacidad | |---|---|---| | Beneficios Trabajadores · 1 empresa | (30, empresa NNN) | 1,000,000 | | Beneficios Trabajadores · total | (30, 000–999) | 1,000,000,000 | | Rebelocity Club individual | (20, 000) | 1,000,000 | --- ## 6. El dígito verificador (Luhn) — cómo se calcula Algoritmo módulo 10 (ISO/IEC 7812-1, Annex B), idéntico al de las tarjetas de crédito: 1. Tomar los 15 dígitos P1–P15. 2. Desde el dígito **más a la derecha** de esos 15, **duplicar uno sí, uno no** (empezando por duplicar el más a la derecha). 3. Si un duplicado supera 9, restarle 9. 4. Sumar todos los dígitos resultantes → `S`. 5. Dígito verificador `P16 = (10 − (S mod 10)) mod 10`. Validar un número = aplicar Luhn a los 16 dígitos completos y comprobar que la suma total es divisible entre 10. ### 6.1 Ejemplos verificados (Luhn correcto, validados por código) | Número (PAN) | Significado | |---|---| | `9202 0000 0001 2341` | Rebelocity Club · Oro · individual · adulto · canal web · miembro #1234 | | `9300 0452 2087 6547` | Beneficios Trab. · Básica · empresa 045 · tercera edad · canal RRHH · miembro #87654 | | `9103 0000 3000 0126` | Tribus RRHH · Platino · individual · adulto · canal evento · miembro #12 | | `9349 9991 1999 9997` | Beneficios Trab. · LED · empresa 999 · menor · canal promotor · miembro #999999 | ### 6.2 Pseudocódigo de generación ``` base15 = "9" + comunidad(2) + nivel(1) + grupo(3) + tipoAfiliado(1) + canal(1) + secuencial(6) check = luhnCheckDigit(base15) PAN = base15 + check // 16 dígitos ``` ```js function luhnCheckDigit(num15) { let sum = 0; const d = num15.split('').reverse(); for (let i = 0; i < d.length; i++) { let n = parseInt(d[i], 10); if (i % 2 === 0) { n *= 2; if (n > 9) n -= 9; } // posiciones que se duplican sum += n; } return (10 - (sum % 10)) % 10; } ``` --- ## 7. Impacto en la base de datos (Points v2) Cambios mínimos sobre el esquema documentado en `DC-REB-RClub-SistemaPuntosMembresias-v01`: 1. **`user_cards.card_number`**: `VARCHAR(12)` → **`VARCHAR(16)`** (UNIQUE se mantiene). Considerar `VARCHAR(19)` si se quiere dejar margen al máximo ISO. 2. **`card_sequences`** (ya existe en el diagrama de relaciones): convertirla en contador atómico por **(comunidad, grupo)**. Asigna el secuencial de 6 dígitos con `INSERT ... ON DUPLICATE KEY UPDATE seq = LAST_INSERT_ID(seq+1)` o `SELECT ... FOR UPDATE`, igual que el patrón transaccional ya usado para puntos. 3. **`affiliated_organizations`**: añadir columna `community_code CHAR(2)` (P2–P3) y dar de alta la comunidad **Beneficios para Trabajadores** (`30`). 4. **Nuevas tablas de catálogo** (o `ENUM`/config): `card_groups` (empresa → código P5–P7), `affiliate_types` (P8), `affiliation_channels` (P9). 5. **`affiliations`** (capa de afiliación del RFI): guardar `affiliated_by_user_id` (afiliador específico), `group_id`, `affiliate_type`, `channel` — el número se deriva de aquí. 6. **Separación que ya existe y se conserva:** `card_id` (UUID opaco) sigue siendo la llave interna; `card_number` es el número humano/impreso/escaneable. El QR (`qr_secret = HMAC-SHA256(cardId, …)`) **sigue firmando sobre `card_id`**, no sobre el PAN — el PAN es legible, no secreto. --- ## 8. Reglas de gobernanza del número - **Unicidad global:** el PAN completo (16 díg.) es UNIQUE en toda la plataforma. La combinación de prefijo+comunidad+grupo+secuencial ya lo garantiza; Luhn es control de tecleo, no de unicidad. - **Inmutabilidad:** una vez emitido, el PAN no cambia aunque el miembro suba de nivel, cumpla la mayoría de edad o cambie de empresa. Cambios de estado se reflejan en BD y, si aplica, con **reemisión** de tarjeta (nuevo PAN, el anterior se marca `superseded`). - **No reutilización:** un secuencial dado de baja **no** se recicla. - **El PAN no es secreto:** es identificador, no credencial. La autenticación/validación vive en el QR firmado (HMAC) y en la sesión del usuario. --- ## 9. Preguntas abiertas para cerrar v02 1. ¿**Opción A o B** del §3? (nivel fuera del número vs. nivel-snapshot dentro). — *Decisión de Victor.* 2. ¿Se ratifican las **tablas de códigos** del §4 (comunidades, niveles, tipos, canales)? 3. ¿La comunidad de **Beneficios para Trabajadores** organiza siempre por empresa, o habrá afiliados sueltos (grupo `000`) dentro de ella? 4. ¿Hace falta **atribución comercial por afiliador** (comisiones)? Si sí, se especifica el modelo en BD (no afecta al número). 5. ¿Migramos el campo a `VARCHAR(16)` o dejamos `VARCHAR(19)` para futuro? --- > **Owner + Sherpa + Ratificador** — Owner: Victor Heredia · Sherpa: Jay · Ratificador: _pendiente_. Este SPEC queda **Draft** hasta ratificar §3 y §4.