Skip to main content

Overview

The @yuno-payments/react package provides pre-built React components and hooks for integrating Yuno checkout into your React application. Components handle SDK lifecycle management, PCI compliance, and payment state automatically.
React components wrap the Yuno Web SDK. All features available in the Web SDK are accessible through React components and hooks.

Prerequisites

Installation

npm install @yuno-payments/react
npm install @yuno-payments/react

YunoProvider

Wrap your application (or the section that uses Yuno components) with <YunoProvider> to initialize the SDK context:
import { YunoProvider } from '@yuno-payments/react';

function App() {
  return (
    <YunoProvider
      publicApiKey="your-public-api-key"
      country="CO"
      language="es"
    >
      <CheckoutPage />
    </YunoProvider>
  );
}

YunoProvider Props

PropTypeRequiredDescription
publicApiKeystringYesYour Yuno public API key
countrystringYesISO 3166-1 alpha-2 country code
language'en' | 'es' | 'pt'NoUI language (auto-detected if omitted)
environment'sandbox' | 'production'NoDefaults to 'production'
themeYunoThemeNoTheme configuration object
childrenReactNodeYesChild components
Do not render multiple <YunoProvider> instances in the same React tree. Use a single provider at the top level.

YunoCheckout Component

Renders the full checkout UI with all available payment methods.
import { YunoCheckout } from '@yuno-payments/react';

function CheckoutPage() {
  return (
    <YunoCheckout
      checkoutSession="session_abc123"
      country="CO"
      language="es"
      onPaymentComplete={(data) => {
        console.log('Payment completed:', data.payment_id);
        window.location.href = `/confirmation?id=${data.payment_id}`;
      }}
      onError={(error) => {
        console.error('Payment error:', error.code, error.message);
      }}
    />
  );
}

YunoCheckout Props

PropTypeRequiredDescription
checkoutSessionstringYesCheckout session ID from server-side creation
countrystringYesISO 3166-1 alpha-2 country code
language'en' | 'es' | 'pt'NoOverrides provider language setting
onPaymentComplete(data: PaymentResult) => voidNoCalled when payment succeeds or is pending
onPaymentProcessing() => voidNoCalled when async processing begins
onError(error: YunoError) => voidNoCalled on payment failure
layoutLayoutOptionsNoLayout direction and display options
classNamestringNoCSS class for the container element
styleCSSPropertiesNoInline styles for the container element
Always verify the final payment status server-side via webhooks or the GET payment API. Client-side callbacks should not be the sole source of truth.

YunoPaymentForm Component

Renders a card-only payment form for custom checkout flows where you control payment method selection.
import { YunoPaymentForm } from '@yuno-payments/react';

function CardPaymentForm() {
  return (
    <YunoPaymentForm
      checkoutSession="session_abc123"
      paymentMethodType="CARD"
      onReady={() => console.log('Form ready')}
      onError={(error) => console.error(error)}
    />
  );
}

YunoPaymentForm Props

PropTypeRequiredDescription
checkoutSessionstringYesCheckout session ID
paymentMethodTypestringYesPayment method type (e.g., 'CARD')
onReady() => voidNoCalled when the form is rendered and interactive
onError(error: YunoError) => voidNoCalled on validation or payment errors
classNamestringNoCSS class for the container
styleCSSPropertiesNoInline styles for the container

Hooks

useYunoPayment

Provides imperative control over the payment flow. Use this hook when you need to trigger submission programmatically.
import { useYunoPayment } from '@yuno-payments/react';

function CustomCheckout() {
  const {
    submit,
    reset,
    getPaymentStatus,
    isSubmitting,
    error,
  } = useYunoPayment();

  const handleSubmit = async () => {
    const result = await submit();
    if (result.status === 'APPROVED') {
      window.location.href = '/success';
    }
  };

  return (
    <div>
      <YunoPaymentForm
        checkoutSession="session_abc123"
        paymentMethodType="CARD"
      />
      <button onClick={handleSubmit} disabled={isSubmitting}>
        {isSubmitting ? 'Processing...' : 'Pay Now'}
      </button>
      {error && <p className="error">{error.message}</p>}
    </div>
  );
}

useYunoPayment Return Type

PropertyTypeDescription
submit() => Promise<PaymentResult>Submits the current payment form
reset() => voidResets the form to its initial state
getPaymentStatus() => Promise<PaymentStatus>Queries current payment status
isSubmittingbooleantrue while a submission is in progress
errorYunoError | nullMost recent error, or null

useYunoCheckoutSession

Creates and manages checkout sessions client-side. Useful when session creation needs to be triggered dynamically.
import { useYunoCheckoutSession } from '@yuno-payments/react';

function DynamicCheckout({ orderId, amount }: Props) {
  const {
    session,
    isLoading,
    error,
    createSession,
  } = useYunoCheckoutSession();

  useEffect(() => {
    createSession({
      amount: { currency: 'USD', value: amount },
      country: 'CO',
      merchant_order_id: orderId,
      workflow: 'SDK_CHECKOUT',
    });
  }, [orderId, amount]);

  if (isLoading) return <div>Loading checkout...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!session) return null;

  return (
    <YunoCheckout
      checkoutSession={session.checkout_session}
      country="CO"
      onPaymentComplete={(data) => console.log(data)}
    />
  );
}

useYunoCheckoutSession Return Type

PropertyTypeDescription
sessionCheckoutSession | nullThe created session object
isLoadingbooleantrue while the session is being created
errorYunoError | nullError from session creation
createSession(params: SessionParams) => Promise<void>Triggers session creation

TypeScript Interfaces

interface YunoTheme {
  primary?: string;
  secondary?: string;
  background?: string;
  text?: string;
  error?: string;
  success?: string;
  borderRadius?: string;
  fontFamily?: string;
  fontSize?: string;
  mode?: 'light' | 'dark';
}

interface PaymentResult {
  payment_id: string;
  status: 'APPROVED' | 'PENDING' | 'DECLINED' | 'CANCELLED';
  amount: { currency: string; value: number };
  payment_method: string;
}

interface YunoError {
  code: string;
  message: string;
  details?: Record<string, unknown>;
}

interface PaymentStatus {
  payment_id: string;
  status: 'APPROVED' | 'PENDING' | 'DECLINED' | 'CANCELLED' | 'PROCESSING';
  provider_status?: string;
}

interface LayoutOptions {
  direction?: 'vertical' | 'horizontal';
  showHeader?: boolean;
  showPaymentMethodIcons?: boolean;
  compactMode?: boolean;
}

interface CheckoutSession {
  checkout_session: string;
  status: string;
  merchant_order_id: string;
}

interface SessionParams {
  amount: { currency: string; value: number };
  country: string;
  merchant_order_id: string;
  workflow: 'SDK_CHECKOUT';
  customer_id?: string;
}

Theming with CSS Variables

In addition to the theme prop on <YunoProvider>, you can override styles using CSS variables:
:root {
  --yuno-primary: #0066FF;
  --yuno-secondary: #6B7280;
  --yuno-background: #FFFFFF;
  --yuno-text: #1F2937;
  --yuno-error: #EF4444;
  --yuno-success: #10B981;
  --yuno-border-radius: 8px;
  --yuno-font-family: 'Inter', system-ui, sans-serif;
  --yuno-font-size: 14px;
}
CSS variables take the lowest precedence. The theme prop on <YunoProvider> overrides CSS variables, and the Checkout Builder in the Dashboard is overridden by both.

Error Boundary Integration

Wrap Yuno components in a React error boundary to handle unexpected SDK errors gracefully:
import { ErrorBoundary } from 'react-error-boundary';

function CheckoutErrorFallback({ error, resetErrorBoundary }) {
  return (
    <div role="alert">
      <p>Checkout failed to load.</p>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Retry</button>
    </div>
  );
}

function CheckoutPage() {
  return (
    <ErrorBoundary FallbackComponent={CheckoutErrorFallback}>
      <YunoCheckout
        checkoutSession="session_abc123"
        country="CO"
        onPaymentComplete={(data) => console.log(data)}
      />
    </ErrorBoundary>
  );
}

Server-Side Rendering (Next.js)

Yuno components require browser APIs and cannot render on the server. Use dynamic imports with SSR disabled:
import dynamic from 'next/dynamic';

const YunoCheckout = dynamic(
  () => import('@yuno-payments/react').then((mod) => mod.YunoCheckout),
  { ssr: false, loading: () => <div>Loading checkout...</div> }
);

export default function CheckoutPage({ sessionId }: { sessionId: string }) {
  return (
    <YunoCheckout
      checkoutSession={sessionId}
      country="CO"
      onPaymentComplete={(data) => console.log(data)}
    />
  );
}
Always create checkout sessions server-side (e.g., in getServerSideProps or a Route Handler) to keep your private API key secure.

Complete Example

A full checkout page combining provider, session management, and payment handling:
'use client';

import {
  YunoProvider,
  YunoCheckout,
  useYunoCheckoutSession,
} from '@yuno-payments/react';
import { useEffect, useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';

function CheckoutContent({ orderId, amount }: {
  orderId: string;
  amount: number;
}) {
  const { session, isLoading, error, createSession } =
    useYunoCheckoutSession();

  useEffect(() => {
    createSession({
      amount: { currency: 'USD', value: amount },
      country: 'CO',
      merchant_order_id: orderId,
      workflow: 'SDK_CHECKOUT',
    });
  }, [orderId, amount]);

  if (isLoading) return <p>Preparing checkout...</p>;
  if (error) return <p>Error: {error.message}</p>;
  if (!session) return null;

  return (
    <YunoCheckout
      checkoutSession={session.checkout_session}
      country="CO"
      language="es"
      onPaymentComplete={(data) => {
        window.location.href = `/confirmation?id=${data.payment_id}`;
      }}
      onError={(error) => {
        console.error('Payment failed:', error.code);
      }}
    />
  );
}

export default function CheckoutPage() {
  return (
    <YunoProvider
      publicApiKey={process.env.NEXT_PUBLIC_YUNO_KEY!}
      country="CO"
      language="es"
    >
      <ErrorBoundary fallback={<p>Checkout unavailable.</p>}>
        <h1>Complete Your Purchase</h1>
        <CheckoutContent orderId="order-456" amount={99.99} />
      </ErrorBoundary>
    </YunoProvider>
  );
}

Testing with React Testing Library

import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { YunoProvider, YunoCheckout } from '@yuno-payments/react';

// Mock the SDK module
jest.mock('@yuno-payments/react', () => ({
  ...jest.requireActual('@yuno-payments/react'),
  YunoCheckout: ({ onPaymentComplete, onError }: any) => (
    <div data-testid="yuno-checkout">
      <button onClick={() => onPaymentComplete({
        payment_id: 'pay_test_123',
        status: 'APPROVED',
      })}>
        Simulate Success
      </button>
      <button onClick={() => onError({
        code: 'DECLINED',
        message: 'Card declined',
      })}>
        Simulate Error
      </button>
    </div>
  ),
}));

describe('CheckoutPage', () => {
  it('handles successful payment', async () => {
    const onComplete = jest.fn();

    render(
      <YunoProvider publicApiKey="test-key" country="CO">
        <YunoCheckout
          checkoutSession="session_test"
          country="CO"
          onPaymentComplete={onComplete}
        />
      </YunoProvider>
    );

    await userEvent.click(screen.getByText('Simulate Success'));

    expect(onComplete).toHaveBeenCalledWith(
      expect.objectContaining({ status: 'APPROVED' })
    );
  });
});
Since Yuno SDK components depend on browser APIs and external scripts, mock the components in unit tests and use end-to-end tests (e.g., Cypress, Playwright) for full integration testing against the sandbox environment.