Integración con React

Añade recopilación y tokenización de tarjetas compatible con PCI a tu aplicación React con el Tokeflow Bridge SDK — TokeflowProvider, hooks y componentes de elementos seguros.

El Tokeflow Bridge SDK incluye bindings de React de primera categoría para que puedas recopilar datos de tarjeta, exponer las capacidades del Merchant y tokenizar los detalles de pago sin manipular números de tarjeta en crudo. Los datos de tarjeta se recopilan dentro de iframes aislados y de origen cruzado (cross-origin), se cifran en el navegador y nunca llegan a tus servidores — tus componentes de React solo llegan a ver referencias de token.

Esta página cubre la capa de React: el TokeflowProvider, los hooks, los componentes de elementos y un formulario de pago completo y listo para TypeScript.

Los bindings de React son una envoltura ligera sobre el bridge principal. Todo lo que puedes hacer con el SDK vanilla está disponible aquí — el provider gestiona una única instancia del bridge y los hooks exponen su estado a tu árbol de componentes.

Cómo encaja todo

Tu aplicación React recopila los detalles de tarjeta en campos seguros, los tokeniza en el navegador y envía únicamente el tokenId resultante a tu backend. Tu backend usa luego su clave secreta para crear la transacción contra la Tokeflow API.

Usa una clave pública (pk_live_… / pk_test_…) en el código del navegador. Las claves secretas (sk_*) son solo del lado del servidor y nunca deben aparecer en los bundles del lado del cliente. Consulta Entornos y claves.

Instalación

npm install @tokeflow_com/bridge-js
# or: yarn add @tokeflow_com/bridge-js
# or: pnpm add @tokeflow_com/bridge-js

Los bindings de React viven en una exportación de subruta dedicada:

import {
  TokeflowProvider,
  useTokeflow,
  CardElement,
} from '@tokeflow_com/bridge-js/react';

Los tipos principales y las clases de error provienen de la exportación raíz:

import { TokeflowError, TokeflowErrorCode } from '@tokeflow_com/bridge-js';

Configuración del provider

Envuelve la parte de tu árbol que necesita pagos en un TokeflowProvider. El provider crea e inicializa una única instancia del bridge (que genera una sesión de navegador y carga el runtime de elementos seguros) y la comparte a través del contexto de React.

import { TokeflowProvider } from '@tokeflow_com/bridge-js/react';

export default function App() {
  return (
    <TokeflowProvider
      config={{
        publicKey: 'pk_test_your_public_key',
        locale: 'pt-BR',
        onError: (error) => console.error(error.code, error.message),
      }}
    >
      <YourApp />
    </TokeflowProvider>
  );
}

Configuración del provider

La prop config acepta la configuración completa del bridge. Las opciones más relevantes para una aplicación React:

FieldTypeRequiredDescription
publicKeystringSíTu clave pública de Tokeflow (pk_test_* o pk_live_*).
environment'sandbox' | 'production'NoEntorno de la API. Se detecta automáticamente a partir de la clave por defecto.
checkoutSessionIdstringNoVincula el bridge a una checkout session creada en el backend (cks_…). Requerido para useCheckout.
localeTokeflowLocaleNoLocale por defecto para cada elemento. Por defecto es 'en'.
translationsPartial<TokeflowTranslations>NoSobrescribe las cadenas de traducción integradas.
elementOptions{ style?: ElementStyle; fonts?: string[] }NoEstilo y fuentes por defecto aplicados a cada elemento.
autoRefreshSessionbooleanNoRefresca la sesión antes de que expire. Por defecto es true.
timeoutnumberNoTiempo de espera de la solicitud en ms. Por defecto es 30000.
onSessionCreated(session: Session) => voidNoSe llama cuando se crea una sesión.
onSessionExpired() => voidNoSe llama cuando la sesión expira.
onError(error: TokeflowError) => voidNoManejador de errores global.

Monta el provider tan alto en tu árbol como tenga sentido (por ejemplo, alrededor de tu ruta de checkout), pero no más arriba — inicializar el bridge genera una sesión, así que no hay ningún beneficio en crearlo antes de que el usuario llegue a una superficie de pago.

Hooks

Todos los hooks deben llamarse desde un componente renderizado dentro de un TokeflowProvider.

HookReturnsPurpose
useTokeflow(){ tokeflow, session, isReady, error }Accede a la instancia del bridge y al estado de disponibilidad.
useCapabilities()Capabilities | nullCapacidades del Merchant derivadas de la sesión.
useSessionSettings()SessionSettings | nullAjustes de tokenización (por ejemplo, marcas de tarjeta permitidas).
useAvailablePaymentMethods()AvailablePaymentMethod[]Métodos que el Merchant ha habilitado ('cards', 'pix', 'applePay', 'googlePay').
useCheckout(){ context, isLoading, error, refresh }Obtiene y almacena en caché el contexto de checkout vinculado.

useTokeflow

El hook principal. Condiciona tu UI a isReady antes de tocar el bridge, y muestra error si la inicialización falla.

import { useTokeflow } from '@tokeflow_com/bridge-js/react';

function PaymentForm() {
  const { tokeflow, session, isReady, error } = useTokeflow();

  if (error) return <div role="alert">Error: {error.message}</div>;
  if (!isReady) return <div>Loading payment form…</div>;

  // `tokeflow` is the initialized bridge instance.
  return <p>Session {session?.id} is ready.</p>;
}
FieldTypeDescription
tokeflowTokeflowBridge | nullLa instancia del bridge. null hasta que el provider se inicializa.
sessionSession | nullLa sesión de navegador actual.
isReadybooleantrue una vez que init() se ha resuelto y los elementos pueden montarse.
errorTokeflowError | nullError de inicialización, si lo hay.

useCapabilities y useSessionSettings

Lee las capacidades del Merchant y los ajustes de tokenización activos (como las marcas de tarjeta permitidas) para controlar la UI condicional.

import { useCapabilities, useSessionSettings } from '@tokeflow_com/bridge-js/react';

function CardBrands() {
  const capabilities = useCapabilities();
  const settings = useSessionSettings();

  if (!settings) return null;

  return (
    <ul>
      {settings.allowedCardBrands?.map((brand) => (
        <li key={brand}>{brand}</li>
      ))}
    </ul>
  );
}

Ambos hooks devuelven null hasta que la sesión esté disponible.

useAvailablePaymentMethods

Renderiza únicamente los métodos de pago que el Merchant ha habilitado.

import { useAvailablePaymentMethods } from '@tokeflow_com/bridge-js/react';

function MethodPicker() {
  const methods = useAvailablePaymentMethods(); // ('cards' | 'pix' | 'boleto' | 'applePay' | 'googlePay')[]

  return (
    <div>
      {methods.includes('cards') && <CardForm />}
      {methods.includes('pix') && <PixForm />}
      {methods.includes('boleto') && <BoletoForm />}
      {methods.includes('applePay') && <ApplePayButton />}
    </div>
  );
}

AvailablePaymentMethod incluye 'boleto' a partir del SDK 2.8.0. Un Record<AvailablePaymentMethod, …> o un switch exhaustivo sobre él necesita un caso para boleto.

useCheckout

Cuando el provider está configurado con un checkoutSessionId, useCheckout obtiene y almacena en caché el contexto de checkout — los ítems con precio, la moneda seleccionada, los métodos de pago disponibles del Merchant y los instrumentos guardados del cliente.

import { useCheckout } from '@tokeflow_com/bridge-js/react';

function CheckoutSummary() {
  const { context, isLoading, error, refresh } = useCheckout();

  if (error) return <p role="alert">{error.message}</p>;
  if (isLoading || !context) return <p>Loading…</p>;

  return (
    <>
      <ul>
        {context.checkout.items.map((item) => (
          <li key={item.id}>
            {item.offerId} — {item.amount} {context.checkout.selectedCurrency}
          </li>
        ))}
      </ul>
      <button onClick={refresh}>Refresh</button>
    </>
  );
}
FieldTypeDescription
contextCheckoutContext | nullEl contexto de checkout almacenado en caché. null hasta que se obtiene.
isLoadingbooleantrue mientras se obtiene el contexto.
errorTokeflowError | nullError de obtención, si lo hay.
refresh() => Promise<void>Vuelve a obtener después de que tu backend modifique el checkout (identifica al cliente, cambia ítems, etc.).

useCheckout requiere checkoutSessionId en la configuración del provider. Sin él, el hook muestra un error NO_CHECKOUT_SESSION. Crea la checkout session en tu backend con una clave secreta y luego pasa su id (cks_…) al provider. Consulta Checkout sessions.

Componentes de elementos

Cada elemento renderiza un iframe seguro y de origen cruzado (cross-origin) que recopila una parte de los datos de la tarjeta (o de facturación). El valor cifrado nunca sale del iframe — tu componente recibe únicamente eventos de cambio y, con el tiempo, una referencia de token.

React componentDescription
<CardElement>Número + fecha de vencimiento + CVC en un único iframe.
<CardNumberElement>Solo el número de tarjeta.
<CardExpirationElement>Solo la fecha de vencimiento.
<CardCvcElement>Solo el CVC/CVV.
<CardholderNameElement>Nombre en la tarjeta.
<TextElement>Entrada de texto genérica.
<CpfElement>Entrada de CPF brasileño (usada para PIX).
<AddressLine1Element> / <AddressLine2Element>Líneas de la dirección de facturación.
<CityElement> / <StateElement>Ciudad / estado de facturación.
<PostalCodeElement> / <CountryElement>Código postal / país de facturación.
<ApplePayButton> / <GooglePayButton>Botones de pago con wallet.

Props comunes

Los componentes de elementos aceptan las opciones del elemento más callbacks de eventos amigables con React:

PropTypeDescription
refRef<CardElementRef>Expone la instancia del elemento subyacente a través de ref.current.element.
idstringId del DOM para el contenedor del elemento.
classNamestringClase CSS en el contenedor.
onReady() => voidSe dispara cuando el campo seguro está listo.
onChange(e: ElementChangeEvent) => voidSe dispara en cada cambio de entrada (complete, empty, error, …).
onFocus / onBlur() => voidCiclo de vida del foco.
onError(e) => voidError a nivel de elemento.

El patrón .element del ref

Este es el idiom clave de React: cada componente de elemento reenvía un ref cuya propiedad .element es la instancia del elemento del bridge subyacente. Pasas esa instancia — no el componente de React — a tokenize().

import { useRef } from 'react';
import { CardElement, CardElementRef } from '@tokeflow_com/bridge-js/react';

function CombinedCardForm() {
  const cardRef = useRef<CardElementRef>(null);

  return (
    <CardElement
      ref={cardRef}
      id="card"
      onReady={() => console.log('Ready')}
      onChange={(e) => console.log('Complete:', e.complete, 'Error:', e.error?.message)}
      className="my-card-element"
    />
  );
}

// Later, on submit:
//   await tokeflow.cards.tokenize({ card: cardRef.current.element });

Campos combinados vs. divididos

Usa un único <CardElement>, o divide las entradas entre <CardNumberElement>, <CardExpirationElement> y <CardCvcElement> para tener control total del layout.

import { useRef } from 'react';
import {
  CardNumberElement,
  CardExpirationElement,
  CardCvcElement,
  CardNumberElementRef,
  CardExpirationElementRef,
  CardCvcElementRef,
} from '@tokeflow_com/bridge-js/react';

function SplitCardForm() {
  const numberRef = useRef<CardNumberElementRef>(null);
  const expiryRef = useRef<CardExpirationElementRef>(null);
  const cvcRef = useRef<CardCvcElementRef>(null);

  // On submit, pass the underlying instances — note the key is `expiration`:
  //   await tokeflow.cards.tokenize({
  //     cardNumber: numberRef.current.element,
  //     expiration: expiryRef.current.element,
  //     cvc: cvcRef.current.element,
  //   });

  return (
    <>
      <CardNumberElement ref={numberRef} id="number" />
      <CardExpirationElement ref={expiryRef} id="expiry" />
      <CardCvcElement ref={cvcRef} id="cvc" />
    </>
  );
}

Con elementos divididos, la clave de vencimiento es expiration (no expirationDate). Con el elemento combinado pasas una única clave card.

Tokenización

tokeflow.cards.tokenize(input, options?) cifra los datos de tarjeta recopilados y se resuelve a un CardToken. Envía únicamente token.tokenId a tu backend — nunca ningún campo de tarjeta en crudo.

const token = await tokeflow.cards.tokenize({
  card: cardRef.current.element,
});
// token.tokenId -> 'tok_…'

La forma del token:

interface CardToken {
  tokenId: string;       // your Tokeflow token reference (tok_…)
  type: 'card';
  brand: string;         // 'visa', 'mastercard', etc.
  last4: string;         // last 4 digits
  expiryMonth: string;   // e.g. '12'
  expiryYear: string;    // e.g. '2029'
  bin?: string;          // bank identification number (optional)
  createdAt?: string;    // ISO 8601 timestamp (optional)
}

Puedes adjuntar metadata en el momento de la tokenización:

const token = await tokeflow.cards.tokenize(
  { card: cardRef.current.element },
  { metadata: { cartId: 'cart_123' } }
);

Ejemplo de formulario de pago completo

Un formulario completo y amigable con TypeScript con control de disponibilidad, validación en línea, estado de carga y un POST a tu backend (que luego crea la transacción con su clave secreta).

import React, { useRef, useState, FormEvent } from 'react';
import {
  TokeflowProvider,
  useTokeflow,
  CardElement,
  CardElementRef,
} from '@tokeflow_com/bridge-js/react';
import { TokeflowError } from '@tokeflow_com/bridge-js';

function PaymentForm() {
  const { tokeflow, isReady } = useTokeflow();
  const cardRef = useRef<CardElementRef>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const handleSubmit = async (e: FormEvent) => {
    e.preventDefault();
    if (!tokeflow || !cardRef.current?.element) return;

    setLoading(true);
    setError(null);

    try {
      // 1. Tokenize in the browser — raw card data never reaches your servers.
      const token = await tokeflow.cards.tokenize({
        card: cardRef.current.element,
      });

      // 2. Send ONLY the token reference to your backend.
      const res = await fetch('/api/payments', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ tokenId: token.tokenId }),
      });

      if (!res.ok) throw new Error('Payment could not be completed.');

      alert('Payment successful!');
    } catch (err) {
      if (err instanceof TokeflowError) {
        // Field-level error from the secure element / tokenization.
        setError(err.message);
      } else {
        setError(err instanceof Error ? err.message : 'Unexpected error.');
      }
    } finally {
      setLoading(false);
    }
  };

  if (!isReady) return <div>Loading payment form…</div>;

  return (
    <form onSubmit={handleSubmit}>
      <div className="form-group">
        <label htmlFor="card-element">Card details</label>
        <CardElement
          ref={cardRef}
          id="card-element"
          onChange={(e) => setError(e.error?.message ?? null)}
        />
      </div>

      {error && (
        <div className="error" role="alert">
          {error}
        </div>
      )}

      <button type="submit" disabled={loading}>
        {loading ? 'Processing…' : 'Pay now'}
      </button>
    </form>
  );
}

export default function App() {
  return (
    <TokeflowProvider config={{ publicKey: 'pk_test_your_public_key' }}>
      <PaymentForm />
    </TokeflowProvider>
  );
}

En el servidor, tu handler de /api/payments recibe el tokenId y crea el cargo con tu clave secreta:

curl -X POST https://api.tokeflow.com/api/v1/transactions \
  -H "Authorization: Bearer sk_live_mer_your_secret_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5f1d9c2e-7a3b-4c8e-9f21-0a1b2c3d4e5f" \
  -d '{
    "amount": 9900,
    "currency": "BRL",
    "payment_method": "credit_card",
    "card_ciphertext_id": "tok_8f3c2a1b"
  }'
{
  "success": true,
  "data": {
    "id": "tx_7c9a1f2b",
    "status": "authorized",
    "amount": 9900,
    "currency": "BRL",
    "payment_method": "credit_card",
    "created_at": "2026-01-15T12:30:00.000Z"
  },
  "request_id": "req_8f3c2a1b",
  "timestamp": "2026-01-15T12:30:00.000Z"
}

Nunca llames a la Tokeflow API directamente desde el navegador con una clave secreta. El navegador tokeniza; tu servidor cobra. Consulta Autenticación.

Manejo de errores

La tokenización y las operaciones de elementos se rechazan con un TokeflowError. Ramifica según su code legible por máquina (un TokeflowErrorCode) para renderizar mensajes amigables.

import { TokeflowError, TokeflowErrorCode } from '@tokeflow_com/bridge-js';

try {
  const token = await tokeflow.cards.tokenize({ card: cardRef.current.element });
} catch (err) {
  if (err instanceof TokeflowError) {
    switch (err.code) {
      case TokeflowErrorCode.INVALID_CARD:
        setError('Please check your card number.');
        break;
      case TokeflowErrorCode.EXPIRED_CARD:
        setError('This card has expired.');
        break;
      case TokeflowErrorCode.ELEMENT_INCOMPLETE:
        setError('Please complete all card fields.');
        break;
      case TokeflowErrorCode.UNSUPPORTED_CARD_BRAND:
        setError('This card brand is not supported.');
        break;
      case TokeflowErrorCode.NETWORK_ERROR:
        setError('Network error. Please try again.');
        break;
      default:
        setError(err.message);
    }
    console.log('code:', err.code, 'requestId:', err.requestId);
  }
}

Códigos comunes que manejarás en un formulario de React: ELEMENT_INCOMPLETE, ELEMENT_INVALID, INVALID_CARD, EXPIRED_CARD, UNSUPPORTED_CARD_BRAND, TOKENIZATION_FAILED, NETWORK_ERROR, RATE_LIMITED y SERVER_ERROR. TokeflowError también expone httpStatus?, requestId? y details? para diagnósticos.

Establece un manejador onError global en la configuración del provider para reenviar errores (code, message, requestId) a tu propio servicio de logging en un único lugar.

<BoletoInstructions>

Renderiza las payment_instructions de un cargo con boleto (SDK 2.8.0+): la línea digitable agrupada tal como la imprime el banco, un botón de copiar, la fecha de vencimiento y un enlace "Abrir boleto" al boleto — que solo se muestra para una URL https.

import { BoletoInstructions } from '@tokeflow_com/bridge-js/react';

<BoletoInstructions
  instructions={paymentInstructions}  // from your server; type: 'boleto'
  locale="pt-BR"                      // default; or 'en'
  onCopy={() => track('boleto_copied')}
/>

No incluye estilos: aplica estilos a los nombres de clase tokeflow-boleto__*. Para PIX, renderiza tú mismo qr_code_text (copia e cola) y qr_code_url. Las formas de las instrucciones se exportan como PixInstructions y BoletoInstructions (en la entrada de React, BoletoInstructionsData).

A partir del SDK 2.8.1, la entrada de React importa el core en lugar de empaquetar su propia copia, así que err instanceof TokeflowError funciona para los errores lanzados por TokeflowProvider y el bundle de React es mucho más pequeño. Actualiza si usas ambas entradas.

Soporte de TypeScript

El SDK incluye definiciones de tipos completas. Los tipos de ref específicos de React provienen de la subruta /react:

import type {
  CardElementRef,
  CardNumberElementRef,
  CardExpirationElementRef,
  CardCvcElementRef,
} from '@tokeflow_com/bridge-js/react';

import type {
  Session,
  CardToken,
  TokenizeOptions,
  ElementStyle,
  ElementState,
  ElementChangeEvent,
  CardElementOptions,
  CheckoutContext,
} from '@tokeflow_com/bridge-js';

Próximos pasos

En esta página