Getting Started
Install Bora Pesa and process your first payment in under 5 minutes.
Installation
pnpm add @borapesa/pesaThe core package includes a BogusProvider for local development — no credentials, no network, no sandbox registration. When you're ready for production, install a provider adapter:
pnpm add @borapesa/clickpesaYour first payment
import { createPesa } from '@borapesa/pesa';
import { BogusPaymentProvider } from '@borapesa/pesa/testing';
// 1. Create an instance — that's it, no config needed for dev
const pesa = createPesa({
provider: new BogusPaymentProvider({ defaultBehavior: 'success' }),
});
// 2. Initiate a payment
const order = await pesa.createOrder({
amount: 15000, // TZS 15,000 — whole integers only
currency: 'TZS',
reference: 'order_abc123', // Your internal order ID — must be unique
customer: {
name: 'Juma Ali',
phone: '255712345678', // MSISDN format: 255XXXXXXXXX
},
});
console.log(order.status); // 'SUCCESS'
console.log(order.orderId); // Provider-assigned transaction IDReact to events
Bora Pesa emits typed events after every verified webhook:
pesa.on('PAYMENT_SUCCESS', async (event) => {
// event.reference — your order ID
// event.amount — TZS amount
// event.provider — which provider processed it
await db.orders.update({
where: { id: event.reference },
data: { status: 'paid' },
});
});Going to production
Swap the BogusProvider for a real provider and add a persistent event store:
pnpm add @borapesa/clickpesa @borapesa/sqliteimport { retryPlugin, idempotencyPlugin, loggingPlugin } from '@borapesa/pesa/plugins';
import { ClickPesaProvider } from '@borapesa/clickpesa';
import { SQLiteAdapter } from '@borapesa/sqlite';
const pesa = createPesa({
provider: new ClickPesaProvider({
clientId: process.env.CLICKPESA_CLIENT_ID!,
apiKey: process.env.CLICKPESA_API_KEY!,
checksumKey: process.env.CLICKPESA_CHECKSUM_KEY, // enables webhook verification
}),
db: new SQLiteAdapter('./pesa.db'),
plugins: [
idempotencyPlugin(),
retryPlugin({ maxAttempts: 3 }),
loggingPlugin({ level: 'info' }),
],
});Any class implementing PesaDatabaseAdapter works as an event store — use @borapesa/sqlite, or implement the four-method interface for your own database. See Adapters for details.
Mount on your server
Every pesa instance exposes a webhook handler. Mount it publicly — providers POST callbacks here:
Bun.serve({ fetch: pesa.mountWebhook });The handler serves one endpoint (customizable via basePath in config):
POST {basePath}/webhook— receive provider callbacks
For order creation and status queries, use pesa.createOrder() and pesa.getPaymentStatus() in your own routes behind your own auth middleware. basePath defaults to /pesa.
Next steps
- Architecture — understand the design
- Event System — webhooks, persistence, and event handlers
- Plugins — retry, idempotency, logging, and custom plugins
- Error Handling — the error hierarchy and when to retry
- API Reference — auto-generated from JSDoc