# Zaidor AI Checkout Brief

Brief ligero para ChatGPT, Gemini, Claude, Cursor u otra IA. Objetivo: crear o evaluar una web transaccional usando Zaidor sin cargar documentación pesada ni exponer el kit completo.


## Validación real: external_signed_order sin catálogo Zaidor

La prueba local con kit activado confirmó el flujo completo de sandbox para `external_signed_order`:

```text
Demo local + backend Node
→ HMAC v1 con secreto sandbox del kit
→ Zaidor sandbox
→ Redsys sandbox
→ Registro de pagos / Gestión de operaciones
```

Este modo no requiere productos subidos en Autoservicio. Es el camino recomendado para una primera prueba de kit de integradores, tiendas externas, ERPs, Shopify/WooCommerce o webs a medida.

La alternativa `zaidor_catalog` sigue disponible cuando el comercio quiere que Zaidor sea la fuente del catálogo, cargando productos manualmente o con Excel/CSV y exponiéndolos mediante `checkout-lite-catalog`.

Nota fiscal: pago correcto y documento fiscal no son lo mismo. Si la fiscalidad del evento no estaba activada en el momento de la operación, el pago puede estar correcto y no aparecer aún en documentos fiscales. Activa la fiscalidad antes de las siguientes pruebas si quieres que el circuito fiscal se vea de forma natural.
## Importante: este brief público no es el kit completo

Este `.md` está pensado para que una IA pueda entender el patrón de integración y generar una demo, una maqueta o una base técnica segura.

No incluye credenciales, `clientCode` real, secreto HMAC, URLs autorizadas personalizadas ni el paquete completo de integrador. Para una integración real, el comercio/integrador debe solicitar acceso a la plataforma Zaidor y descargar desde su espacio privado el kit activado de su empresa/evento.

El kit activado debe incluir, como mínimo:

- Tipo de integración: `demo_multisite` o `site_bound`.
- `clientCode` asignado por Zaidor.
- En site concreto: dominio externo autorizado, Return URL, cancel URL y webhook URL autorizadas.
- En demo multi-site: aviso de que las URLs son dinámicas y solo válidas en sandbox.
- Métodos permitidos por el evento.
- Instrucciones HMAC si se activa sandbox firmado o producción.
- En kit activado privado, `ENV_SANDBOX.example` debe venir completo con secreto sandbox `zaidor_sk_test_...` para backend cuando no hay un secreto antiguo no recuperable. Si el cliente no tenía secreto, Zaidor crea el primero al descargar el kit. Ese secreto nunca sirve para producción.
- Reglas de webhook, idempotencia, conciliación y go-live.

## Qué se puede hacer solo con este MD

Con este documento una IA puede generar:

- Una landing de venta o demo.
- Un catálogo/carrito visual.
- Un formulario de comprador particular o empresa.
- Un backend mínimo de ejemplo tipo Netlify Function, Express, Next.js o similar.
- Una simulación local de checkout.
- Un patrón de integración que luego se conecta al kit real activado.
- Botones separados para ver ticket, ver factura y guardar en app, si existen URLs reales.

Lo que este MD no debe permitir por sí solo:

- Crear pagos reales de producción.
- Exponer secretos.
- Inventar `clientCode` productivo.
- Aceptar importes del navegador como fuente de verdad.
- Marcar pagos como completados solo por volver a una URL.
- Emitir tickets/facturas sin confirmación de Zaidor/backend.

Regla de oro:

```text
Frontend puede elegir.
Backend/Zaidor debe decidir.
```

## Dos formas de probar una integración externa

Zaidor admite dos modelos de prueba y ambos son válidos. Elige uno según quién tenga el catálogo.

### 1. Pedido externo firmado, sin productos en Autoservicio

Sirve para una tienda propia, Shopify, WooCommerce, ERP, web a medida o una demo local generada por IA. No hace falta subir productos a Zaidor.

El backend del integrador envía a Zaidor una orden firmada con líneas de pedido:

```json
{
  "pricingMode": "external_signed_order",
  "externalOrderId": "ORDER-10001",
  "items": [
    {
      "externalProductId": "DEMO-001",
      "name": "Producto demo",
      "quantity": 1,
      "unitAmount": 12.50,
      "taxRate": 21,
      "taxIncluded": true
    }
  ]
}
```

Zaidor valida la firma HMAC, recalcula el total desde esas líneas firmadas y crea el checkout. Este modo exige backend y secreto sandbox/producción; nunca debe hacerse desde HTML público.

### 2. Catálogo Zaidor, con productos subidos por Excel o manualmente

Sirve cuando Zaidor debe ser la fuente de productos, precios, IVA y disponibilidad. El comercio sube su catálogo completo desde Autoservicio, Zaidor publica el JSON y la web externa lo pinta. Al comprar, el backend manda solo `sku` o `productId` y cantidad:

```json
{
  "pricingMode": "zaidor_catalog",
  "items": [{ "sku": "MENU-DEMO", "quantity": 2 }]
}
```

Zaidor busca el producto activo en su catálogo, recalcula precio, IVA y disponibilidad, y crea el checkout.

Para una primera demo con kit activado, usa normalmente `external_signed_order`, porque no obliga a cargar productos previamente. Usa `zaidor_catalog` cuando quieras demostrar el flujo Excel → catálogo JSON → checkout.


El frontend puede elegir producto, cantidad, tarifa o comprador. El backend/Zaidor decide precio final, IVA, disponibilidad, stock, métodos permitidos, ticket, factura y estado final.

## Qué es Zaidor

Zaidor es una capa transaccional para unir venta online, punto físico, móvil como TPV, pagos multi-PSP, wearables NFC, ticketing, documentos fiscales, app y auditoría por evento.

No es solo una pasarela de pago. El PSP mueve dinero; Zaidor ayuda a cerrar el ciclo completo:

1. Crear catálogo o página de compra.
2. Enviar al checkout seguro.
3. Procesar tarjeta, Bizum, contactless/Tap to Pay, NFC o métodos activados.
4. Recibir webhook de pago.
5. Emitir ticket operativo cuando aplica.
6. Generar factura simplificada o factura completa según el comprador.
7. Custodiar documentos, hashes, QR documental y evidencias técnicas.
8. Si se activa el conector fiscal del evento, preparar remisión sandbox/producción, estados, reintentos y auditoría.
9. Devolver resultado a web, app, staff, centro fiscal y exportaciones.

## Valor diferencial: ticket + factura + app

Zaidor no debe entregar solo un estado de pago. El valor está en cerrar el paquete completo:

- Ticket operativo: acceso, viaje, recogida, pedido o comprobante de uso.
- Factura/documento fiscal: justificante con serie, número, IVA, hash, QR documental y destinatario cuando corresponda.
- App Zaidor: el comprador puede guardar la operación en su móvil y consultar después el ticket y la factura vinculada.

En una integración externa, si existe factura asociada, debe enviarse a la app como `invoiceUrl` o `fiscalDocumentUrl`, además del `ticketUrl` operativo. Si la app no está instalada, la web debe abrir la página de instalación de Zaidor para Android y permitir al usuario instalarla antes de guardar.

Ruta pública de instalación en Zaidor:

```text
/staff-app.html
/downloads/zaidor.apk
```

## Modalidades de integración

### Mapa rápido de evolución

1. **Kit IA público / MoC**: no necesita evento Zaidor ni credenciales. Sirve para crear una demo local, maqueta, catálogo, backend de ejemplo y patrón seguro. No crea operaciones en Zaidor.
2. **Evento demo multi-site**: se crea desde Zaidor cuando quieres probar varias webs generadas por IA o demos locales contra un mismo evento. No pide URLs fijas, acepta `localhost`, túneles o distintos dominios de prueba como `returnUrl/cancelUrl`, y queda bloqueado en sandbox. Nunca puede pasar a producción.
3. **Evento Checkout as a Service normal**: se crea como ahora, con site/URLs autorizadas. Puede empezar en sandbox y pasar a producción cuando el evento tenga credenciales live de Redsys/Stripe, HMAC, HTTPS, webhook/status y checklist.

Regla: demo multi-site reduce fricción para enseñar y probar. Producción siempre exige integración por site concreto.

### 1. Checkout Lite / JSON

La web externa puede leer un catálogo JSON publicado desde Autoservicio:

```text
/.netlify/functions/checkout-lite-catalog?eventId={EVENT_ID}
```

Sirve para MVP, demos, landings externas o asistentes IA. Devuelve productos, categorías, precios, IVA, conceptos fiscales y métodos activos.

Uso seguro:

- El catálogo sirve para pintar productos y ayudar al usuario a elegir.
- Al comprar, la web debe mandar `productId`, `quantity`, `fareId`, `cartId` o `reservationId`.
- Zaidor/backend recalcula siempre el precio final.
- El frontend no manda `amount` como fuente de verdad.


### Catálogo desde Autoservicio y Excel

Zaidor puede publicar un catálogo externo vivo a partir de productos creados a mano o importados desde una plantilla compatible con Excel.

Flujo esperado:

1. El comercio descarga la plantilla desde Autoservicio.
2. Rellena todos sus productos: SKU, categoría, nombre, precio, IVA, stock y activo.
3. Sube la lista completa a Zaidor.
4. Zaidor crea/actualiza productos y oculta los productos que ya no estén en el archivo.
5. La web externa consume el JSON vivo para pintar productos con su propio diseño.
6. Al comprar, la web envía `productId` o `sku` y cantidad a su backend.
7. El backend firma la petición y Zaidor recalcula precio, IVA, disponibilidad, ticket, factura y estado.

Endpoint de catálogo:

```text
/.netlify/functions/checkout-lite-catalog?eventId={EVENT_ID}
```

También puede resolverse por `event` o `code`.

Ejemplo seguro de petición desde backend a Zaidor:

```json
{
  "clientCode": "external-web",
  "externalOrderId": "ORDER-10001",
  "items": [
    { "sku": "MENU-DEMO", "quantity": 2 }
  ],
  "requestedMethod": "card",
  "returnUrl": "https://tienda.example/ok",
  "cancelUrl": "https://tienda.example/cancel"
}
```

Regla importante para la IA: no generes una tienda donde el navegador mande `amount` como fuente de verdad. La web externa muestra productos; Zaidor decide el importe final y crea el checkout.

Valor diferencial: el comercio no tiene que mover toda su tienda a Zaidor ni crear su propio checkout. Puede mantener una web externa con diseño propio e imágenes propias, mientras Zaidor se encarga de pago, ticket, factura, app/wallet y trazabilidad.

### 2. Backend seguro del integrador

Integración con backend propio del comercio. Es el patrón recomendado para producción estándar.

La web llama a su backend:

```text
POST /api/create-zaidor-checkout
```

El backend:

1. Lee producto/reserva/servicio desde su catálogo o desde Zaidor.
2. Si usa catálogo propio, calcula precio e IVA en su backend. Si usa catálogo Zaidor, envía `items` con `productId`/`sku` y cantidad para que Zaidor recalcule.
3. Crea la sesión Zaidor.
4. Redirige al `checkoutUrl` devuelto.
5. Recibe webhook firmado.
6. Actualiza su pedido solo tras confirmación.

### 3. Kit activado de Zaidor

Para pruebas reales, sandbox firmado o producción, el integrador debe descargar el kit desde la plataforma Zaidor. Ese kit no es público y queda asociado a una empresa/evento.

En modo demo multi-site, el kit activado debe incluir `ENV_SANDBOX.example` con un `ZAIDOR_CLIENT_SECRET` de test si el cliente no tiene un secreto antiguo no recuperable. Si no existía secreto, Zaidor crea el primero al descargar el kit. Ese valor solo debe usarse en backend y nunca permite producción.

En modo site concreto, el kit incluye URLs autorizadas y ejemplos backend. Puede empezar en sandbox y pasar a producción solo con URLs reales, secreto de producción separado, PSP live y checklist.

### 4. Checkout avanzado

Para integraciones con auditoría ampliada, Payment Hub multi-PSP, app artifact bundle, ticketing, documentos fiscales, remisión fiscal y operaciones enterprise.

## Comprador, factura simplificada y factura de empresa

La web integradora debe diferenciar dos casos.

### Compra particular

No mostrar de entrada campos de empresa. Usar los datos mínimos del comprador o viajero principal si existen:

```json
{
  "buyerFiscalProfile": {
    "buyerType": "consumer",
    "legalName": "Nombre Apellidos",
    "email": "comprador@example.com",
    "phone": "+34600111222",
    "country": "ES"
  }
}
```

Zaidor puede generar factura simplificada o documento fiscal-operativo con destinatario operativo si hay datos mínimos. No debe convertirse en factura B2B por el simple hecho de tener nombre.

### Factura a empresa o profesional

Mostrar los campos solo si el usuario marca una opción tipo “Necesito factura a nombre de empresa o profesional”. Si la marca, pedir al menos:

- Razón social.
- NIF/CIF/VAT.
- Domicilio fiscal.
- Código postal.
- Ciudad.
- Email de facturación.
- País.

Ejemplo:

```json
{
  "buyerFiscalProfile": {
    "buyerType": "business",
    "legalName": "Empresa Compradora S.L.",
    "taxId": "B12345674",
    "address": "Calle Demo 1",
    "postcode": "28001",
    "city": "Madrid",
    "province": "Madrid",
    "country": "ES",
    "email": "facturas@example.com"
  }
}
```

La validación final de NIF/CIF y VERI*FACTU corresponde a Zaidor/proveedor fiscal. El frontend puede guiar, pero no debe bloquear demos por validaciones excesivas salvo campos obligatorios.

## Ejemplo seguro de frontend

El frontend nunca manda un importe libre. Manda intención de compra:

```html
<button id="pay">Pagar con Zaidor</button>
<script>
document.getElementById('pay').onclick = async () => {
  const res = await fetch('/api/create-zaidor-checkout', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      productId: 'entrada-general',
      quantity: 2,
      buyerFiscalProfile: {
        buyerType: 'consumer',
        legalName: 'Nombre Apellidos',
        email: 'comprador@example.com',
        phone: '+34600111222',
        country: 'ES'
      }
    })
  });
  const data = await res.json();
  if (!res.ok || !data.checkoutUrl) throw new Error(data.message || 'No se pudo crear el checkout');
  location.href = data.checkoutUrl;
};
</script>
```

## Ejemplo seguro de backend mínimo

Este ejemplo es deliberadamente simple. En producción se sustituye el catálogo local por BBDD propia o catálogo Zaidor, se añade HMAC, idempotencia persistente y webhook firmado.

```js
// /api/create-zaidor-checkout.js o Netlify Function equivalente
const CATALOG = {
  'entrada-general': {
    name: 'Entrada general',
    price: 24.90,
    currency: 'EUR',
    taxRate: 0.21,
    active: true
  }
};

export async function handler(event) {
  const input = JSON.parse(event.body || '{}');
  const item = CATALOG[input.productId];
  const quantity = Math.max(1, Math.min(Number(input.quantity || 1), 10));

  if (!item || !item.active) {
    return json(400, { ok: false, message: 'Producto no disponible' });
  }

  const amount = Number((item.price * quantity).toFixed(2));
  const externalOrderId = 'ORDER-' + Date.now();

  // Con kit activado, estos valores vienen de Zaidor/admin, no se inventan.
  const payload = {
    clientCode: process.env.ZAIDOR_CLIENT_CODE,
    externalOrderId,
    amount,
    currency: item.currency,
    description: `${quantity} × ${item.name}`,
    requestedMethod: input.requestedMethod || 'card',
    returnUrl: process.env.ZAIDOR_RETURN_URL,
    cancelUrl: process.env.ZAIDOR_CANCEL_URL,
    webhookUrl: process.env.ZAIDOR_WEBHOOK_URL,
    buyerFiscalProfile: input.buyerFiscalProfile || null,
    metadata: {
      source: 'ai-generated-demo',
      productId: input.productId,
      quantity
    }
  };

  // En sandbox firmado / producción: firmar con HMAC desde backend.
  const nwRes = await fetch(process.env.ZAIDOR_BASE_URL + '/.netlify/functions/external-checkout-create', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Zaidor-Client': process.env.ZAIDOR_CLIENT_CODE
    },
    body: JSON.stringify(payload)
  });

  const data = await nwRes.json();
  return json(nwRes.status, data);
}

function json(statusCode, body) {
  return { statusCode, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) };
}
```

## Request mínimo de checkout desde backend

Este payload solo debe generarse desde backend, nunca desde HTML estático como fuente de verdad del precio.

```json
{
  "clientCode": "demo-client",
  "externalOrderId": "ORDER-1001",
  "amount": 84.80,
  "currency": "EUR",
  "requestedMethod": "card",
  "returnUrl": "https://tuweb.com/resultado?status={status}&session={session}",
  "cancelUrl": "https://tuweb.com/cancelado",
  "buyerFiscalProfile": {
    "buyerType": "consumer",
    "legalName": "Nombre Apellidos",
    "email": "comprador@example.com",
    "phone": "+34600111222",
    "country": "ES"
  },
  "artifact": {
    "type": "ticket",
    "label": "Entrada",
    "ticketUrl": "https://tuweb.com/ticket/ORDER-1001"
  }
}
```

## Resultado esperado

Tras pago confirmado, Zaidor puede devolver o exponer:

- `checkoutSessionId`
- `paymentStatus`
- `ticketUrl` o artifact operativo
- `fiscalDocumentUrl` si se genera factura/documento fiscal
- `fiscalDocumentCode`
- `fiscalDocumentHash`
- `exportStatus`
- `fiscalRemittanceStatus` si el conector fiscal del evento está activo

## Ticket vs factura

Ticket operativo: sirve para entrar, viajar, recoger, validar o consumir.

Factura/documento fiscal: sirve como justificante fiscal/comercial con serie, número, destinatario si aplica, IVA, hash, QR documental, custodia y, si procede, estado de remisión.

No son lo mismo. Una web debe mostrarlos separados:

- Descargar ticket.
- Ver factura.
- Guardar en app.

Si se guarda en la app y existe factura, la app debe recibir ambas referencias: `ticketUrl` + `invoiceUrl`/`fiscalDocumentUrl`. No mostrar botones que no tengan URL real.

## SIF / VERI*FACTU-ready y conector fiscal

Zaidor prepara estructura documental y exportable: serie, número, hash, QR documental, custodia y export fiscal.

El Centro fiscal permite trabajar por evento con:

- Sandbox fiscal activable por evento.
- Envío automático al sandbox si está activo.
- Producción fiscal preparada por evento, protegida hasta go-live específico.
- Contrato específico para activar remisión en producción.
- Pausa/reactivación con registro técnico paginado.
- Estados de remisión por documento.
- Evidencia técnica dentro de la propia factura.

Variables necesarias para sandbox fiscal real:

- `INVOPOP_SANDBOX_TOKEN` o `INVOPOP_API_TOKEN_SANDBOX`.
- `INVOPOP_VERIFACTU_SANDBOX_WORKFLOW_ID`.

La web externa no debe prometer remisión fiscal real si el evento solo está en modo manual o sandbox. Debe mostrar el estado fiscal que devuelve Zaidor.

## Prompt recomendado para IA

Crea una web de venta con catálogo, carrito, checkout seguro Zaidor, webhook, ticket operativo, factura/documento fiscal opcional, app artifact bundle y exportación básica. Usa backend para calcular importes desde producto, tarifa, reserva o catálogo. No confíes en el frontend para marcar pago como completado. No pongas secretos en el navegador. Separa ticket y factura. Para comprador particular, manda datos mínimos si existen; para empresa/profesional, despliega campos fiscales solo si el usuario lo solicita. Si la app no está instalada, ofrece instalar Zaidor desde `/staff-app.html` antes de guardar ticket y factura. Para una integración real, solicita acceso a Zaidor y usa el kit activado de la plataforma.

## App Wallet / Document Bundle v9.1.328

Zaidor puede entregar al comprador algo más que un estado de pago: una cartera local en la app donde guardar la operación y, cuando existan URLs reales, acceder al ticket operativo y a la factura o documento fiscal vinculado.

### Qué puede guardar la app

- **Operación**: importe, fecha, comercio/evento, canal, estado y referencia técnica.
- **Ticket operativo**: entrada, pedido, recogida, viaje, justificante de uso o comprobante del integrador.
- **Factura/documento fiscal**: factura simplificada, factura completa o documento fiscal-operativo cuando Zaidor lo haya generado o custodiado.

La app no es la fuente de verdad. La fuente de verdad sigue siendo Zaidor/backend/BBDD y el PSP. La app solo guarda una copia local útil y enlaces seguros a documentos reales.

### Campos recomendados

Usa URLs reales y útiles. No envíes home, buscadores, returnUrl, cancelUrl ni páginas genéricas como ticket.

```json
{
  "artifact": {
    "type": "ticket",
    "label": "Ticket operativo",
    "ticketUrl": "https://tuweb.com/ticket/ORDER-1001",
    "invoiceUrl": "https://zaidor.example/fiscal/document/INV-1001",
    "fiscalDocumentUrl": "https://zaidor.example/fiscal/document/INV-1001",
    "fiscalDocumentCode": "F2026-0001",
    "fiscalDocumentHash": "...",
    "documentBundle": "operation+ticket+invoice"
  }
}
```

### Reglas para integradores e IA

- Mostrar **Guardar en app** solo cuando exista una operación real o una sesión confirmada.
- Mostrar **Ver ticket** solo si `ticketUrl` existe y apunta al documento operativo correcto.
- Mostrar **Ver factura** solo si `invoiceUrl` o `fiscalDocumentUrl` existe.
- No marcar una operación como pagada por la URL de retorno; confirmar por webhook o consulta de estado.
- Si la app no está instalada, dirigir al usuario a `/staff-app.html`.
- La APK vigente se descarga en `/downloads/zaidor.apk` y es única dentro del ZIP web.

## Go-live real

Para producción, el integrador debe solicitar acceso en Zaidor y trabajar desde el kit activado de su empresa/evento. Ese kit debe descargarse desde la plataforma, no desde una ruta pública abierta. Producción requiere:

- HMAC o mecanismo de firma equivalente en backend.
- Webhook firmado.
- URLs HTTPS autorizadas.
- Idempotencia.
- PSP real configurado en el evento.
- Validación de ticket/factura/app antes de go-live.