npm package: @pok-pay/react
Description: POK Pay official React SDK — card, wallet, and tokenized card payments
Latest version: 0.1.0

# @pok-pay/react

Official POK Pay React SDK — card, wallet, and tokenized card payment components for React and Next.js apps.

## Installation

```bash
npm install @pok-pay/react qrcode
```

Peer dependencies (React 18+):

```bash
npm install react react-dom
```

> **Next.js note:** `@nebula-ltd/pok-payments-js` is loaded via dynamic import inside the SDK and is **never imported statically**. No special `next.config.js` configuration is required.

---

## Environment Setup

| `env` value | POK App URL | Description |
|---|---|---|
| `staging` | `https://app-staging.pokpay.io` | Use for development and testing |
| `production` | `https://app.pokpay.io` | Use for live payments |

The `env` prop is passed to every component. It controls which POK endpoints are used.

---

## Security

> **NEVER expose `keyId` or `keySecret` in frontend code.** These are backend-only credentials used by `@pok-pay/server-sdk` to authenticate with POK's REST API. The React SDK deals only with `sdkOrderId` values that are created by your backend and sent to the frontend.

The typical integration flow is:
1. **Backend** creates a POK SDK order (using `@pok-pay/server-sdk`) and returns an `sdkOrderId` to the frontend.
2. **Frontend** passes `sdkOrderId` to the React components below.

---

## Quick Start — New Card Payment

```tsx
import { PokNewCardForm } from '@pok-pay/react';

export function CheckoutPage({ sdkOrderId }: { sdkOrderId: string }) {
  return (
    <PokNewCardForm
      sdkOrderId={sdkOrderId}
      env="staging"
      initialState={{ name: 'Jane Doe', email: 'jane@example.com' }}
      onSuccess={() => console.log('Card payment complete!')}
      onError={(err) => console.error('Payment error:', err)}
    />
  );
}
```

---

## Quick Start — Wallet / QR Payment

```tsx
import { PokWalletCheckout } from '@pok-pay/react';

export function WalletCheckoutPage({ sdkOrderId }: { sdkOrderId: string }) {
  return (
    <PokWalletCheckout
      sdkOrderId={sdkOrderId}
      env="staging"
      statusEndpoint="https://your-api.com/orders"
      onSuccess={() => console.log('Wallet payment confirmed!')}
      onError={(err) => console.error('Polling error:', err)}
      onTimeout={() => alert('Payment window expired. Please try again.')}
    />
  );
}
```

The component polls `GET https://your-api.com/orders/{sdkOrderId}` every 3 seconds. Your backend endpoint must return `{ status: 'PENDING' | 'COMPLETED' | 'FAILED' }`.

---

## Quick Start — Tokenized Card (Saved Card / 3DS)

```tsx
import { PokCardTokenCheckout } from '@pok-pay/react';

export function SavedCardCheckoutPage({
  sdkOrderId,
  cardId,
}: {
  sdkOrderId: string;
  cardId: string;
}) {
  return (
    <PokCardTokenCheckout
      sdkOrderId={sdkOrderId}
      cardId={cardId}
      env="staging"
      setup3dsEndpoint="https://your-api.com/payments/setup-tokenized-3ds"
      onSuccess={() => console.log('Saved card payment complete!')}
      onError={(err) => console.error('3DS error:', err)}
    />
  );
}
```

Your `setup3dsEndpoint` will receive:
```json
POST { "cardId": "...", "sdkOrderId": "..." }
```
And must return:
```json
{ "payerAuthentication": { ... } }
```
Implement this endpoint using `@pok-pay/server-sdk`'s `handleSetup3DS` helper.

---

## Quick Start — Unified Payment Sheet

```tsx
import { PokPaymentSheet } from '@pok-pay/react';

export function PaymentPage({
  sdkOrderId,
  savedCards,
}: {
  sdkOrderId: string;
  savedCards: Array<{ cardId: string; label: string }>;
}) {
  return (
    <PokPaymentSheet
      sdkOrderId={sdkOrderId}
      env="staging"
      statusEndpoint="https://your-api.com/orders"
      setup3dsEndpoint="https://your-api.com/payments/setup-tokenized-3ds"
      savedCards={savedCards}
      onSuccess={() => console.log('Payment complete!')}
      onError={(err) => console.error(err)}
      onTimeout={() => alert('QR code expired')}
    />
  );
}
```

---

## Component API Reference

### `PokNewCardForm`

| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| `sdkOrderId` | `string` | Yes | — | SDK order ID from your backend |
| `env` | `'staging' \| 'production'` | Yes | — | POK environment |
| `containerId` | `string` | No | auto-generated | DOM id for the POK form to mount into |
| `initialState` | `{ name?: string; email?: string }` | No | — | Pre-fill name/email in card form |
| `onSuccess` | `() => void` | Yes | — | Called when payment completes |
| `onError` | `(error: Error) => void` | Yes | — | Called on payment error |

### `PokWalletCheckout`

| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| `sdkOrderId` | `string` | Yes | — | SDK order ID from your backend |
| `env` | `'staging' \| 'production'` | Yes | — | POK environment |
| `statusEndpoint` | `string` | Yes | — | Backend URL polled as `GET {statusEndpoint}/{sdkOrderId}` |
| `qrSize` | `number` | No | `256` | QR image size in pixels |
| `pollInterval` | `number` | No | `3000` | ms between polls |
| `pollTimeout` | `number` | No | `300000` | ms total before `onTimeout` fires |
| `onSuccess` | `() => void` | Yes | — | Called when payment confirmed |
| `onError` | `(error: Error) => void` | Yes | — | Called on poll error |
| `onTimeout` | `() => void` | Yes | — | Called when timeout expires |

### `PokCardTokenCheckout`

| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| `sdkOrderId` | `string` | Yes | — | SDK order ID from your backend |
| `cardId` | `string` | Yes | — | POK card token UUID (from saved cards) |
| `env` | `'staging' \| 'production'` | Yes | — | POK environment |
| `setup3dsEndpoint` | `string` | Yes | — | Your backend endpoint for 3DS setup |
| `containerId` | `string` | No | auto-generated | DOM id for the 3DS frame |
| `onSuccess` | `() => void` | Yes | — | Called when payment completes |
| `onError` | `(error: Error) => void` | Yes | — | Called on error |

### `PokPaymentSheet`

| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| `sdkOrderId` | `string` | Yes | — | SDK order ID from your backend |
| `env` | `'staging' \| 'production'` | Yes | — | POK environment |
| `statusEndpoint` | `string` | Yes | — | Backend URL for wallet polling |
| `onSuccess` | `() => void` | Yes | — | Called on any successful payment |
| `onError` | `(error: Error) => void` | Yes | — | Called on any error |
| `onTimeout` | `() => void` | Yes | — | Called when wallet QR expires |
| `savedCards` | `Array<{ cardId: string; label: string; logo?: string }>` | No | `[]` | List of user's saved cards |
| `setup3dsEndpoint` | `string` | No* | — | Required when `savedCards` non-empty |
| `initialState` | `{ name?: string; email?: string }` | No | — | Pre-fill for new card form |

---

## `usePokWalletPoll` Hook

```tsx
import { usePokWalletPoll } from '@pok-pay/react';

const { status, error, stopPolling } = usePokWalletPoll(
  sdkOrderId,
  'https://your-api.com/orders',
  { interval: 3000, timeout: 300000 },
);
```

| Return | Type | Description |
|---|---|---|
| `status` | `'POLLING' \| 'COMPLETED' \| 'FAILED' \| 'TIMEOUT'` | Current poll state |
| `error` | `Error \| null` | Set when status is `FAILED` due to a network error |
| `stopPolling` | `() => void` | Immediately stop polling |

---

## React 19 Compatibility

`@nebula-ltd/pok-payments-js` was compiled for React 18 internals. The SDK automatically applies a compatibility shim before loading the POK JS library — no configuration is needed from the consumer.

If you need to apply the shim manually for any reason:

```typescript
import { applyReact19Shim } from '@pok-pay/react';
applyReact19Shim(); // idempotent — safe to call multiple times
```