npm package: @ivorypay/node-sdk
Description: Official Node.js SDK for the IvoryPay Merchant API
Author: IvoryPay
Latest version: 1.0.0

# IvoryPay Node.js SDK

Official Node.js SDK for the [IvoryPay](https://ivorypay.io) Merchant API. Accept crypto and fiat payments, send payouts, query rates, and verify webhooks — all with full TypeScript support.

---

## Table of Contents

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Configuration](#configuration)
- [Authentication](#authentication)
- [Modules](#modules)
  - [Transactions (Accept Payments)](#transactions-accept-payments)
  - [Business Transactions](#business-transactions)
  - [Crypto Transfer](#crypto-transfer)
  - [Fiat Transfer](#fiat-transfer)
  - [Rates](#rates)
  - [Blockchain Accounts](#blockchain-accounts)
  - [Tokens](#tokens)
  - [Webhooks](#webhooks)
- [Error Handling](#error-handling)
- [TypeScript Support](#typescript-support)
- [Supported Values](#supported-values)
- [Environments](#environments)

---

## Installation

```bash
npm install @ivorypay/node-sdk
# or
yarn add @ivorypay/node-sdk
```

---

## Quick Start

```typescript
import { IvoryPay } from '@ivorypay/node-sdk';

const ivorypay = new IvoryPay({
  secretKey: process.env.IVORYPAY_SECRET_KEY,
});

// Accept a crypto payment
const { data } = await ivorypay.transactions.create({
  amount: 5000,
  baseFiat: 'NGN',
  token: 'USDT',
  channel: 'CRYPTO',
  network: 'BSC_MAINNET',
  reference: 'order_abc123',
  email: 'customer@example.com',
});

console.log(data.collectionDetails); // { network, address }
console.log(data.amountInCrypto);    // amount customer should send
console.log(data.expiresAt);         // session expiry
```

---

## Configuration

```typescript
const ivorypay = new IvoryPay({
  secretKey: 'sk_test_xxxxxxxxxxxx', // required
  baseUrl: 'https://api.ivorypay.io/api', // optional — override for local testing
});
```

| Option      | Type     | Required | Description                                                     |
| ----------- | -------- | -------- | --------------------------------------------------------------- |
| `secretKey` | `string` | Yes      | Your secret API key from the IvoryPay dashboard                 |
| `baseUrl`   | `string` | No       | Override the API base URL. Defaults to `https://api.ivorypay.io/api` |

---

## Authentication

IvoryPay uses secret API keys for authentication. Your key is sent directly in the `Authorization` header — **no Bearer prefix**.

```
Authorization: sk_test_xxxxxxxxxxxx
```

- Keys prefixed with `sk_test_` point to the **TEST** (sandbox) environment
- Keys prefixed with `sk_live_` point to the **LIVE** (production) environment

> **Security:** Never expose your secret key in frontend code, version control, or logs. Always load it from environment variables.

---

## Modules

### Transactions (Accept Payments)

Create payment requests and let customers pay with crypto or fiat.

#### `ivorypay.transactions.create(input)`

```typescript
// CRYPTO payment
const { data } = await ivorypay.transactions.create({
  amount: 5000,          // amount in baseFiat
  baseFiat: 'NGN',       // NGN or USD
  token: 'USDT',         // crypto token to accept
  channel: 'CRYPTO',
  network: 'BSC_MAINNET',
  reference: 'order_001', // must be unique per transaction
  email: 'customer@example.com',
});

// data.collectionDetails => { network, address }
// Share this address with your customer so they can pay

// FIAT payment
const { data } = await ivorypay.transactions.create({
  amount: 5000,
  baseFiat: 'NGN',
  token: 'USDT',
  channel: 'FIAT',
  fiatCurrency: 'NGN',   // currency the customer pays in
  reference: 'order_002',
  email: 'customer@example.com',
});

// data.collectionDetails => { accountName, accountNumber, bankName }
// Show these bank details to your customer for payment
```

**Input fields:**

| Field          | Type     | Required         | Description                                    |
| -------------- | -------- | ---------------- | ---------------------------------------------- |
| `amount`       | `number` | Yes              | Amount in base fiat                            |
| `baseFiat`     | `string` | Yes              | `NGN` or `USD`                                 |
| `token`        | `string` | Yes              | `USDT` or `USDC`                               |
| `channel`      | `string` | Yes              | `CRYPTO` or `FIAT`                             |
| `reference`    | `string` | Yes              | Unique transaction reference                   |
| `email`        | `string` | Yes              | Customer email                                 |
| `network`      | `string` | CRYPTO only      | Blockchain network                             |
| `fiatCurrency` | `string` | FIAT only        | Fiat currency for the payment                  |

---

#### `ivorypay.transactions.verify(reference)`

Verify the status of any transaction. Useful for polling before webhooks arrive.

```typescript
const { data } = await ivorypay.transactions.verify('order_001');

console.log(data.status); // PENDING | PROCESSING | CONFIRMING | SUCCESS | FAILED | EXPIRED | MISMATCH
```

---

### Business Transactions

Verify and manage transactions using your secret key for additional settlement details.

#### `ivorypay.businessTransactions.verify(reference)`

```typescript
const { data } = await ivorypay.businessTransactions.verify('order_001');

console.log(data.settledAmountInCrypto);
console.log(data.receivedAmountInCrypto);
console.log(data.completedAt);
```

#### `ivorypay.businessTransactions.refresh(reference, fiatCurrency?)`

Refresh an expired CRYPTO payment session, extending validity by 10 minutes.

```typescript
const { data } = await ivorypay.businessTransactions.refresh('order_001', 'NGN');
```

---

### Crypto Transfer

Send crypto directly to a wallet address.

#### `ivorypay.cryptoTransfer.initiate(input)`

```typescript
const { data } = await ivorypay.cryptoTransfer.initiate({
  amount: 10,             // amount in crypto
  token: 'USDT',
  network: 'BSC_MAINNET',
  address: '0xRecipientWalletAddress',
  reference: 'payout_001', // must be unique
});
```

**Input fields:**

| Field       | Type     | Required | Description                   |
| ----------- | -------- | -------- | ----------------------------- |
| `amount`    | `number` | Yes      | Amount in crypto              |
| `token`     | `string` | Yes      | Token to send                 |
| `network`   | `string` | Yes      | Blockchain network            |
| `address`   | `string` | Yes      | Recipient wallet address      |
| `reference` | `string` | Yes      | Unique payout reference       |

---

#### `ivorypay.cryptoTransfer.getNetworks(token)`

```typescript
const { data } = await ivorypay.cryptoTransfer.getNetworks('USDT');
// data => ['BSC_MAINNET', 'POLYGON', ...]
```

#### `ivorypay.cryptoTransfer.getSupportedTokens()`

```typescript
const { data } = await ivorypay.cryptoTransfer.getSupportedTokens();
```

---

### Fiat Transfer

Send fiat to a bank account or mobile money wallet.

#### `ivorypay.fiatTransfer.resolveAccount(input)`

Always resolve the account first to verify the recipient before sending funds.

```typescript
const { data } = await ivorypay.fiatTransfer.resolveAccount({
  accountNumber: '0123456789',
  bankCode: '058',
  currency: 'NGN',
});

console.log(data.accountName); // verify this with your customer
```

#### `ivorypay.fiatTransfer.initiate(input)`

```typescript
const { data } = await ivorypay.fiatTransfer.initiate({
  amount: 50,             // amount in crypto (debited from wallet)
  token: 'USDT',
  fiatCurrency: 'NGN',   // currency recipient receives
  payoutMethod: 'BANK_TRANSFER',
  accountNumber: '0123456789',
  bankCode: '058',
  reference: 'payout_002',
});
```

**Input fields:**

| Field           | Type     | Required | Description                              |
| --------------- | -------- | -------- | ---------------------------------------- |
| `amount`        | `number` | Yes      | Amount in crypto to debit                |
| `token`         | `string` | Yes      | `USDT` or `USDC`                         |
| `fiatCurrency`  | `string` | Yes      | Fiat currency the recipient receives     |
| `payoutMethod`  | `string` | Yes      | `BANK_TRANSFER` or `MOBILE_MONEY`        |
| `accountNumber` | `string` | Yes      | Recipient account number                 |
| `bankCode`      | `string` | Yes      | Bank code from `getBanks()`              |
| `reference`     | `string` | Yes      | Unique payout reference                  |

---

#### `ivorypay.fiatTransfer.getBanks()`

```typescript
const { data } = await ivorypay.fiatTransfer.getBanks();
// data => [{ id, name, code, country, currency }, ...]
```

---

### Rates

Fetch conversion rates and fee estimates before initiating a transfer. Use these to show your users the exact amounts they will send and receive.

#### `ivorypay.rates.fiatTransfer(input)`

```typescript
const { data } = await ivorypay.rates.fiatTransfer({
  amount: 100,
  token: 'USDT',
  fiatCurrency: 'NGN',
});

console.log(data.rate);               // current exchange rate
console.log(data.platformFeeInFiat);  // IvoryPay fee in fiat
console.log(data.amountInFiatLessFee); // recipient receives this
```

#### `ivorypay.rates.cryptoTransfer(input)`

```typescript
const { data } = await ivorypay.rates.cryptoTransfer({
  amount: 10,
  token: 'USDT',
  network: 'BSC_MAINNET',
});

console.log(data.gasFeeInCrypto);
console.log(data.platformFeeInCrypto);
```

#### `ivorypay.rates.fiatCollection(input)`

```typescript
const { data } = await ivorypay.rates.fiatCollection({
  amount: 5000,
  token: 'USDT',
  fiatCurrency: 'NGN',
});
```

#### `ivorypay.rates.cryptoCollection(input)`

```typescript
const { data } = await ivorypay.rates.cryptoCollection({
  amount: 5000,
  token: 'USDT',
  fiatCurrency: 'NGN',
});
```

---

### Blockchain Accounts

Create permanent, dedicated blockchain deposit addresses for your customers. Any deposit to these addresses automatically credits your business wallet.

#### `ivorypay.blockchainAccounts.create(input)`

```typescript
const { data } = await ivorypay.blockchainAccounts.create({
  firstName: 'Jane',
  lastName: 'Doe',
  email: 'jane@example.com',
  blockchain: 'BSC_MAINNET',  // BSC_TESTNET for TEST keys
  token: 'USDT',
  phoneNumber: '+2348012345678', // optional
});

console.log(data.address);      // permanent deposit address
console.log(data.customerId);   // your reference to this customer
```

> **Idempotent:** Calling this with the same email + blockchain + token returns the existing address rather than creating a new one.

**Input fields:**

| Field         | Type     | Required | Description                           |
| ------------- | -------- | -------- | ------------------------------------- |
| `firstName`   | `string` | Yes      | Customer first name                   |
| `lastName`    | `string` | Yes      | Customer last name                    |
| `email`       | `string` | Yes      | Customer email (unique identifier)    |
| `blockchain`  | `string` | Yes      | `BSC_MAINNET` or `POLYGON`            |
| `token`       | `string` | Yes      | `USDT` or `USDC`                      |
| `phoneNumber` | `string` | No       | Customer phone number                 |

---

### Tokens

#### `ivorypay.tokens.getSupportedTokens()`

Get all tokens and networks currently enabled for your business.

```typescript
const { data } = await ivorypay.tokens.getSupportedTokens();
```

#### `ivorypay.tokens.getNetworks(token)`

```typescript
const { data } = await ivorypay.tokens.getNetworks('USDT');
```

---

### Webhooks

IvoryPay sends webhook notifications to your registered URL when transaction events occur.

#### `ivorypay.webhooks.verifySignature(rawBody, signature, secret)`

Always verify the webhook signature before processing any event.

> **Important:** Use the raw request body (before any JSON parsing) for verification. With Express, use `express.raw()` on your webhook route.

```typescript
import express from 'express';
import { IvoryPay, WebhookPayload } from '@ivorypay/node-sdk';

const ivorypay = new IvoryPay({ secretKey: process.env.IVORYPAY_SECRET_KEY });

app.post(
  '/webhooks/ivorypay',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const rawBody = req.body.toString();
    const signature = req.headers['x-ivorypay-signature'] as string;

    const isValid = ivorypay.webhooks.verifySignature(
      rawBody,
      signature,
      process.env.IVORYPAY_WEBHOOK_SECRET,
    );

    if (!isValid) {
      return res.status(401).json({ message: 'Invalid signature' });
    }

    const payload = ivorypay.webhooks.parse(rawBody);

    switch (payload.event) {
      case 'cryptoCollection.success':
        // fulfill the order
        await fulfillOrder(payload.data.reference);
        break;
      case 'fiatCollection.success':
        await fulfillOrder(payload.data.reference);
        break;
      case 'cryptoPayout.success':
        // mark payout as completed
        break;
      case 'fiatPayout.failed':
        // handle failed payout
        break;
    }

    res.sendStatus(200); // always return 200 to acknowledge receipt
  },
);
```

**Supported webhook events:**

| Event                           | Description                               |
| ------------------------------- | ----------------------------------------- |
| `cryptoCollection.success`      | Crypto payment received successfully      |
| `cryptoCollection.failed`       | Crypto payment failed                     |
| `cryptoCollection.mismatch`     | Wrong amount received                     |
| `fiatCollection.success`        | Fiat payment received successfully        |
| `fiatCollection.failed`         | Fiat payment failed                       |
| `cryptoPayout.success`          | Crypto transfer completed                 |
| `cryptoPayout.failed`           | Crypto transfer failed                    |
| `fiatPayout.success`            | Fiat transfer completed                   |
| `fiatPayout.failed`             | Fiat transfer failed                      |
| `cryptoSettlement.success`      | Crypto settlement completed               |
| `fiatSettlement.success`        | Fiat settlement completed                 |
| `swap.success`                  | Swap completed                            |
| `swap.failed`                   | Swap failed                               |
| `cryptoCollectionRefund.success`| Crypto collection refund sent             |
| `fiatCollectionRefund.success`  | Fiat collection refund sent               |
| `permanentWalletDeposit.success`| Deposit to blockchain address received    |

---

## Error Handling

All SDK methods throw an `IvoryPayError` on failure. Always wrap your calls in try/catch.

```typescript
import { IvoryPay, IvoryPayError } from '@ivorypay/node-sdk';

try {
  const { data } = await ivorypay.transactions.create({ ... });
} catch (error) {
  if (error instanceof IvoryPayError) {
    console.error(error.message);     // human-readable message
    console.error(error.statusCode);  // HTTP status code
    console.error(error.raw);         // full raw API response
  }
}
```

**Common error status codes:**

| Status | Meaning                                             |
| ------ | --------------------------------------------------- |
| `400`  | Bad request — check your input fields               |
| `401`  | Unauthorized — invalid or missing API key           |
| `404`  | Resource not found                                  |
| `422`  | Unprocessable — e.g. duplicate reference            |
| `500`  | Server error — retry with exponential backoff       |

---

## TypeScript Support

The SDK is written entirely in TypeScript and ships with full type declarations. All inputs and responses are typed.

```typescript
import type {
  CreateTransactionInput,
  CreateTransactionResponse,
  InitiateFiatTransferInput,
  WebhookPayload,
  WebhookEvent,
  SupportedToken,
  SupportedNetwork,
  SupportedFiatCurrency,
  TransactionStatus,
  IvoryPayResponse,
} from '@ivorypay/node-sdk';
```

---

## Supported Values

### Tokens
| Value  | Description           |
| ------ | --------------------- |
| `USDT` | Tether USD            |
| `USDC` | USD Coin              |
| `BTC`  | Bitcoin               |
| `ETH`  | Ethereum              |
| `SOL`  | Solana                |

### Networks
| Value         | Description             |
| ------------- | ----------------------- |
| `BSC_MAINNET` | BNB Smart Chain         |
| `BSC_TESTNET` | BNB Smart Chain Testnet |
| `POLYGON`     | Polygon (MATIC)         |
| `ETH`         | Ethereum                |
| `TRON`        | Tron                    |
| `SOLANA`      | Solana                  |
| `BITCOIN`     | Bitcoin                 |

### Fiat Currencies
| Value | Description     |
| ----- | --------------- |
| `NGN` | Nigerian Naira  |
| `KES` | Kenyan Shilling |
| `GHS` | Ghanaian Cedi   |
| `ZAR` | South African Rand |
| `USD` | US Dollar       |

### Transaction Statuses
| Value        | Description                                   |
| ------------ | --------------------------------------------- |
| `PENDING`    | Awaiting payment from customer                |
| `PROCESSING` | Payment detected, being confirmed             |
| `CONFIRMING` | Awaiting blockchain confirmations             |
| `SUCCESS`    | Payment confirmed and settled                 |
| `FAILED`     | Payment failed                                |
| `EXPIRED`    | Session expired before payment was received   |
| `MISMATCH`   | Wrong amount was sent by the customer         |

---

## Environments

| Environment | Key Prefix  | Description                            |
| ----------- | ----------- | -------------------------------------- |
| TEST        | `sk_test_`  | Sandbox — no real funds move           |
| LIVE        | `sk_live_`  | Production — real transactions         |

Always build and test with `sk_test_` keys. Switch to `sk_live_` only when you are ready to go live.

---

## License

MIT