# React SDK

React components and hooks for integrating ZevPay Checkout. Wraps the [Inline Checkout SDK](./inline) with idiomatic React APIs.

## Installation

```bash
npm install @zevpay/react @zevpay/inline
```

Requires React 18 or later.

## Quick start

### Button component

The simplest way to add checkout — renders a button that opens the payment modal on click:

```tsx
import { ZevPayButton } from '@zevpay/react';

function CheckoutPage() {
  return (
    <ZevPayButton
      apiKey="pk_test_your_public_key"
      email="customer@example.com"
      amount={500000} // ₦5,000 in kobo
      onSuccess={(reference) => {
        console.log('Payment successful:', reference);
        // Verify payment on your server
      }}
    >
      Pay ₦5,000
    </ZevPayButton>
  );
}
```

### Hook

For custom UI — call `openCheckout()` from any event handler:

```tsx
import { useZevPayCheckout } from '@zevpay/react';

function CheckoutPage() {
  const { openCheckout } = useZevPayCheckout({
    apiKey: 'pk_test_your_public_key',
    onSuccess: (reference) => {
      console.log('Paid:', reference);
    },
    onClose: () => {
      console.log('Modal closed');
    },
  });

  return (
    <button onClick={() => openCheckout({
      email: 'customer@example.com',
      amount: 500000,
      reference: 'ORDER-123',
      firstName: 'John',
      lastName: 'Doe',
    })}>
      Pay ₦5,000
    </button>
  );
}
```

### Provider (shared API key)

Wrap your app in `<ZevPayProvider>` to avoid repeating `apiKey` on every component:

```tsx
import { ZevPayProvider, ZevPayButton } from '@zevpay/react';

function App() {
  return (
    <ZevPayProvider apiKey="pk_test_your_public_key">
      <StorePage />
    </ZevPayProvider>
  );
}

function StorePage() {
  // No apiKey needed — inherited from provider
  return (
    <ZevPayButton email="customer@example.com" amount={500000}>
      Pay ₦5,000
    </ZevPayButton>
  );
}
```

## `<ZevPayButton />`

A button that opens ZevPay Checkout on click.

### Props

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `apiKey` | `string` | Yes* | Your public API key (`pk_live_*` or `pk_test_*`) |
| `email` | `string` | Yes | Customer email address |
| `amount` | `number` | Yes | Amount in kobo (₦1 = 100 kobo) |
| `currency` | `string` | No | Currency code. Default: `"NGN"` |
| `reference` | `string` | No | Your unique transaction reference |
| `firstName` | `string` | No | Customer first name |
| `lastName` | `string` | No | Customer last name |
| `metadata` | `object` | No | Custom metadata attached to the transaction |
| `paymentMethods` | `"all"` \| `string[]` | No | Payment methods to show |
| `onSuccess` | `(reference: string) => void` | No | Called when payment is confirmed |
| `onClose` | `() => void` | No | Called when the modal closes |
| `onError` | `(error: Error) => void` | No | Called on error |
| `onFailure` | `(error?: Error) => void` | No | Called when payment fails |
| `onExpired` | `() => void` | No | Called when session expires |
| `onInitialize` | `() => void` | No | Called when session initializes |
| `children` | `ReactNode` | No | Button content. Default: `"Pay Now"` |
| `className` | `string` | No | CSS class name for the button |
| `disabled` | `boolean` | No | Whether the button is disabled |

*Required unless `<ZevPayProvider>` is used.

### Examples

```tsx
// Minimal
<ZevPayButton
  apiKey="pk_test_xxx"
  email="customer@example.com"
  amount={500000}
  onSuccess={(ref) => verifyPayment(ref)}
>
  Pay
</ZevPayButton>

// With all options
<ZevPayButton
  apiKey="pk_test_xxx"
  email="customer@example.com"
  amount={500000}
  currency="NGN"
  reference="ORDER-123"
  firstName="John"
  lastName="Doe"
  metadata={{ orderId: '123' }}
  paymentMethods={['bank', 'payid']}
  className="btn btn-primary"
  onSuccess={(ref) => verifyPayment(ref)}
  onClose={() => console.log('closed')}
  onError={(err) => console.error(err)}
>
  Pay ₦5,000
</ZevPayButton>
```

## `useZevPayCheckout(config)`

Hook for imperative checkout control.

### Config

Shared options that persist across calls:

| Option | Type | Description |
|--------|------|-------------|
| `apiKey` | `string` | Public API key (or use `<ZevPayProvider>`) |
| `currency` | `string` | Currency code |
| `paymentMethods` | `"all"` \| `string[]` | Payment methods to enable |
| `onSuccess` | `function` | Success callback |
| `onClose` | `function` | Close callback |
| `onError` | `function` | Error callback |
| `onFailure` | `function` | Failure callback |
| `onExpired` | `function` | Expiry callback |
| `onInitialize` | `function` | Init callback |

### Return value

| Property | Type | Description |
|----------|------|-------------|
| `openCheckout` | `(params) => void` | Opens the checkout modal |

### `openCheckout` params

Per-call parameters:

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| `email` | `string` | Yes | Customer email |
| `amount` | `number` | Yes | Amount in kobo |
| `reference` | `string` | No | Transaction reference |
| `firstName` | `string` | No | Customer first name |
| `lastName` | `string` | No | Customer last name |
| `metadata` | `object` | No | Custom metadata |

### Example

```tsx
function DonationPage() {
  const [email, setEmail] = useState('');
  const [amount, setAmount] = useState(0);

  const { openCheckout } = useZevPayCheckout({
    apiKey: 'pk_test_xxx',
    onSuccess: (reference) => {
      fetch('/api/verify', {
        method: 'POST',
        body: JSON.stringify({ reference }),
      });
    },
  });

  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      openCheckout({ email, amount: amount * 100 });
    }}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Your email"
      />
      <input
        type="number"
        value={amount}
        onChange={(e) => setAmount(Number(e.target.value))}
        placeholder="Amount in Naira"
      />
      <button type="submit">Donate</button>
    </form>
  );
}
```

## `<ZevPayProvider>`

Context provider for sharing configuration across components.

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `apiKey` | `string` | Yes | Shared public API key |
| `children` | `ReactNode` | Yes | Child components |

Both `<ZevPayButton>` and `useZevPayCheckout()` will inherit the API key from the nearest provider. A direct `apiKey` prop/config always takes priority over the provider.

## Payment methods

```tsx
// All methods (default)
<ZevPayButton paymentMethods="all" ... />

// Specific methods
<ZevPayButton paymentMethods={['bank', 'payid']} ... />

// Bank transfer only
<ZevPayButton paymentMethods={['bank']} ... />
```

| Method | Value | Status |
|--------|-------|--------|
| Bank Transfer | `"bank"` | Available |
| ZevPay ID | `"payid"` | Available |
| Card | `"card"` | Coming soon |

## Verify payments

::: warning Always verify server-side
Never trust the client-side `onSuccess` callback alone. Always verify the payment on your server.
:::

```typescript
// Server-side (Node.js)
import ZevPay from '@zevpay/node';

const zevpay = new ZevPay('sk_live_xxx');
const result = await zevpay.checkout.verify(sessionId);

if (result.status === 'completed') {
  // Payment confirmed — fulfill the order
}
```

## TypeScript

All types are exported:

```tsx
import type {
  ZevPayCheckoutOptions,
  ZevPayButtonProps,
  UseZevPayConfig,
  OpenCheckoutParams,
  UseZevPayReturn,
  PaymentMethodOption,
} from '@zevpay/react';
```

Error types from the inline SDK are also re-exported:

```tsx
import { CheckoutError } from '@zevpay/react';
```

## Resources

- [API Reference](/api/overview) — Full endpoint documentation
- [Inline Checkout SDK](/sdks/inline) — Underlying checkout SDK
- [Webhook Events](/webhooks/events) — All event types
- [npm](https://www.npmjs.com/package/@zevpay/react) — Package registry
