Integración React - Configuración Básica

Aprende cómo integrar Bloque Payments en tu aplicación React usando el componente BloqueCheckout.

Instalación

Instala el paquete de componentes React:

npm install @bloque/payments-react
# o
yarn add @bloque/payments-react
# o
pnpm add @bloque/payments-react
# o
bun add @bloque/payments-react

Inicialización del SDK

Antes de usar el componente BloqueCheckout, debes inicializar el SDK con tu Public API Key:

import { init } from '@bloque/payments-react';

// Inicializar una vez al inicio de tu aplicación (ej: en main.tsx o App.tsx)
init({
  publicApiKey: 'pk_live_...',
  mode: 'sandbox', // o 'production'
});
Public API Key

Usa tu Public API Key para inicializar el SDK en el frontend. Esta clave es segura para exponer en código del lado del cliente. Tu Secret API Key solo debe usarse en el backend.

Uso Básico del Componente

La forma más simple de agregar pagos a tu app React. El componente procesa los pagos directamente con la API de Bloque:

import { init, BloqueCheckout } from '@bloque/payments-react';

// Inicializar una vez al inicio de tu aplicación
init({
  publicApiKey: 'pk_live_...',
  mode: 'production',
});

function CheckoutPage() {
  return (
    <BloqueCheckout
      config={{
        payment_methods: ['card', 'pse', 'cash'],
        amount: 2999,
        currency: 'USD',
      }}
      onSuccess={(response) => {
        console.log('¡Pago exitoso!', response.id);
        window.location.href = '/success';
      }}
      onError={(error) => {
        console.error('Pago fallido:', error.message);
        alert(`Pago fallido: ${error.message}`);
      }}
    />
  );
}

export default CheckoutPage;

Uso con Backend Personalizado

Si prefieres procesar los pagos a través de tu propio backend, usa la prop onSubmit:

import { BloqueCheckout } from '@bloque/payments-react';

function CheckoutPage() {
  const handleSubmit = async (payload) => {
    const response = await fetch('/api/payments', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    });

    if (!response.ok) {
      throw new Error('Pago fallido');
    }

    return response.json();
  };

  return (
    <BloqueCheckout
      config={{
        payment_methods: ['card', 'pse', 'cash'],
        amount: 2999,
        currency: 'USD',
      }}
      onSubmit={handleSubmit}
      onSuccess={(response) => {
        console.log('¡Pago exitoso!', response.id);
        window.location.href = '/success';
      }}
      onError={(error) => {
        console.error('Pago fallido:', error.message);
        alert(`Pago fallido: ${error.message}`);
      }}
    />
  );
}

export default CheckoutPage;

Props del Componente

Props Opcionales

onSubmit

Función llamada cuando el usuario envía el formulario de pago. Si no se proporciona, el componente procesa el pago directamente con la API de Bloque.

onSubmit?: (payload: PaymentSubmitPayload) => Promise<PaymentResponse | undefined>;

Ejemplo:

const handleSubmit = async (payload) => {
  const response = await fetch('/api/payments', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });

  if (!response.ok) {
    throw new Error('Pago fallido');
  }

  return response.json();
};

<BloqueCheckout onSubmit={handleSubmit} />

Props Opcionales

config

Objeto de configuración para la experiencia de checkout:

interface CheckoutConfig {
  payment_methods?: PaymentMethodType[];  // ['card', 'pse', 'cash']
  amount?: number;                         // Monto en la unidad más pequeña
  currency?: string;                       // Código de moneda (ej: 'USD')
  labels?: CheckoutLabels;                 // Textos personalizados
}

Ejemplo:

<BloqueCheckout
  config={{
    payment_methods: ['card', 'pse'],
    amount: 2999,
    currency: 'USD',
  }}
  onSubmit={handleSubmit}
/>

config.labels

Personaliza los textos del formulario de pago:

interface CheckoutLabels {
  card?: CardFormLabels;
  pse?: PSEFormLabels;
  cash?: CashFormLabels;
}

interface CardFormLabels {
  cardNumber?: string;
  cardNumberPlaceholder?: string;
  cardholderName?: string;
  cardholderNamePlaceholder?: string;
  expiryDate?: string;
  expiryDatePlaceholder?: string;
  cvv?: string;
  cvvPlaceholder?: string;
  email?: string;
  emailPlaceholder?: string;
  submitButton?: string;
}

Ejemplo con textos personalizados:

<BloqueCheckout
  config={{
    payment_methods: ['card', 'pse'],
    amount: 4999,
    currency: 'USD',
    labels: {
      card: {
        cardNumber: 'Número de tarjeta',
        cardNumberPlaceholder: '1234 5678 9012 3456',
        cardholderName: 'Nombre del titular',
        cardholderNamePlaceholder: 'JUAN PÉREZ',
        expiryDate: 'Fecha de expiración',
        expiryDatePlaceholder: 'MM/AA',
        cvv: 'CVV',
        cvvPlaceholder: '123',
        email: 'Correo electrónico',
        emailPlaceholder: 'ejemplo@correo.com',
        submitButton: 'Pagar $49.99',
      },
    },
  }}
  onSubmit={handleSubmit}
/>

appearance

Personaliza la apariencia visual:

interface AppearanceConfig {
  primaryColor?: string;    // Color de marca
  borderRadius?: string;    // Radio de borde para inputs
  fontFamily?: string;      // Familia de fuente
}

Ejemplo:

<BloqueCheckout
  config={config}
  appearance={{
    primaryColor: '#10b981',
    borderRadius: '12px',
    fontFamily: 'Inter, system-ui, sans-serif',
  }}
  onSubmit={handleSubmit}
/>

amount

Forma alternativa de establecer el monto (en lugar de usar config.amount):

<BloqueCheckout
  amount={2999}
  config={{
    payment_methods: ['card'],
    currency: 'USD',
  }}
  onSubmit={handleSubmit}
/>

availableMethods

Array de métodos de pago a mostrar (alternativa a config.payment_methods):

<BloqueCheckout
  availableMethods={['card', 'pse']}
  onSubmit={handleSubmit}
/>

requireEmail

Si el email es requerido para pagos con tarjeta (predeterminado: true):

<BloqueCheckout
  requireEmail={true}
  config={{ payment_methods: ['card'] }}
  onSubmit={handleSubmit}
/>

showMethodSelector

Mostrar/ocultar el selector de método de pago (predeterminado: true):

<BloqueCheckout
  showMethodSelector={true}
  config={{ payment_methods: ['card', 'pse'] }}
  onSubmit={handleSubmit}
/>

onSuccess

Callback disparado cuando el pago tiene éxito:

<BloqueCheckout
  config={config}
  onSubmit={handleSubmit}
  onSuccess={(response) => {
    console.log('¡Pago exitoso!');
    console.log('ID de Pago:', response.id);
    console.log('Estado:', response.status);
    window.location.href = '/success';
  }}
/>

onError

Callback disparado cuando el pago falla:

<BloqueCheckout
  config={config}
  onSubmit={handleSubmit}
  onError={(error) => {
    console.error('Pago fallido:', error.message);
    alert(`Pago fallido: ${error.message}`);
  }}
/>

className y style

Props estándar de React para estilos:

<BloqueCheckout
  config={config}
  onSubmit={handleSubmit}
  className="my-checkout"
  style={{ maxWidth: '600px', margin: '0 auto' }}
/>

Ejemplo Completo

Aquí hay un ejemplo completo con todas las opciones comunes:

import { BloqueCheckout } from '@bloque/payments-react';
import type {
  CheckoutConfig,
  AppearanceConfig,
  PaymentResponse,
} from '@bloque/payments-react';

function CheckoutPage() {
  // Configuración
  const config: CheckoutConfig = {
    payment_methods: ['card', 'pse', 'cash'],
    amount: 2999,
    currency: 'USD',
  };

  // Apariencia
  const appearance: AppearanceConfig = {
    primaryColor: '#10b981',
    borderRadius: '12px',
    fontFamily: 'Inter, system-ui, sans-serif',
  };

  // Manejador de envío
  const handleSubmit = async (payload) => {
    try {
      const response = await fetch('/api/payments', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload),
      });

      if (!response.ok) {
        const error = await response.json();
        throw new Error(error.message || 'Pago fallido');
      }

      return response.json();
    } catch (error) {
      console.error('Error de pago:', error);
      throw error;
    }
  };

  // Manejador de éxito
  const handleSuccess = (response: PaymentResponse) => {
    console.log('¡Pago exitoso!');
    console.log('ID de Pago:', response.id);
    console.log('Estado:', response.status);
    window.location.href = `/success?payment=${response.id}`;
  };

  // Manejador de error
  const handleError = (error: { message: string }) => {
    console.error('Pago fallido:', error.message);
    alert(`Pago fallido: ${error.message}`);
  };

  return (
    <div className="checkout-container">
      <h1>Checkout</h1>
      <BloqueCheckout
        config={config}
        appearance={appearance}
        onSubmit={handleSubmit}
        onSuccess={handleSuccess}
        onError={handleError}
        className="checkout-form"
        style={{ maxWidth: '600px' }}
      />
    </div>
  );
}

export default CheckoutPage;

Soporte TypeScript

El paquete incluye definiciones completas de TypeScript:

import type {
  BloqueCheckoutProps,
  CheckoutConfig,
  AppearanceConfig,
  PaymentResponse,
  PaymentSubmitPayload,
  PaymentMethodType,
} from '@bloque/payments-react';

// Configuración type-safe
const config: CheckoutConfig = {
  payment_methods: ['card', 'pse'],
  amount: 2999,
  currency: 'USD',
};

// Manejadores type-safe
const handleSuccess = (response: PaymentResponse) => {
  console.log('ID de Pago:', response.id);
};

Ejemplos Específicos por Framework

Next.js App Router

'use client';

import { BloqueCheckout } from '@bloque/payments-react';

export default function CheckoutPage() {
  const handleSubmit = async (payload) => {
    const response = await fetch('/api/payments', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    });
    return response.json();
  };

  return (
    <BloqueCheckout
      config={{
        payment_methods: ['card', 'pse'],
        amount: 2999,
        currency: 'USD',
      }}
      onSubmit={handleSubmit}
    />
  );
}

Next.js Pages Router

import { BloqueCheckout } from '@bloque/payments-react';

export default function Checkout() {
  const handleSubmit = async (payload) => {
    const response = await fetch('/api/payments', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    });
    return response.json();
  };

  return (
    <BloqueCheckout
      config={{
        payment_methods: ['card'],
        amount: 2999,
        currency: 'USD',
      }}
      onSubmit={handleSubmit}
    />
  );
}

Vite + React

import { BloqueCheckout } from '@bloque/payments-react';

function App() {
  const handleSubmit = async (payload) => {
    const response = await fetch('http://localhost:3000/api/payments', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    });
    return response.json();
  };

  return (
    <BloqueCheckout
      config={{
        payment_methods: ['card', 'pse', 'cash'],
        amount: 2999,
        currency: 'USD',
      }}
      onSubmit={handleSubmit}
    />
  );
}

export default App;

Próximos Pasos