--- type: SPEC asset_id: SPEC-REB-RClub-NumeracionTarjetas-v02 version: v02 status: Ratificado (decisión de diseño) · Draft de implementación owner: Victor Heredia sherpa_owner: Jay ratificador: Victor Heredia dirigido_a: Jesús — Equipo de Desarrollo EmpowerLabs fecha_creacion: 2026-06-11 fecha_ultima_actualizacion: 2026-06-11 supersede: SPEC-REB-RClub-NumeracionTarjetas-v01 intellbank: IB-REB-Rebelocity subbank: — tipo_largo: Especificación técnica de implementación — Esquema de numeración de tarjetas de membresía multi-comunidad (Card PAN). Documento de referencia canónico para desarrollo y para futuros cambios/upgrades. audiencia: Jesús (dev) + equipo de desarrollo + producto (EmpowerLabs) proposito: Especificar, con justificación, la estructura definitiva del número de tarjeta (PAN) de 16 dígitos para todas las comunidades colaborativas de la plataforma. Decisión de diseño ratificada (Opción A — nivel fuera del número). Sirve como contrato de implementación para el equipo de desarrollo y como documento de referencia para cualquier cambio o upgrade futuro. nota_scope: Esquema transversal a la plataforma (Tribus RRHH · Rebelocity Club · Beneficios para Trabajadores · Mi Playbook). Se archiva en IB-REB porque Rebelocity Club es la comunidad piloto. Candidato a copia/referencia en PB-EL-Plataforma. referencias_canonicas: - DC-REB-RClub-SistemaPuntosMembresias-v01 (motor Points v2 · tabla user_cards) - 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-v02 - **Version:** v02 (supersede v01) - **Status:** Decisión de diseño **ratificada** · implementación en Draft - **Owner:** Victor Heredia · **Sherpa:** Jay · **Ratificador:** Victor Heredia - **Dirigido a:** Jesús — Equipo de Desarrollo - **IntellBank:** IB-REB-Rebelocity - **Tipo:** SPEC — Especificación técnica de implementación / referencia canónica - **Última actualización:** 2026-06-11 --- # SPEC · Numeración de Tarjetas de Membresía (Card PAN) ## Documento de implementación y referencia — Plataforma de Comunidades Colaborativas > **Para Jesús (dev):** este documento es el contrato del número de tarjeta. La sección §1 te dice qué construir; §2–§3 explican **por qué** está diseñado así (para que cualquier cambio futuro respete las razones); §5–§7 son la implementación concreta (BD, generación, validación). Cualquier upgrade futuro del esquema debe partir de este documento y subir la versión (v03…). --- ## 0. Resumen ejecutivo Cada miembro de cualquier comunidad de la plataforma recibe un **número de tarjeta (PAN) de 16 dígitos**, con el formato familiar de una tarjeta de crédito (`XXXX XXXX XXXX XXXX`), construido bajo el estándar **ISO/IEC 7812** y cerrado con un **dígito verificador de Luhn**. El número está diseñado para **tres objetivos simultáneos**: 1. **Multi-comunidad:** un mismo esquema sirve para Tribus RRHH, Rebelocity Club y la comunidad de Beneficios para Trabajadores (que puede superar **1,000,000 de tarjetas**), e integra comunidades nuevas sin colisiones. 2. **Control y trazabilidad:** el número codifica la comunidad emisora, el grupo/empresa afiliante, el tipo de afiliado y el canal de afiliación. 3. **Estabilidad e integridad:** el número es inmutable durante toda la vida de la tarjeta y se autovalida con Luhn para detectar errores de captura. **Decisión de diseño ratificada (Opción A):** el **nivel de la tarjeta NO se codifica en el número**. El nivel es dinámico (sube solo con los puntos) y vive en la base de datos y en el arte visual de la tarjeta. El número solo codifica identidad **estable**. (Justificación completa en §3.) --- ## 1. Estructura del número (16 dígitos) — qué construir ``` 9 30 0045 2 2 187654 6 │ │ │ │ │ │ │ P1 P2-3 P4-7 P8 P9 P10-15 P16 red com. grupo tip can secuencial Luhn ``` Mostrar/imprimir agrupado: **`9300 0452 2187 6546`** | Pos | Díg. | Campo | Rango | Qué codifica | |---|---|---|---|---| | **P1** | 1 | **Red propietaria** | `9` (fijo) | Marca la tarjeta como privada de la plataforma. El `9` está reservado en ISO 7812 a uso privado/nacional → **nunca colisiona** con Visa (4), Mastercard (5), Amex (3), Discover (6). | | **P2–P3** | 2 | **Comunidad emisora** | `00`–`99` | El "emisor". Hasta 100 comunidades. Ej.: `10` Tribus RRHH · `20` Rebelocity Club · `30` Beneficios Trabajadores · `40` Mi Playbook. | | **P4–P7** | 4 | **Grupo / Empresa** | `0001`–`9999` | Empresa/organización afiliante. `0000` queda **reservado como "sin asignar / inválido"** (sentinel, nunca se emite); el bucket individual/B2C usa un código no-cero definido por comunidad (en RClub = `0010`). Hasta ~9,998 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. Se congela al emitir (ver §3.2). | | **P9** | 1 | **Canal de afiliación** | `0`–`9` | Categoría de cómo se afilió: `0` Directo/web · `1` Promotor de campo · `2` Empresa/RRHH · `3` Evento · `4` Alianza/partner. El afiliador **específico** va en BD. | | **P10–P15** | 6 | **Secuencial del miembro** | `100000`–`999999` | Correlativo dentro de (comunidad + grupo). **Arranca en `100000`** (no en 1) para no revelar escala ni mostrar números casi-vacíos. ~900,000 por bucket. | | **P16** | 1 | **Dígito verificador (Luhn)** | `0`–`9` | Checksum módulo 10 sobre P1–P15. | **Bloque emisor (análogo al BIN bancario):** `P1 + P2–P3` (`9` + comunidad). Es lo que permite enrutar y segmentar por prefijo, y dar de alta comunidades nuevas sin tocar el resto. --- ## 2. Justificación de fondo — por qué se parece a una tarjeta de crédito Las tarjetas de crédito siguen **ISO/IEC 7812**, que estructura el *Primary Account Number* (PAN) en tres bloques: **IIN/BIN** (identifica al emisor; su primer dígito es el **MII**, Major Industry Identifier), **identificador individual de cuenta**, y **dígito verificador Luhn**. **Qué tomamos del estándar y por qué:** - **Bloque de emisor al inicio** → en nuestro caso, la **comunidad**. Resuelve directamente el requerimiento de Victor: poder integrar muchas comunidades bajo un control común y enrutar por prefijo. - **Dígito verificador Luhn** → detecta el **100% de los errores de un solo dígito** y la mayoría de transposiciones de dígitos adyacentes. Es la red de seguridad contra números mal tecleados en mostrador, call center o formularios. - **16 dígitos en bloques de 4** → formato legible, imprimible/embozable y reconocible por cualquier usuario. **Qué NO tomamos (y por qué importa):** - **No es una tarjeta de pago.** No mueve dinero por redes bancarias. Por eso el prefijo **`9`** (uso privado/nacional en ISO 7812): nuestras tarjetas son auto-identificables como privadas y no chocan con ninguna red de pago. - **No registramos un IIN real.** Registrar un IIN ante ANSI / organismo nacional es un trámite pagado y solo es necesario si algún día las tarjetas se aceptan como medio de pago interbancario. Si ese escenario llega, **se migra únicamente el prefijo** a un IIN registrado, sin rediseñar el resto. --- ## 3. La decisión clave — por qué el NIVEL queda FUERA del número (Opción A) Esta es la decisión de diseño más importante del documento, y la razón por la que cualquier upgrade futuro **no debe** volver a meter el nivel en el número sin entender lo siguiente. ### 3.1 El nivel es dinámico por diseño del propio sistema El motor Points v2 (ver `DC-REB-RClub-SistemaPuntosMembresias-v01`) **sube el tier automáticamente** según los puntos de vida del miembro: `TierService.recalculateTier()` se ejecuta cada vez que se otorgan puntos. Un miembro pasa de **Normal → Clásica → Oro → Platino** con el tiempo. Los tiers **solo suben, nunca bajan**. ### 3.2 Un PAN es inmutable; un nivel cambia → no deben mezclarse El número de tarjeta es un identificador **inmutable**: una vez impreso, embozado o escaneado en un QR, no puede cambiar. Si codificáramos el nivel dentro del número: - Tras cada ascenso de tier, el número impreso quedaría **desactualizado** ("dice Básica" cuando el miembro ya es Oro). - Para mantener el número "verdadero" habría que **reimprimir la tarjeta en cada ascenso** → costo de plástico y logística innecesario. - El staff o los comercios podrían **leer el nivel del número** y equivocarse, porque el dato del número y el dato real (BD) divergen. ### 3.3 La solución: separar identidad (número) de estado (BD + arte) | Concepto | Naturaleza | Dónde vive | |---|---|---| | **Identidad** del miembro (quién es, de qué comunidad/grupo, qué tipo de afiliado, secuencial) | **Estable** | En el **número** (PAN) | | **Estado** del miembro (nivel/tier vigente, beneficios, saldo, badges) | **Dinámico** | En la **BD** (`user_cards.tier_code`, `user_points`) y en el **arte visual** de la tarjeta (que sí se actualiza) | > **Regla de oro permanente:** la **fuente de verdad** del nivel y de cualquier estado vigente es **siempre la base de datos**, nunca el número impreso. El PAN **identifica**; la BD **describe el estado actual**. *Cualquier cambio futuro del esquema debe respetar esta separación.* El **tipo de afiliado** (P8) sí va en el número porque es mayormente estable; cuando cambie (ej. un menor cumple 18), se trata como **reemisión** (ver §8), no como edición del número. ### 3.4 Qué hicimos con el dígito liberado Al sacar el nivel del número se liberó 1 dígito. Se reasignó a **ampliar el grupo/empresa de 3 a 4 dígitos** (de 999 a **9,999 empresas** por comunidad), porque la comunidad de Beneficios para Trabajadores se organiza por empleador y conviene soportar muchos empleadores. Se mantienen los **16 dígitos** para conservar el formato de tarjeta de crédito. --- ## 4. Tablas de códigos (a ratificar por Victor antes de codificar) Los catálogos son decisiones de negocio. Propuesta inicial — **confirmar o ajustar**: ### 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 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.3 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 hizo la afiliación) es de **alta cardinalidad** y **no cabe** en un dígito. Se guarda en BD como FK (`affiliations.affiliated_by_user_id`); P9 solo guarda la **categoría** del canal. La atribución comercial fina (comisiones por promotor) se resuelve en BD. --- ## 5. Capacidad — confirmación numérica | Concepto | Cálculo | Resultado | |---|---|---| | Secuencial por comunidad+grupo (arranca en `100000`) | 9×10⁵ | **~900,000** miembros | | Grupos/empresas por comunidad (4 díg., `0000` reservado) | ~10⁴ | ~9,998 grupos (`0010` = B2C default) | | Capacidad teórica por comunidad | ~10⁴ × 9×10⁵ | **~9,000,000,000** tarjetas | El requisito de **1 millón de tarjetas** para la comunidad de Beneficios queda cubierto **incluso dentro de un solo bucket de empresa** (cada empresa tiene ~900K). Si una comunidad B2C superara su bucket individual (`0010`), se habilitan buckets de overflow contiguos (`0011`…). --- ## 6. Dígito verificador (Luhn) — algoritmo y ejemplos Módulo 10, ISO/IEC 7812-1 Annex B (idéntico al de tarjetas de crédito): 1. Tomar los 15 dígitos P1–P15. 2. Desde el más a la derecha, **duplicar uno sí, uno no** (empezando por duplicar el de más a la derecha). 3. Si un duplicado supera 9, restarle 9. 4. Sumar todo → `S`. 5. `P16 = (10 − (S mod 10)) mod 10`. Validar = aplicar Luhn a los 16 dígitos completos; la suma total debe ser divisible entre 10. ### 6.1 Ejemplos verificados por código (Luhn correcto) | PAN | Significado | |---|---| | `9200 0100 0100 0007` | Rebelocity Club · individual (`0010`) · adulto · web · 1er miembro (sec `100000`) | | `9300 0452 2187 6546` | Beneficios Trab. · empresa `0045` · tercera edad · canal RRHH · sec `187654` | | `9100 0100 3100 0127` | Tribus RRHH · individual (`0010`) · adulto · canal evento · sec `100012` | | `9309 9991 1999 9995` | Beneficios Trab. · empresa `9999` · menor · canal promotor · sec `999999` | ### 6.2 Generación (pseudocódigo) ``` base15 = "9" + comunidad(2) + grupo(4) + 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; } // dígitos que se duplican sum += n; } return (10 - (sum % 10)) % 10; } function isValidPAN(pan) { // valida 16 dígitos let sum = 0; const d = pan.split('').reverse(); for (let i = 0; i < d.length; i++) { let n = parseInt(d[i], 10); if (i % 2 === 1) { n *= 2; if (n > 9) n -= 9; } sum += n; } return sum % 10 === 0; } ``` --- ## 7. Implementación en base de datos (Points v2) Cambios sobre el esquema documentado en `DC-REB-RClub-SistemaPuntosMembresias-v01`: 1. **`user_cards.card_number`**: `VARCHAR(12)` → **`VARCHAR(16)`** (mantener UNIQUE). *Opcional:* `VARCHAR(19)` para dejar margen al máximo ISO en el futuro. 2. **`card_sequences`** (ya aparece en el diagrama de relaciones): contador atómico por **(community_code, group_code)**. Asignar el secuencial con patrón transaccional ya usado para puntos: ```sql INSERT INTO card_sequences (community_code, group_code, seq) VALUES (?, ?, 1) ON DUPLICATE KEY UPDATE seq = LAST_INSERT_ID(seq + 1); -- el valor asignado = LAST_INSERT_ID(); rellenar a 6 dígitos con zero-pad ``` 3. **`affiliated_organizations`**: añadir `community_code CHAR(2)` (P2–P3). Dar de alta la comunidad **Beneficios para Trabajadores** (`30`). 4. **Catálogos** (tablas o config): `card_groups` (empresa → `group_code` P4–P7), `affiliate_types` (P8), `affiliation_channels` (P9). 5. **`affiliations`** (capa de afiliación del RFI): persistir `affiliated_by_user_id` (afiliador específico), `group_id`, `affiliate_type`, `channel`. **El número se deriva de estos campos al emitir.** 6. **Separación que 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 (permanentes) - **Unicidad global:** el PAN de 16 díg. es UNIQUE en toda la plataforma. La combinación prefijo + comunidad + grupo + secuencial ya lo garantiza; Luhn es control de captura, 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. El estado vigente se refleja en BD. - **Reemisión:** si cambia un dato estructural que sí está en el número (ej. tipo de afiliado de menor → adulto, o cambio de empresa), se **emite una tarjeta nueva** (nuevo PAN) y la anterior se marca `superseded`. El nivel **nunca** dispara reemisión (no está en el número). - **No reutilización:** un secuencial dado de baja **no** se recicla. - **El PAN no es credencial:** identifica, no autentica. La validación/autenticación vive en el QR firmado (HMAC) y en la sesión del usuario. --- ## 9. Cómo evolucionar este esquema (guía para upgrades futuros) Este documento es la **referencia canónica**. Cualquier cambio futuro debe: 1. **Subir versión** (v03…) y marcar este v02 como `superseded`. 2. **No introducir atributos mutables en el número** (ver §3) sin una razón de negocio explícita y un plan de reemisión. 3. **Conservar el prefijo `9`** mientras no sea tarjeta de pago; si se vuelve medio de pago, migrar solo el prefijo a un IIN registrado. 4. **Mantener Luhn** como último dígito y la longitud de 16 (o ampliar a 19 documentando el motivo). 5. **Preservar la separación identidad (PAN) ↔ estado (BD/arte).** --- ## 10. Pendientes para cerrar la implementación | # | Pendiente | Responsable | |---|---|---| | 1 | Ratificar tablas de códigos §4 (comunidades, tipos, canales) | Victor | | 2 | Confirmar `VARCHAR(16)` vs. `VARCHAR(19)` | Victor + Jesús | | 3 | Crear comunidad `30` Beneficios y su esquema de grupos/empresas | Jesús | | 4 | Implementar `card_sequences` atómico por (comunidad, grupo) | Jesús | | 5 | Definir si Beneficios admite afiliados sueltos (grupo individual `0010`) o siempre por empresa | Victor | | 6 | ¿Se requiere atribución comercial por afiliador (comisiones)? Modelar en BD | Victor + Jesús | --- > **Tripleta WORX** — Owner: Victor Heredia · Sherpa: Jay · Ratificador: Victor Heredia (decisión Opción A ratificada 2026-06-11). Implementación queda en Draft hasta cerrar los pendientes del §10.