# Node.js SDK

Official server-side SDK for integrating ZevPay Checkout in Node.js applications. Provides full REST API coverage with TypeScript support.

## Installation

```bash
npm install @zevpay/node
```

Requires Node.js 18 or later.

## Quick start

```typescript
import ZevPay from '@zevpay/node';

const zevpay = new ZevPay('sk_test_your_secret_key');

// Initialize a checkout session
const session = await zevpay.checkout.initialize({
  amount: 500000, // ₦5,000 in kobo
  email: 'customer@example.com',
  reference: 'ORDER-123',
  callbackUrl: 'https://yoursite.com/callback',
});

console.log(session.checkoutUrl);
// → https://secure.zevpaycheckout.com/ses_abc123...
```

## Configuration

```typescript
const zevpay = new ZevPay('sk_live_xxx', {
  baseUrl: 'https://api.zevpaycheckout.com', // default
  timeout: 30000,  // 30s request timeout (default)
  maxRetries: 2,   // retries on 5xx errors (default)
});
```

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `baseUrl` | string | `https://api.zevpaycheckout.com` | API base URL |
| `timeout` | number | `30000` | Request timeout in ms |
| `maxRetries` | number | `2` | Max retries on server errors |

::: warning Secret keys only
The SDK only accepts secret keys (`sk_live_*` or `sk_test_*`). Public keys cannot be used server-side.
:::

## Checkout sessions

### Initialize a session

```typescript
const session = await zevpay.checkout.initialize({
  amount: 500000,
  email: 'customer@example.com',
  currency: 'NGN',
  reference: 'ORDER-123',
  callbackUrl: 'https://yoursite.com/callback',
  metadata: { orderId: '123' },
  paymentMethods: ['bank_transfer', 'payid'],
});

// Redirect your customer to:
console.log(session.checkoutUrl);
```

### Verify a payment

```typescript
const result = await zevpay.checkout.verify(session.sessionId);

if (result.status === 'completed') {
  console.log('Payment confirmed at:', result.paidAt);
}
```

### Get session details

```typescript
const details = await zevpay.checkout.get('ses_abc123');
```

## Transfers

### Bank transfer

```typescript
const transfer = await zevpay.transfers.create({
  type: 'bank_transfer',
  amount: 1000000, // ₦10,000
  accountNumber: '0123456789',
  bankCode: '044',
  accountName: 'John Doe',
  narration: 'Payout',
  reference: 'TXN-123',
});
```

### PayID transfer

```typescript
const transfer = await zevpay.transfers.create({
  type: 'payid',
  amount: 500000,
  payId: 'johndoe',
  narration: 'Payment',
});
```

### List transfers

```typescript
const transfers = await zevpay.transfers.list({
  page: 1,
  pageSize: 20,
  status: 'completed',
  type: 'bank_transfer',
});
```

### Verify a transfer

```typescript
const result = await zevpay.transfers.verify('TXN-123');
```

### List banks

```typescript
const banks = await zevpay.transfers.listBanks();
// [{ code: '044', name: 'Access Bank', slug: 'access-bank' }, ...]
```

### Resolve bank account

```typescript
const account = await zevpay.transfers.resolveAccount({
  accountNumber: '0123456789',
  bankCode: '044',
});

console.log(account.accountName); // 'JOHN DOE'
```

### Calculate fees

```typescript
const charges = await zevpay.transfers.calculateCharges({
  amount: 1000000,
  bankCode: '044',
});

console.log(charges.fee);   // Fee in kobo
console.log(charges.total); // Amount + all charges
```

### Get wallet balance

```typescript
const balance = await zevpay.transfers.getBalance();
console.log(balance.availableBalance); // Balance in kobo
```

## Invoices

### Create an invoice

```typescript
const invoice = await zevpay.invoices.create({
  customerName: 'Jane Doe',
  customerEmail: 'jane@example.com',
  dueDate: '2026-04-01',
  lineItems: [
    { description: 'Web Design', quantity: 1, unitPrice: 5000000 },
    { description: 'Hosting (1yr)', quantity: 1, unitPrice: 1200000 },
  ],
  taxRate: 7.5,
  note: 'Thank you for your business',
});

// Send to customer:
console.log(invoice.paymentUrl);
```

### Send, list, cancel

```typescript
// Send invoice (draft → sent)
await zevpay.invoices.send(invoice.publicId);

// List invoices
const invoices = await zevpay.invoices.list({ status: 'sent' });

// Cancel invoice
await zevpay.invoices.cancel(invoice.publicId);
```

## Static PayIDs

```typescript
// Create
const payid = await zevpay.staticPayId.create({
  payId: 'mystore',
  suffix: '.spid',
  name: 'My Store',
  description: 'Accept payments to my store',
});

// List
const payids = await zevpay.staticPayId.list();

// Update
await zevpay.staticPayId.update(payid.id, { name: 'New Name' });

// Deactivate / Reactivate
await zevpay.staticPayId.deactivate(payid.id);
await zevpay.staticPayId.reactivate(payid.id);
```

## Dynamic PayIDs

```typescript
// Create a time-limited payment link
const dpayid = await zevpay.dynamicPayId.create({
  amount: 1000000,
  name: 'Donation Drive',
  expiresInMinutes: 60,
  amountValidation: 'A0',
});

console.log(dpayid.fullPayId); // e.g. 'donation-drive-xyz.dpay'

// List active
const active = await zevpay.dynamicPayId.list({ status: 'active' });

// Deactivate
await zevpay.dynamicPayId.deactivate(dpayid.id);
```

## Virtual accounts

```typescript
// Create
const va = await zevpay.virtualAccounts.create({
  amount: 1000000,
  validityMinutes: 60,
});

console.log(va.accountNumber); // Customer pays to this account

// List
const accounts = await zevpay.virtualAccounts.list({ status: 'pending' });
```

## Wallet

```typescript
// Get wallet details
const wallet = await zevpay.wallet.get();

// List members
const members = await zevpay.wallet.listMembers();

// Add / remove members
await zevpay.wallet.addMember({ payId: 'johndoe' });
await zevpay.wallet.removeMember('johndoe');
```

## Webhook verification

Verify incoming webhook signatures using your webhook secret:

::: code-group

```typescript [Express]
import ZevPay from '@zevpay/node';

app.post('/webhooks/zevpay', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-zevpay-signature'] as string;

  try {
    const event = ZevPay.webhooks.constructEvent(
      req.body,
      signature,
      process.env.ZEVPAY_WEBHOOK_SECRET!,
    );

    switch (event.event) {
      case 'charge.success':
        // Handle successful checkout payment
        break;
      case 'transfer.success':
        // Handle successful transfer
        break;
      case 'transfer.failed':
        // Handle failed transfer
        break;
      case 'invoice.paid':
        // Handle paid invoice
        break;
    }

    res.sendStatus(200);
  } catch (err) {
    res.status(400).send('Invalid signature');
  }
});
```

```typescript [Manual verification]
const isValid = ZevPay.webhooks.verify(
  rawBody,
  signature,
  webhookSecret,
);
```

:::

::: warning Always verify
Never process webhook events without verifying the signature. See [Verifying Signatures](/webhooks/signatures) for details.
:::

## Error handling

All API errors throw typed exceptions:

```typescript
import ZevPay, {
  ZevPayError,
  ZevPayValidationError,
  ZevPayAuthenticationError,
  ZevPayNotFoundError,
  ZevPayRateLimitError,
  ZevPayConflictError,
} from '@zevpay/node';

try {
  await zevpay.transfers.create({ /* ... */ });
} catch (err) {
  if (err instanceof ZevPayValidationError) {
    // 400 — invalid parameters
    console.log(err.code);    // e.g. 'VALIDATION_ERROR'
    console.log(err.message); // Human-readable message
    console.log(err.details); // Field-level errors
  } else if (err instanceof ZevPayAuthenticationError) {
    // 401 — invalid API key
  } else if (err instanceof ZevPayNotFoundError) {
    // 404 — resource not found
  } else if (err instanceof ZevPayConflictError) {
    // 409 — duplicate transaction
  } else if (err instanceof ZevPayRateLimitError) {
    // 429 — rate limit exceeded
    console.log(err.retryAfter); // seconds to wait
  }
}
```

| Error class | Status | When |
|-------------|--------|------|
| `ZevPayValidationError` | 400 | Invalid request parameters |
| `ZevPayAuthenticationError` | 401 | Invalid or missing API key |
| `ZevPayPermissionError` | 403 | Insufficient permissions |
| `ZevPayNotFoundError` | 404 | Resource not found |
| `ZevPayConflictError` | 409 | Duplicate resource |
| `ZevPayRateLimitError` | 429 | Too many requests |
| `ZevPayApiError` | 500+ | Server error |

## Full example — Express.js

```typescript
import express from 'express';
import ZevPay from '@zevpay/node';

const app = express();
const zevpay = new ZevPay(process.env.ZEVPAY_SECRET_KEY!);

// Initialize payment
app.post('/api/pay', express.json(), async (req, res) => {
  const session = await zevpay.checkout.initialize({
    amount: req.body.amount,
    email: req.body.email,
    reference: `ORDER-${Date.now()}`,
    callbackUrl: `${req.protocol}://${req.get('host')}/api/callback`,
  });

  res.json({ checkoutUrl: session.checkoutUrl });
});

// Verify after callback
app.get('/api/callback', async (req, res) => {
  const sessionId = req.query.session_id as string;
  const result = await zevpay.checkout.verify(sessionId);

  if (result.status === 'completed') {
    res.send('Payment successful!');
  } else {
    res.send('Payment not completed yet.');
  }
});

// Receive webhooks
app.post('/webhooks/zevpay', express.raw({ type: 'application/json' }), (req, res) => {
  try {
    const event = ZevPay.webhooks.constructEvent(
      req.body,
      req.headers['x-zevpay-signature'] as string,
      process.env.ZEVPAY_WEBHOOK_SECRET!,
    );

    console.log('Webhook received:', event.event, event.data);
    res.sendStatus(200);
  } catch {
    res.status(400).send('Invalid signature');
  }
});

app.listen(3000);
```

## Resources

- [API Reference](/api/overview) — Full endpoint documentation
- [Webhook Events](/webhooks/events) — All event types
- [GitHub](https://github.com/zevpay/zevpay-node) — Source code
- [npm](https://www.npmjs.com/package/@zevpay/node) — Package registry
