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/reactThis 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
| Prop | Type | Required | Description |
|---|---|---|---|
amount | number | Required | Amount in cents |
merchantName | string | Required | Your business name |
customerEmail | string | Optional | Pre-fill customer email |
orderId | string | Optional | Your order ID |
theme | "light" | "dark" | "auto" | Optional | UI theme |
onSuccess | (result) => void | Required | Called on successful payment |
onError | (error) => void | Optional | Called on error |
onClose | () => void | Optional | Called when modal closes |
disabled | boolean | Optional | Disable the button |
className | string | Optional | Custom CSS class for the button |
children | ReactNode | Optional | Custom 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
| Property | Type | Description |
|---|---|---|
open | (params) => Promise<Result> | Opens the checkout modal |
ready | boolean | Whether the SDK is loaded and ready |
loading | boolean | Whether checkout is currently open |
error | CoverPayError | null | Last error, if any |
destroy | () => void | Destroy 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.