Link/React

React SDK

The CoverPay React SDK provides components, hooks, and a context provider for integrating BNPL checkout into React applications. Built on top of the JavaScript SDK with full TypeScript support.

Installation

npm install @coverpay/react

This package includes @coverpay/sdk as a dependency. You do not need to install both.

Provider Setup

Wrap your app (or the relevant subtree) with CoverPayProvider. This initializes the SDK and makes it available to all child components.

import { CoverPayProvider } from '@coverpay/react';

export default function App({ children }) {
  return (
    <CoverPayProvider
      clientId="your_client_id"
      environment="sandbox"
      onReady={() => console.log('CoverPay ready')}
    >
      {children}
    </CoverPayProvider>
  );
}

CoverPayButton

The simplest way to add BNPL checkout. Renders a styled button that opens the checkout modal when clicked.

import { CoverPayButton } from '@coverpay/react';

function CheckoutPage() {
  return (
    <CoverPayButton
      amount={9999}
      merchantName="My Store"
      customerEmail="user@example.com"
      theme="dark"
      onSuccess={(result) => {
        console.log('Token:', result.paymentToken);
        console.log('Provider:', result.provider);
        // Send token to your server
      }}
      onError={(error) => {
        console.error('Checkout error:', error.code);
      }}
      onClose={() => {
        console.log('Modal closed');
      }}
    />
  );
}

CoverPayButton Props

PropTypeRequiredDescription
amountnumberRequiredAmount in cents
merchantNamestringRequiredYour business name
customerEmailstringOptionalPre-fill customer email
orderIdstringOptionalYour order ID
theme"light" | "dark" | "auto"OptionalUI theme
onSuccess(result) => voidRequiredCalled on successful payment
onError(error) => voidOptionalCalled on error
onClose() => voidOptionalCalled when modal closes
disabledbooleanOptionalDisable the button
classNamestringOptionalCustom CSS class for the button
childrenReactNodeOptionalCustom button content

useCoverPay() Hook

For full control over the checkout flow, use the useCoverPay() hook. This gives you access to the SDK instance and checkout state.

import { useCoverPay } from '@coverpay/react';

function CustomCheckout() {
  const { open, ready, loading, error } = useCoverPay();

  const handleCheckout = async () => {
    const result = await open({
      amount: 9999,
      merchantName: 'My Store',
    });

    if (result.success) {
      // Handle success
      await confirmPayment(result.paymentToken);
    }
  };

  return (
    <div>
      <button
        onClick={handleCheckout}
        disabled={!ready || loading}
      >
        {loading ? 'Processing...' : 'Pay with BNPL'}
      </button>
      {error && <p>Error: {error.message}</p>}
    </div>
  );
}

Hook Return Values

PropertyTypeDescription
open(params) => Promise<Result>Opens the checkout modal
readybooleanWhether the SDK is loaded and ready
loadingbooleanWhether checkout is currently open
errorCoverPayError | nullLast error, if any
destroy() => voidDestroy the SDK instance

Custom Trigger (Headless Mode)

Build your own checkout button or trigger using the hook. This is useful when you want full control over the UI.

import { useCoverPay } from '@coverpay/react';

function ProductPage({ product }) {
  const { open, ready } = useCoverPay();

  const handleBuyNow = async () => {
    const result = await open({
      amount: product.priceInCents,
      merchantName: 'My Store',
      orderId: product.id,
      customerEmail: user.email,
    });

    if (result.success) {
      router.push(`/order/confirm?token=${result.paymentToken}`);
    }
  };

  return (
    <div className="product-card">
      <h2>{product.name}</h2>
      <p>${(product.priceInCents / 100).toFixed(2)}</p>
      <p className="installment-hint">
        or 4 payments of ${(product.priceInCents / 400).toFixed(2)}
      </p>
      <button onClick={handleBuyNow} disabled={!ready}>
        Buy Now, Pay Later
      </button>
    </div>
  );
}

TypeScript Types

import type {
  CoverPayConfig,
  CoverPayOpenParams,
  CoverPayResult,
  CoverPayError,
  CoverPayPlan,
  CoverPayProvider,
} from '@coverpay/react';

// CoverPayConfig
interface CoverPayConfig {
  clientId: string;
  environment: 'sandbox' | 'production';
  onReady?: () => void;
}

// CoverPayOpenParams
interface CoverPayOpenParams {
  amount: number;           // cents
  merchantName: string;
  customerEmail?: string;
  orderId?: string;
  theme?: 'light' | 'dark' | 'auto';
  locale?: string;
  onSuccess?: (result: CoverPayResult) => void;
  onError?: (error: CoverPayError) => void;
  onClose?: () => void;
}

// CoverPayResult
interface CoverPayResult {
  success: true;
  paymentToken: string;
  provider: CoverPayProvider;
  plan: CoverPayPlan;
}

// CoverPayPlan
interface CoverPayPlan {
  type: 'pay_in_4' | 'monthly' | 'custom';
  installments: number;
  installmentAmount: number; // cents
  totalAmount: number;       // cents
  apr: number;               // percentage
}

// CoverPayProvider
type CoverPayProvider =
  | 'coverpay'
  | 'klarna'
  | 'affirm'
  | 'afterpay'
  | 'paypal'
  | 'sezzle'
  | 'zip';

// CoverPayError
interface CoverPayError {
  code: string;
  message: string;
}

Complete Example with Next.js

app/layout.tsx

import { CoverPayProvider } from '@coverpay/react';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <CoverPayProvider
          clientId={process.env.NEXT_PUBLIC_COVERPAY_CLIENT_ID!}
          environment={
            process.env.NODE_ENV === 'production'
              ? 'production'
              : 'sandbox'
          }
        >
          {children}
        </CoverPayProvider>
      </body>
    </html>
  );
}

app/checkout/page.tsx

'use client';

import { useCoverPay } from '@coverpay/react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';

export default function CheckoutPage() {
  const router = useRouter();
  const { open, ready, loading } = useCoverPay();
  const [error, setError] = useState<string | null>(null);

  const cart = useCart(); // your cart hook

  const handleCheckout = async () => {
    setError(null);

    const result = await open({
      amount: cart.totalInCents,
      merchantName: 'My Store',
      customerEmail: cart.customerEmail,
      orderId: cart.orderId,
      theme: 'dark',
    });

    if (result.success) {
      // Confirm payment server-side
      const res = await fetch('/api/checkout/confirm', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          paymentToken: result.paymentToken,
          orderId: cart.orderId,
        }),
      });

      if (res.ok) {
        router.push('/order/success');
      } else {
        setError('Payment confirmation failed');
      }
    } else {
      setError(result.error?.message || 'Checkout failed');
    }
  };

  return (
    <div>
      <h1>Checkout</h1>
      <div>
        <p>Total: ${(cart.totalInCents / 100).toFixed(2)}</p>
        <button
          onClick={handleCheckout}
          disabled={!ready || loading}
        >
          {loading ? 'Processing...' : 'Pay with BNPL'}
        </button>
        {error && <p style={{ color: 'red' }}>{error}</p>}
      </div>
    </div>
  );
}

app/api/checkout/confirm/route.ts

import { NextResponse } from 'next/server';

export async function POST(request: Request) {
  const { paymentToken, orderId } = await request.json();

  // Confirm with CoverPay API
  const response = await fetch(
    'https://api.coverpayme.com/v1/checkout/confirm',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${process.env.COVERPAY_SECRET_KEY}`,
      },
      body: JSON.stringify({ paymentToken, orderId }),
    }
  );

  if (!response.ok) {
    return NextResponse.json(
      { error: 'Payment confirmation failed' },
      { status: 400 }
    );
  }

  const data = await response.json();

  // Update your order in your database
  await updateOrder(orderId, {
    status: 'paid',
    provider: data.provider,
    bnplPlan: data.plan,
  });

  return NextResponse.json({ success: true });
}

Always confirm server-side

Never trust the client-side result alone. Always confirm the payment token with the CoverPay API from your server before fulfilling the order. The client-side token is only a proof of authorization, not a confirmed payment.

Not using React?

Use the vanilla JavaScript SDK for Vue, Svelte, Angular, or any other framework. For iOS apps, see the iOS / Swift SDK.