Node SDK
@dottspay/node is the official library for Node.js. It has no dependencies, uses the built-in fetch, ships full TypeScript types, and verifies webhook signatures for you.Install
npm install @dottspay/nodeRequires Node 18 or later. The package is ESM.
Create a client
import { DottsPay } from '@dottspay/node'
export const dottspay = new DottsPay(process.env.DOTTSPAY_SECRET_KEY)
console.log(dottspay.mode) // 'test' or 'live', from the keyThe constructor takes your secret key and an optional options object:
| Option | Default | What it does |
|---|---|---|
baseUrl | https://dottspay.hiddekellabs.com | Root URL of the API server. /api/v1 is added for you. |
timeout | 30000 | Request timeout in milliseconds. |
fetch | globalThis.fetch | A custom fetch, useful in tests. |
baseUrl- Default
https://dottspay.hiddekellabs.com - What it doesRoot URL of the API server.
/api/v1is added for you. timeout- Default
30000 - What it doesRequest timeout in milliseconds.
fetch- Default
globalThis.fetch - What it doesA custom fetch, useful in tests.
Passing a public key (pk_…) or anything that is not a secret key throws straight away, so a misconfigured environment fails loudly.
Methods
Every method returns a promise. Single objects resolve to the object itself, without the { status, message, data } envelope. List methods resolve to { data, meta: { page, perPage, total } }.
| Method | Endpoint | Returns |
|---|---|---|
checkout.create(params, { idempotencyKey }) | POST /checkout/sessions | CheckoutSession |
checkout.retrieve(id) | GET /checkout/sessions/{id} | CheckoutSession |
transactions.list(params) | GET /transactions | Page<Transaction> |
transactions.verify(reference) | GET /transactions/{reference} | Transaction |
refunds.create(params, { idempotencyKey }) | POST /refunds | Refund |
paymentLinks.list(params) | GET /payment-links | Page<PaymentLink> |
paymentLinks.create(params, { idempotencyKey }) | POST /payment-links | PaymentLink |
paymentLinks.retrieve(id) | GET /payment-links/{id} | PaymentLink |
paymentLinks.update(id, params) | PATCH /payment-links/{id} | PaymentLink |
customers.list(params) | GET /customers | Page<Customer> |
banks.list(currency) | GET /banks | Bank[] |
banks.resolve(accountNumber, bankCode) | GET /banks/resolve | { account_name } |
balance.retrieve() | GET /balance | Balance[] |
checkout.create(params, { idempotencyKey })- Endpoint
POST /checkout/sessions - Returns
CheckoutSession checkout.retrieve(id)- Endpoint
GET /checkout/sessions/{id} - Returns
CheckoutSession transactions.list(params)- Endpoint
GET /transactions - Returns
Page<Transaction> transactions.verify(reference)- Endpoint
GET /transactions/{reference} - Returns
Transaction refunds.create(params, { idempotencyKey })- Endpoint
POST /refunds - Returns
Refund paymentLinks.list(params)- Endpoint
GET /payment-links - Returns
Page<PaymentLink> paymentLinks.create(params, { idempotencyKey })- Endpoint
POST /payment-links - Returns
PaymentLink paymentLinks.retrieve(id)- Endpoint
GET /payment-links/{id} - Returns
PaymentLink paymentLinks.update(id, params)- Endpoint
PATCH /payment-links/{id} - Returns
PaymentLink customers.list(params)- Endpoint
GET /customers - Returns
Page<Customer> banks.list(currency)- Endpoint
GET /banks - Returns
Bank[] banks.resolve(accountNumber, bankCode)- Endpoint
GET /banks/resolve - Returns
{ account_name } balance.retrieve()- Endpoint
GET /balance - Returns
Balance[]
Parameter names match the API exactly, so the API reference doubles as SDK reference.
Take a payment
const session = await dottspay.checkout.create(
{
amount: 500000, // ₦5,000 in kobo
email: 'ada@example.com',
callback_url: 'https://yourshop.com/payment/callback',
metadata: { order_id: 'ord_1042' },
},
{ idempotencyKey: 'order-1042' },
)
// Later, when the customer returns with ?reference=dp_...
const tx = await dottspay.transactions.verify(reference)
if (tx.status === 'success' && tx.amount === 500000) {
// give value
}Refund part of a payment
const refund = await dottspay.refunds.create(
{ reference: 'dp_Hk2pQx9LmT4sWb7NcY', amount: 200000, reason: 'One item returned' },
{ idempotencyKey: 'refund-ord_1042-1' },
)
console.log(refund.status) // 'pending'Create and pause a payment link
const link = await dottspay.paymentLinks.create({
title: 'Small chops tray',
amount: 2500000,
stock_limit: 50,
})
console.log(link.url)
await dottspay.paymentLinks.update(link.id, { active: false })Page through transactions
let page = 1
while (true) {
const { data, meta } = await dottspay.transactions.list({
status: 'success',
from: new Date('2026-10-01'), // Date objects or ISO strings
perPage: 100,
page,
})
for (const tx of data) console.log(tx.reference, tx.net)
if (page * meta.perPage >= meta.total) break
page++
}Confirm a bank account
const banks = await dottspay.banks.list('NGN')
const { account_name } = await dottspay.banks.resolve('0123456789', '058')Errors
Any failed call throws a DottsPayError with the HTTP status, the API's code, and a readable message. See the error codes.
import { DottsPayError } from '@dottspay/node'
try {
await dottspay.refunds.create({ reference: 'dp_Hk2pQx9LmT4sWb7NcY' })
} catch (err) {
if (err instanceof DottsPayError) {
console.log(err.status) // 422
console.log(err.code) // 'validation_error'
console.log(err.message)
} else {
throw err
}
}When a request never gets a response, status is 0 and code is network_error or timeout. The SDK does not retry on its own. If you retry a create call, pass the same idempotencyKey so it cannot happen twice.
Webhooks
The webhook helpers are static, so you can use them without a client. Pass the raw body as a string or Buffer.
DottsPay.webhooks.verify(rawBody, signature, secret)returnstrueorfalse.DottsPay.webhooks.constructEvent(rawBody, signature, secret)verifies, then returns the parsed, typed event. It throws aDottsPayErrorwith codeinvalid_signatureif the signature does not match.
app.post('/webhooks/dottspay', express.raw({ type: 'application/json' }), (req, res) => {
let event
try {
event = DottsPay.webhooks.constructEvent(
req.body,
req.get('X-DottsPay-Signature'),
process.env.DOTTSPAY_WEBHOOK_SECRET,
)
} catch {
return res.sendStatus(400)
}
res.sendStatus(200)
// event.event is 'charge.success' | 'charge.failed' | 'refund.created' | 'payout.paid' | 'payout.failed'
})More detail, including PHP, is in the webhooks guide.
TypeScript
Every object, parameter and event is typed. Import the types you need:
import type { Transaction, CheckoutSession, Refund, PaymentLink, WebhookEvent } from '@dottspay/node'
function onEvent(event: WebhookEvent) {
if (event.event === 'charge.success') {
const tx: Transaction = event.data // narrowed by the event name
}
}