> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fluz.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Descripción general de Secure Elements

> Incrusta un frame alojado por Fluz directamente en tu página para mostrarle a un usuario los detalles de su propia tarjeta virtual, sin que los datos de la tarjeta lleguen jamás a tus servidores ni al JavaScript de tu página.

<Warning>
  **Solo staging, solo reveal.** Secure Elements está en desarrollo activo.
  Todo en esta página y en la página de [Card Reveal](/build-a-platform/card-reveal)
  se ejecuta contra el entorno de staging de Fluz — los hosts de producción aún
  no están confirmados. Esta sección documenta únicamente la capacidad de
  **Card Reveal**; la recolección de una tarjeta física (tokenización) aún no se
  cubre aquí.
</Warning>

## Qué es Secure Elements

Secure Elements es un SDK de JavaScript, `@fluz/secure-elements`, que monta un frame aislado, alojado por Fluz, directamente dentro de un elemento contenedor en tu página. El frame renderiza los datos de la tarjeta; tu página y tus servidores solo mantienen un token opaco de corta duración que autoriza una acción específica.

Esta es una tercera forma de mostrarle a un usuario los detalles de su propia tarjeta, junto a las dos que ya tienes:

| Ruta                                                                                                  | Dónde son legibles los datos de la tarjeta                     | Lo que requiere                                                                                                                                                                         |
| :---------------------------------------------------------------------------------------------------- | :------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`revealVirtualCardByVirtualCardId`](/api-reference/mutations/reveal-virtual-card-by-virtual-card-id) | Tu propio servidor, en la respuesta del API                    | `PCI_COMPLIANCE` — un scope administrado por Fluz, otorgado solo a desarrolladores que hayan demostrado cumplimiento PCI DSS. (Las aplicaciones personales/privadas están exentas).     |
| [Embedded Widget](/developers/widgets)                                                                | El modal alojado por Fluz, a pantalla completa sobre tu página | Nada más allá del grant de OAuth — Fluz controla toda la superficie, incluido el reveal.                                                                                                |
| **Secure Elements Card Reveal**                                                                       | Un frame alojado por Fluz, montado inline dentro de tu layout  | Un client token emitido a partir de tu token de acceso OAuth existente. No se necesita el grant `PCI_COMPLIANCE` — tu página nunca recibe los datos, por lo que no entra en su alcance. |

<Note>
  Si ya usas el [Embedded Widget](/developers/widgets) para todo, no necesitas
  esto. Secure Elements es para integraciones headless o impulsadas por API que
  aún necesitan mostrarle al usuario su PAN, fecha de expiración y CVV sin abrir
  el modal completo del widget ni buscar `PCI_COMPLIANCE`.
</Note>

## Cómo funciona

```
Your backend  →  mint client token  →  Your frontend  →  SDK mounts Fluz frame  →  callback with result
```

<Steps>
  <Step title="Tu backend emite un client token" icon="server">
    Intercambia tu [token de acceso OAuth de Fluz](/build-a-platform/oauth-applications-overview)
    existente por un **client token** de corta duración, con alcance a un único reveal.
  </Step>

  <Step title="Tu frontend monta el frame" icon="app-window">
    Entrega el client token a `@fluz/secure-elements`, que monta el frame alojado
    por Fluz en un contenedor que tú provees — inline en tu página, no un modal.
  </Step>

  <Step title="El SDK reporta vía callbacks" icon="reply">
    Tu página nunca lee los datos crudos de la tarjeta. Solo ve eventos de éxito,
    error o montaje.
  </Step>
</Steps>

## Requisitos previos

* Tu aplicación está registrada con Fluz y tiene el scope `CREATE_VIRTUALCARD` habilitado en tu token de acceso.
* Tienes un id de tarjeta virtual `ACTIVE`, propiedad de la cuenta que estás revelando, para pasar al emitir un client token.

<Warning>
  **Pide a Fluz que incluya tu origen en la allow-list antes de escribir código.** El frame se niega a renderizar desde un origen que Fluz no haya preaprobado — hoy no existe un toggle de autoservicio para esto, así que es el único requisito previo que puede bloquearte si lo dejas para después.

  Envía un correo a [partnerships@fluz.app](mailto:partnerships@fluz.app) (o a tu ejecutivo de cuenta, si tienes uno) con el **nombre o ID de tu aplicación** y cada **origen** que necesites aprobar — cada `http://localhost:PORT` contra el que desarrolles, además de tus dominios de staging y producción. Fluz los agrega a la allow-list en el backend; no hay nada que configurar de tu lado una vez hecho.
</Warning>

## Entornos

| Entorno    | URL base                          |
| :--------- | :-------------------------------- |
| Staging    | `https://staging.secure.fluz.app` |
| Producción | `https://secure.fluz.app`         |

<Note>
  Actualmente, Reveal devuelve resultados simulados en staging mientras se
  finaliza la integración del procesador de Fluz. Usa staging para validar tu
  integración de extremo a extremo — la disponibilidad en producción se
  confirmará por separado.
</Note>

`frameHostOrigin` es opcional en `createCardViewer` — omítelo y usará producción por defecto (`https://secure.fluz.app`). Pásalo explícitamente para apuntar a staging. Solo se aceptan estos dos orígenes exactos; cualquier otro lanza un `FluzElementsError` (`error.code === "INVALID_FRAME_HOST_ORIGIN"`) tan pronto como llames a `createCardViewer`, antes de montar cualquier frame.

## Carga del SDK

`@fluz/secure-elements` no está publicado en npm — cárgalo como un build global de navegador (IIFE) desde el CDN de Fluz con una etiqueta `<script>`. Expone un global `FluzSecureElements`:

```html theme={null}
<script src="https://secure-cdn-staging.fluz.app/secure-elements/v0.1.0/index.global.js"></script>
<script>
  const { createCardViewer } = FluzSecureElements;

  const viewer = createCardViewer({
    /* ... */
  });
</script>
```

Cada ejemplo de código en esta página y en [Card Reveal](/build-a-platform/card-reveal) asume que cargaste la etiqueta de script y desestructuraste lo que necesitas de `FluzSecureElements`, como arriba.

Cada versión se publica en una ruta inmutable, fijada por versión (`.../v0.1.0/index.global.js`) y en una `.../latest/index.global.js` flotante que siempre apunta a la versión más reciente. Fija una versión específica para cualquier cosa más allá de un prototipo — `latest` puede cambiar sin previo aviso.

<Info>
  Solo el host del CDN de staging está activo por ahora (`secure-cdn-staging.fluz.app`).
  El hosting de producción se confirmará junto con la disponibilidad del API en producción.
</Info>

## Emitir un client token

Tu backend llama a esto usando el token de acceso OAuth de Fluz que ya obtienes mediante el [flujo de grant de OAuth](/client-facing-o-auth-grant-flow) estándar. **Nunca envíes ese token de acceso al navegador** — solo el par `clientToken` / `loadToken` que devuelve este endpoint debe llegar a tu frontend.

```text theme={null}
POST {baseUrl}/v1/client-token
Authorization: Bearer <your Fluz OAuth access token>
Content-Type: application/json

{
  "purpose": "reveal",
  "virtualCardId": "<uuid>"
}
```

```json theme={null}
{ "clientToken": "<token>", "loadToken": "<token>", "expiresIn": 300 }
```

Una emisión exitosa devuelve `201`. Ambos tokens son de un solo propósito y de corta duración — emite un par nuevo para cada reveal. `clientToken` es lo que autoriza el reveal en sí (`expiresIn` segundos, 300 por defecto); `loadToken` tiene un alcance aún más limitado (60 segundos) ya que viaja en una URL — ver la nota en [Card Reveal](/build-a-platform/card-reveal) — y se rechaza en cualquier lugar excepto al cargar el frame. Pasa ambos directamente a `createCardViewer`, y nunca pongas `clientToken` en una URL por tu cuenta — el SDK ya se encarga de mantenerlo fuera.

<Accordion title="Respuestas de error">
  | Status | Error                      | Significado                                                                            |
  | :----- | :------------------------- | :------------------------------------------------------------------------------------- |
  | 400    | `invalid_purpose`          | `purpose` faltante o inválido                                                          |
  | 400    | `virtual_card_id_required` | falta `virtualCardId` para un token de reveal                                          |
  | 401    | `unauthorized`             | falta el token de acceso                                                               |
  | 401    | `invalid_token`            | el token de acceso falló verificación (malformado, expirado, firma incorrecta)         |
  | 403    | `insufficient_scope`       | al token de acceso le falta el scope requerido                                         |
  | 403    | `app_not_registered`       | tu aplicación no está registrada — contacta a Fluz                                     |
  | 403    | `forbidden`                | tarjeta no encontrada o no es propiedad de este usuario                                |
  | 429    | `rate_limited`             | demasiadas solicitudes de client-token para este usuario — ver el header `Retry-After` |
  | 500    | `internal_error`           | error inesperado del servidor                                                          |
</Accordion>

## Estilizar campos

`createCardViewer` acepta un objeto opcional `style`, aplicado a cada campo que monta:

```js theme={null}
const viewer = createCardViewer({
  clientToken,
  loadToken,
  frameHostOrigin: "https://staging.secure.fluz.app",
  style: {
    color: "#1a1a1a",
    fontSize: "16px",
    fontWeight: "600",
    fontFamily: "Inter",
  },
});
```

`style` se valida antes de enviar algo al frame. Si un valor no coincide con lo documentado abajo, `await viewer.mount(...)` rechaza con un `FluzElementsError` (`error.code === "INVALID_STYLE"`) — envuelve tu llamada a `mount()` en un try/catch si aceptas entradas de estilo configurables.

| Propiedad    | Acepta                                                                                                  |
| :----------- | :------------------------------------------------------------------------------------------------------ |
| `color`      | Un color hex (`#1a1a1a` o `#111`), un valor `rgb(r, g, b)` o un color con nombre de CSS (`"slategray"`) |
| `fontSize`   | Un número seguido de `px`, `pt`, `em` o `rem` (p. ej., `"16px"`)                                        |
| `fontWeight` | `"normal"`, `"bold"`, o un múltiplo de 100 desde `"100"` hasta `"900"`                                  |
| `fontFamily` | Un nombre exacto de fuente del sistema o de familia de Google Fonts — ver abajo                         |

`fontFamily` debe coincidir de forma **exacta y con mayúsculas/minúsculas sensibles** con una de dos listas permitidas:

* **Fuentes del sistema** — stacks comunes del SO/web-safe (`system-ui`, `-apple-system`, `Helvetica Neue`, `Arial`, `Georgia`, `Menlo`, y las palabras clave genéricas `monospace` / `serif` / `sans-serif`, entre otras). Renderizan de inmediato, sin solicitud de red.
* **Google Fonts** — cualquier familia del catálogo de Google Fonts (`"Roboto"`, `"Inter"`, `"IBM Plex Mono"`, etc.), pasada exactamente como la lista Google. El SDK carga la fuente por ti — no necesitas una etiqueta `<link>` ni una regla `@font-face`.

<Note>
  Una Google Font se obtiene después de montar el campo, no se empaqueta de
  antemano, por lo que hay una breve ventana con caché fría en la que el campo
  se renderiza con la fuente de reserva del navegador antes de cambiar a la que
  elegiste. Una fuente del sistema no tiene tal demora.
</Note>

Ambas listas se exportan si quieres validar una elección de fuente, o construir un selector de fuentes, por tu cuenta:

```js theme={null}
const { SYSTEM_FONTS, GOOGLE_FONTS, isAllowedFontFamily } = FluzSecureElements;

isAllowedFontFamily("Roboto"); // true
isAllowedFontFamily("roboto"); // false -- exact, case-sensitive match required
```

## Content Security Policy

Si tu página define una CSP, permite el host del frame al que apuntas:

```text theme={null}
frame-src https://staging.secure.fluz.app;  # or https://secure.fluz.app in production
```

## Modelo de seguridad

* Tu token de acceso OAuth nunca sale de tus servidores.
* El client token que tu frontend mantiene es opaco y de un solo propósito — no lleva datos de tarjeta y no puede reutilizarse para una tarjeta o acción diferente.
* Los datos de la tarjeta solo son legibles dentro del frame alojado por Fluz, aislado del JavaScript de tu página. Cada campo configurado se monta como su propio iframe en sandbox (`allow-scripts allow-same-origin allow-forms`), con `referrerPolicy="no-referrer"` — el SDK nunca coloca datos de tarjeta en el DOM fuera de ellos.
* El frame solo se renderiza dentro de orígenes que hayas preregistrado con Fluz.

## Próximos pasos

<CardGroup cols={2}>
  <Card title="Card Reveal" icon="eye" href="/build-a-platform/card-reveal">
    Crea el visor de la tarjeta, móntalo y controla qué campos se revelan.
  </Card>

  {" "}

  <Card title="Demostración en vivo" icon="play" href="https://demo.secure.fluz.app/">
    Ve el visor de la tarjeta ejecutándose contra staging, incluyendo reveal,
    reveal-CVV-only y mask.
  </Card>

  {" "}

  <Card title="Integraciones de ejemplo" icon="github" href="https://github.com/fluz-app/secure-elements-examples">
    Ejemplos ejecutables en HTML simple y React, ambos llamando a la
    infraestructura real de staging.
  </Card>

  <Card title="Aplicaciones OAuth" icon="handshake" href="/build-a-platform/oauth-applications-overview">
    Cómo obtener el token de acceso que intercambiarás por un client token.
  </Card>
</CardGroup>
