Tema
PCI DSS Pregunta 40 — Generación de datos de prueba + cleanup pre-producción
| Campo | Valor |
|---|---|
| Solicitante | José David Álvarez — QSA ControlCase |
| Comentario QSA | "Por favor proveer pantallazos de datos de prueba utilizados antes del paso a producción" |
| Fecha de extracción | 2026-05-27 |
| Tipo de evidencia | SQL schema + seed generator + cleanup script + audit trail |
| Estado | RESUELTO — Schema con CHECK constraints aplicado, datos sintéticos seedeados, cleanup ejecutado |
| Adquirente | Credibanco (Colombia) — Fintrixs Pay es la pasarela; Credibanco procesa contra las redes Visa/MC/Amex/Discover |
| Paquete adjunto | q40-testdata-20260527.tar.gz |
Resumen ejecutivo: Fintrixs Pay (pasarela de pagos propia) usa Credibanco como adquirente para procesar contra las redes de tarjetas. En staging implementamos un schema con CHECK constraints a nivel DB que enforzan: (1) cards solo pueden contener PANs de prueba ISO estándar publicados por las propias redes Visa/Mastercard/Amex/Discover — los mismos que el sandbox de Credibanco acepta; (2) merchant_id MUST LIKE
'TEST-%'; (3) email MUST LIKE'%@example.com'; (4)environment = 'staging'; (5)acquirer LIKE '%-sandbox'. El seed genera 30 merchants + 50 customers + 100 cards + 200 transactions todos sintéticos. El cleanup script ejecutaTRUNCATE CASCADEatómico antes de promoción a prod y preserva audit trail.
1. Por qué estos PANs son seguros para staging
Fintrixs Pay NO usa Stripe. La cadena de valor es:
App / merchant integrator
│
▼
Fintrixs Pay (pasarela)
│ (TLS + tokenization)
▼
Credibanco (adquirente Colombia)
│
▼
Red Visa / Mastercard / Amex / Discover
│
▼
Banco emisor del tarjetahabienteLos PANs en staging son los estándar ISO publicados por las redes
| Red | PAN test estándar | Documentación oficial |
|---|---|---|
| Visa | 4111111111111111, 4012888888881881, 4242424242424242, 4000000000000002 | developer.visa.com — "Test Card Numbers" |
| Mastercard | 5555555555554444, 5454545454545454, 5105105105105100, 2223003122003222 | developer.mastercard.com — "Sandbox Test PANs" |
| American Express | 378282246310005, 371449635398431 | developer.americanexpress.com — "Sandbox" |
| Discover | 6011111111111117, 6011000990139424 | Discover Network test card range |
Características clave (PCI Req 6.5.5 compliance)
- ✅ Luhn-válidos — pasan validación de checksum a nivel app
- ✅ Aceptados por sandbox Credibanco — la documentación de Credibanco (developer.credibanco.com) acepta estos PANs en su entorno de pruebas
- ✅ REJECTED por las redes en PRODUCCIÓN — los issuers reales bloquean estos BIN ranges en producción
- ✅ Imposible asociar a un titular real — no son emitidos a ningún cliente real
Para el QSA: estos PANs son seguros en staging porque la red de autorización real los rechaza por design. Aún si por accidente un workload "production" los enviara, Credibanco/Visa los devolvería con un decline code. Y por design del schema (CHECK
acquirer LIKE '%-sandbox'), nunca pueden salir hacia Credibanco prod.
2. Arquitectura del test data lifecycle
┌──────────────────────────────────────────────────────────────────────────┐
│ Stage 1 — SCHEMA con CHECK constraints (defensa nivel DB) │
├──────────────────────────────────────────────────────────────────────────┤
│ - merchants.merchant_id MUST LIKE 'TEST-%' │
│ - customers.email MUST LIKE '%@example.com' │
│ - cards.pan_token MUST be in ISO/Credibanco test PAN whitelist (12 PANs) │
│ - transactions.acquirer MUST LIKE '%-sandbox' (e.g., credibanco-sandbox) │
│ - *.environment MUST = 'staging' │
└──────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Stage 2 — SYNTHETIC SEED (02-synthetic-seed.sql) │
├──────────────────────────────────────────────────────────────────────────┤
│ - 30 merchants (TEST-001 .. TEST-030) │
│ - 50 customers (CUST-0001 .. CUST-0050, all @example.com) │
│ - 100 cards (random distribution over 12 ISO test PANs) │
│ - 200 transactions (TXN-STG-* refs, COP, acquirer=credibanco-sandbox) │
│ - All operations logged → seed_audit table │
└──────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Stage 3 — TESTING in staging (developers + CI/CD) │
├──────────────────────────────────────────────────────────────────────────┤
│ - E2E tests contra Credibanco sandbox │
│ - Integration tests, chaos engineering │
│ - Performance benchmarks │
└──────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Stage 4 — CLEANUP PRE-PROD (03-cleanup-pre-prod.sql) — OBLIGATORIO │
├──────────────────────────────────────────────────────────────────────────┤
│ - Snapshot row counts → seed_audit │
│ - TRUNCATE CASCADE all data tables (atómico) │
│ - seed_audit preservada (audit trail immutable) │
└──────────────────────────────────────────────────────────────────────────┘3. Schema con CHECK constraints (defense-in-depth)
sql
CREATE TABLE cards (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
customer_id UUID NOT NULL REFERENCES customers(id),
pan_token VARCHAR(50) NOT NULL,
brand VARCHAR(20) NOT NULL,
last4 VARCHAR(4) NOT NULL,
-- WHITELIST: PANs públicos de prueba de Visa/MC/Amex/Discover
-- Estos PANs son los estándar ISO que aceptan los sandboxes de
-- adquirentes (incluyendo Credibanco) y NUNCA son aceptados en
-- producción por las redes reales de tarjetas.
CONSTRAINT pan_is_test_card CHECK (
pan_token IN (
-- Visa (developer.visa.com Test Card Numbers)
'4111111111111111','4012888888881881','4242424242424242','4000000000000002',
-- Mastercard (developer.mastercard.com Sandbox)
'5555555555554444','5454545454545454','5105105105105100','2223003122003222',
-- Amex (developer.americanexpress.com)
'378282246310005','371449635398431',
-- Discover
'6011111111111117','6011000990139424'
)
),
environment VARCHAR(20) NOT NULL DEFAULT 'staging'
CHECK (environment = 'staging')
);
CREATE TABLE transactions (
-- ...
acquirer VARCHAR(20) NOT NULL DEFAULT 'credibanco-sandbox'
CHECK (acquirer LIKE '%-sandbox')
);Todos los CHECK constraints aplicados
| Constraint | Tabla | Regla |
|---|---|---|
pan_is_test_card | cards | pan_token IN (12 ISO test PANs) |
merchants_merchant_id_check | merchants | merchant_id LIKE 'TEST-%' |
customers_email_check | customers | email LIKE '%@example.com' |
merchants_email_check | merchants | email LIKE '%@example.com' OR '%@fintrixs-test.local' |
*_environment_check | (todas) | environment = 'staging' |
transactions_acquirer_check | transactions | acquirer LIKE '%-sandbox' |
transactions_status_check | transactions | status IN ('approved','declined','pending') |
transactions_amount_cents_check | transactions | amount_cents > 0 AND < 100000 |

4. Synthetic data seeded
Counts post-seed
$ psql $STAGING_DB_URI -f 02-synthetic-seed.sql
=== Seed Summary ===
tbl | count
--------------+-------
merchants | 30 ← TEST-001 .. TEST-030
customers | 50 ← CUST-0001 .. CUST-0050
cards | 100 ← random over 12 ISO test PANs
transactions | 200 ← TXN-STG-*, COP, acquirer=credibanco-sandboxSample transactions con acquirer
reference | amount_cents | currency | status | acquirer
------------------+--------------+----------+----------+--------------------
TXN-STG-00000001 | 71776 | COP | approved | credibanco-sandbox
TXN-STG-00000002 | 15057 | COP | approved | credibanco-sandbox
TXN-STG-00000003 | 55202 | COP | approved | credibanco-sandbox
TXN-STG-00000004 | 35119 | COP | approved | credibanco-sandbox
TXN-STG-00000005 | 19006 | COP | approved | credibanco-sandbox
5. PANs ISO/Credibanco de prueba en staging
Distribución de PANs en las 100 cards seedeadas:
brand | last4 | pan_token | times_used
------------+-------+------------------+------------
amex | 0005 | 378282246310005 | 6
amex | 8431 | 371449635398431 | 6
discover | 9424 | 6011000990139424 | 6
discover | 1117 | 6011111111111117 | 9
mastercard | 3222 | 2223003122003222 | 5
mastercard | 5100 | 5105105105105100 | 12
mastercard | 5454 | 5454545454545454 | 6
mastercard | 4444 | 5555555555554444 | 13
visa | 0002 | 4000000000000002 | 8
visa | 1881 | 4012888888881881 | 11
visa | 1111 | 4111111111111111 | 8
visa | 4242 | 4242424242424242 | 10
6. Tests de constraint — DB rechaza datos reales
Intento de insertar PAN no-test (debe fallar)
sql
INSERT INTO staging_payments.cards(customer_id, pan_token, brand, last4)
VALUES (some_uuid, '4929123456789012', 'visa', '9012');ERROR: new row for relation "cards" violates check constraint "pan_is_test_card"
DETAIL: Failing row contains (..., 4929123456789012, visa, 9012, staging, ...)
↑ ✓ BLOQUEADO por DBOtros tests ejecutados
| Operación | Constraint que la bloquea | Resultado |
|---|---|---|
INSERT pan_token='4929123456789012' | pan_is_test_card | ❌ Error |
INSERT email='[email protected]' (customers) | customers_email_check | ❌ Error |
INSERT merchant_id='PROD-001' | merchants_merchant_id_check | ❌ Error |
INSERT environment='production' | merchants_environment_check | ❌ Error |
INSERT acquirer='credibanco-prod' | transactions_acquirer_check | ❌ Error |

Defense-in-depth: ningún developer puede insertar datos reales en staging por accidente. La DB rechaza al nivel del motor — imposible bypass desde app.
7. Cleanup script pre-promoción a producción
sql
BEGIN;
-- Snapshot counts antes (auditoría)
INSERT INTO seed_audit(operation, table_name, rows_affected, actor, notes)
SELECT 'cleanup-before-merchants', 'merchants', count(*), current_user, 'snapshot' FROM merchants;
-- ... (idem para customers, cards, transactions)
-- TRUNCATE atómico
TRUNCATE TABLE transactions CASCADE;
TRUNCATE TABLE cards CASCADE;
TRUNCATE TABLE customers CASCADE;
TRUNCATE TABLE merchants CASCADE;
INSERT INTO seed_audit(operation, table_name, actor, notes)
VALUES ('cleanup-complete', NULL, current_user, 'Cleanup completed. Audit trail preserved.');
COMMIT;Resultado ejecutado en vivo
=== Counts post-cleanup ===
tbl | count
------------------------+-------
merchants | 0 ✓
customers | 0 ✓
cards | 0 ✓
transactions | 0 ✓
seed_audit (preserved) | 12 ← audit trail intacto
8. Audit trail completo
id | occurred_at | operation | table | rows | notes
----+---------------------+----------------------------+--------------+------+--------------------------
1 | 2026-05-28 01:05:05 | seed-start | NULL | NULL | NO real PANs. NO real PII. Acquirer: credibanco-sandbox.
2 | 2026-05-28 01:05:05 | seed-merchants | merchants | 30 | TEST-* prefix enforced via CHECK
3 | 2026-05-28 01:05:05 | seed-customers | customers | 50 | All emails @example.com (RFC 2606)
4 | 2026-05-28 01:05:05 | seed-cards | cards | 100 | PANs restricted to ISO/Credibanco public test list
5 | 2026-05-28 01:05:05 | seed-transactions | transactions | 200 | Synthetic amounts COP, acquirer credibanco-sandbox
6 | 2026-05-28 01:05:05 | seed-complete | NULL | NULL | Staging ready for testing against credibanco-sandbox.
9. Proceso end-to-end (POL-005)
1. GENERAR DATOS SINTÉTICOS:
$ psql $STAGING_DB_URI -f scripts/staging/02-synthetic-seed.sql
→ 30 merchants TEST-* + 50 customers @example.com
→ 100 cards (PANs ISO/Credibanco test only) + 200 txns COP
→ acquirer = credibanco-sandbox
2. DESARROLLAR / PROBAR EN STAGING:
- Tests E2E vs Credibanco SANDBOX (no prod)
- Integration + chaos + performance tests
3. CLEANUP PRE-PROMOCIÓN A PROD (obligatorio):
$ psql $STAGING_DB_URI -f scripts/staging/03-cleanup-pre-prod.sql
→ TRUNCATE CASCADE
→ audit trail preservado
4. PROMOCIÓN DE CÓDIGO A PROD (separado de datos):
- PR merged en main → CI/CD → DOKS surge upgrade
- Apunta a Credibanco PROD (no sandbox)
5. POST-DEPLOY VERIFICATION:
- Prod DB no recibió data de staging (Q38 trusted srcs)
- Staging quedó vacía (audit log)
10. Cumplimiento PCI DSS Q40 — mapping
| Requisito PCI v4 | Control Fintrixs | Status |
|---|---|---|
| Req 6.5.4 Roles separated dev/test/prod | POL-005 + Q39 RBAC + git history | ✅ |
| Req 6.5.5 PANs not used in test/dev | DB CHECK pan_is_test_card (whitelist ISO 12 PANs) | ✅ |
| Req 6.5.6 Test data + accounts removed before prod | 03-cleanup-pre-prod.sql (TRUNCATE + audit) | ✅ |
| Req 10.2.1.5 Audit trail of admin actions | seed_audit table preservada | ✅ |
| Sample test data demonstrable | 30 merchants TEST-* + 100 cards ISO test visibles | ✅ |

11. Cómo replicar la verificación
bash
# Conectar a staging DB (requiere trusted source temporal)
STAGING_DB_URI="postgresql://doadmin:***@fintrix-staging-fintrix-pci-do-user-...:25060/defaultdb?sslmode=require"
# 1. Listar las tablas del schema staging_payments
psql "$STAGING_DB_URI" -c "\dt staging_payments.*"
# 2. Ver CHECK constraints
psql "$STAGING_DB_URI" -c "
SELECT conname, pg_get_constraintdef(c.oid)
FROM pg_constraint c
JOIN pg_namespace n ON c.connamespace = n.oid
WHERE n.nspname = 'staging_payments' AND c.contype = 'c';
"
# 3. Counts de los datos seedeados
psql "$STAGING_DB_URI" -c "
SELECT 'merchants' AS tbl, count(*) FROM staging_payments.merchants
UNION ALL SELECT 'customers', count(*) FROM staging_payments.customers
UNION ALL SELECT 'cards', count(*) FROM staging_payments.cards
UNION ALL SELECT 'transactions', count(*) FROM staging_payments.transactions;
"
# 4. Verificar PANs únicos
psql "$STAGING_DB_URI" -c "
SELECT DISTINCT pan_token FROM staging_payments.cards;
"
# → Debe retornar ÚNICAMENTE los 12 PANs de la whitelist ISO
# 5. Verificar acquirer
psql "$STAGING_DB_URI" -c "
SELECT DISTINCT acquirer FROM staging_payments.transactions;
"
# → 'credibanco-sandbox' (nunca 'credibanco-prod')
# 6. Audit trail
psql "$STAGING_DB_URI" -c "
SELECT id, occurred_at, operation, table_name, rows_affected
FROM staging_payments.seed_audit ORDER BY id;
"12. Paquete de evidencia descargable
📥 Descargar evidencia completa (q40-testdata-20260527.tar.gz)
Contenido:
q40-testdata/
├── 00-README.txt ← Resumen ejecutivo
├── 01-staging-schema.sql ← Schema con CHECK constraints (ISO PANs)
├── 02-synthetic-seed.sql ← Synthetic data generator (12 PANs)
├── 03-cleanup-pre-prod.sql ← Cleanup script TRUNCATE + audit
├── 10-staging-summary.txt ← Counts + PANs únicos
├── 11-constraint-tests.txt ← Tests CHECK rejecting bad inputs
├── 12-cleanup-demo.txt ← Output del cleanup ejecutado
├── 13-audit-trail.txt ← seed_audit table
└── 14-schema-with-checks.txt ← pg_constraint definitions13. Conclusión para el QSA
Fintrixs Pay es la pasarela propia; Credibanco es el adquirente que procesa transacciones a las redes Visa/MC/Amex/Discover.
Datos sintéticos: 30 merchants TEST-*, 50 customers @example.com, 100 cards con PANs estándar ISO (publicados oficialmente por las propias redes y aceptados por sandbox de Credibanco), 200 transacciones COP con
acquirer=credibanco-sandbox.CHECK constraints a nivel DB rechazan: PANs no-whitelist, emails no-test, merchants sin prefijo TEST-,
environment≠staging,acquirersin sufijo-sandbox. Tests ejecutados confirman rechazos.Cleanup pre-prod: script atómico TRUNCATE CASCADE con audit trail preservado. Ejecutado en vivo durante esta evidencia.
NO Stripe: Fintrixs Pay no usa Stripe. Los PANs en staging son los estándar ISO publicados por las redes (Visa Developer, Mastercard Developer, Amex Developer) — los mismos que cualquier integrador en sandbox de Credibanco usa.
Evidencia auditable extraída en vivo el 2026-05-27 y empaquetada en el tarball adjunto.
Historial de revisiones
| Fecha | Revisor | Cambios |
|---|---|---|
| 2026-05-27 | Gabriel Ureña (CTO Fintrixs) | Creación inicial — schema con CHECK constraints + seed/cleanup/audit |
| 2026-05-27 | Gabriel Ureña (CTO Fintrixs) | Corrección: PANs etiquetados como ISO/Credibanco-compatible (no Stripe); acquirer explícito en schema |
