Providers
Available payment providers — Selcom, ClickPesa, AzamPay, Snippe — and their capabilities.
Providers are the bridge between the SDK and payment gateways. Pick one that matches your business needs. Swap providers with a one-line config change — no application code changes needed.
Comparison
A ✅ means the feature is implemented in our provider adapter. A — does not guarantee the upstream provider API lacks the feature — it may simply not be wired up yet. Open an issue if you need it.
| Selcom | ClickPesa | AzamPay | Snippe | |
|---|---|---|---|---|
| MNO USSD push | ✅ | ✅ | ✅ | ✅ |
| MNO redirect checkout | ✅ | ✅ | — | — |
| Card payment | — | ✅ | — | ✅ |
| Bank checkout (OTP) | — | — | ✅ | — |
| Hosted payment page | — | ✅ | ✅ | ✅ |
| Disburse (wallet) | ✅ | ✅ | ✅ | ✅ |
| Disburse (bank) | ✅ | ✅ | — | ✅ |
| Webhook signature | HMAC-SHA256 | HMAC-SHA256 | RSA-PKCS#1 | HMAC-SHA256 |
| Order cancellation | ✅ | — | — | — |
| Order listing | ✅ | — | — | ✅ |
| Balance query | ✅ | — | — | ✅ |
| Name lookup | ✅ | ✅ | ✅ | — |
| Credential validation | ✅ | ✅ | ✅ | ✅ |
| Auth method | HMAC per-request | Bearer token | Bearer token | Bearer token |
| Payout fee preview | — | ✅ | — | — |
Configuration
Each provider has a different config surface — fields match what each provider's dashboard gives you. Every config accepts an optional baseUrl override (or equivalent) for sandbox/proxy use.
ClickPesa
import { ClickPesaProvider } from '@borapesa/clickpesa'| Field | Required | Description |
|---|---|---|
clientId | ✅ | Client ID from ClickPesa dashboard |
apiKey | ✅ | API key from ClickPesa dashboard |
checksumKey | HMAC-SHA256 signing key for webhook verification. Generate in the ClickPesa dashboard. When set, all POST/PUT/PATCH bodies are signed and incoming webhooks are verified. Without it, webhooks are accepted unverified. | |
sandbox | true → https://api-sandbox.clickpesa.com. false (default) → https://api.clickpesa.com. | |
baseUrl | Explicit base URL override — takes precedence over sandbox. | |
redirectUrl | Default redirect URL for hosted checkout. Overridable per-payment via payload.redirectUrl. |
const pesa = createPesa({
provider: new ClickPesaProvider({
clientId: process.env.CLICKPESA_CLIENT_ID!,
apiKey: process.env.CLICKPESA_API_KEY!,
sandbox: false,
redirectUrl: 'https://mysite.com/success',
}),
})Selcom
import { SelcomPaymentProvider } from '@borapesa/selcom'| Field | Required | Description |
|---|---|---|
apiKey | ✅ | API key from Selcom |
apiSecret | ✅ | API secret for HMAC-SHA256 signing |
vendor | ✅ | Float account / vendor identifier |
pin | ✅ | Float account PIN — required for disbursement and balance queries |
baseUrl | Defaults to https://apigw.selcommobile.com. Set to the sandbox URL for testing. | |
senderAccount | Source account number for Qwiksend bank transfers. Defaults to vendor. | |
senderName | Account holder display name for bank transfers. Defaults to vendor. | |
senderPhone | Sender mobile number for bank transfers. | |
redirectUrl | Default redirect URL for checkout orders. Overridable per-payment via payload.redirectUrl. | |
cancelUrl | Cancel URL for checkout orders. Customer is sent here if they abandon payment. | |
webhookUrl | Webhook callback URL for payment status. Typically pesa.mountWebhook endpoint. |
const pesa = createPesa({
provider: new SelcomPaymentProvider({
apiKey: process.env.SELCOM_API_KEY!,
apiSecret: process.env.SELCOM_API_SECRET!,
vendor: process.env.SELCOM_VENDOR!,
pin: process.env.SELCOM_PIN!,
redirectUrl: 'https://mysite.com/return',
}),
})AzamPay
import { AzamPayPaymentProvider } from '@borapesa/azampay'| Field | Required | Description |
|---|---|---|
appName | ✅ | App name from AzamPay dashboard |
clientId | ✅ | Client ID from AzamPay dashboard |
clientSecret | ✅ | Client secret from AzamPay dashboard |
apiKey | ✅ | API key from AzamPay dashboard |
senderName | ✅ | Merchant display name for disbursement transfers |
senderBank | Merchant bank name for disbursement. Defaults to "AzamPay". | |
sandbox | Target sandbox (default: true). | |
authBaseUrl | Override auth base URL. | |
checkoutBaseUrl | Override checkout base URL. | |
disbursementBaseUrl | Override disbursement base URL. AzamPay uses separate hosts for checkout and disbursement — this defaults automatically per sandbox. |
const pesa = createPesa({
provider: new AzamPayPaymentProvider({
appName: process.env.AZAMPAY_APP_NAME!,
clientId: process.env.AZAMPAY_CLIENT_ID!,
clientSecret: process.env.AZAMPAY_CLIENT_SECRET!,
apiKey: process.env.AZAMPAY_API_KEY!,
senderName: process.env.AZAMPAY_SENDER_NAME!,
sandbox: false,
}),
})Snippe
import { SnippePaymentProvider } from '@borapesa/snippe'| Field | Required | Description |
|---|---|---|
apiKey | ✅ | Snippe API key (snp_...) from the Snippe dashboard |
webhookSecret | ✅ | HMAC-SHA256 signing key for webhook verification. Found in Snippe dashboard → Settings → Webhook Secret. |
baseUrl | Defaults to https://api.snippe.sh. Override for proxies or staging. | |
webhookUrl | Default webhook URL set on create/disburse calls. Provider callbacks POST here. | |
redirectUrl | Default redirect URL for card payments. Overridable per-payment via payload.redirectUrl. | |
cancelUrl | Cancel URL for card payments. Falls back to redirectUrl if not set. | |
timeoutMs | Request timeout in milliseconds (default: 30_000). |
const pesa = createPesa({
provider: new SnippePaymentProvider({
apiKey: process.env.SNIPPE_API_KEY!,
webhookSecret: process.env.SNIPPE_WEBHOOK_SECRET!,
webhookUrl: 'https://mysite.com/pesa/webhook',
redirectUrl: 'https://mysite.com/success',
cancelUrl: 'https://mysite.com/cancelled',
}),
})Provider-specific features
Some providers expose methods beyond the standard BasePaymentProvider interface. These are available on the provider instance directly (pesa.provider) but are not part of the unified PesaInstance API.
Selcom
- Bank disbursement (Qwiksend) — send to bank accounts via
senderAccount,senderName,senderPhoneconfig. - Order management —
cancelOrder(),listOrders()are first-class. - Wallet pull payment —
checkoutWalletPayment()triggers USSD from an existing checkout order for in-app payments. - Utility payments —
payUtility(),lookupUtility(),queryUtilityStatus()for LUKU, DSTV, GEPG, airtime, and more. - Selcom Pesa —
selcomPesaCashin(),selcomPesaNameLookup()for Selcom's mobile wallet. - Agent cashout —
agentCashout()sends funds for cash pickup at Huduma agents via*150*50#. - Stored cards —
fetchStoredCards(),deleteStoredCard()for tokenized card management.
ClickPesa
- BillPay — 7 methods for control number generation, customer management, and payment status.
- Payout links —
generatePayoutLink()for shareable disbursement URLs. - Exchange rates —
getExchangeRates()for currency conversion. - Bank listing —
getBanks()for supported bank codes. - Account statements —
getAccountStatement()for reconciliation.
AzamPay
- Bank checkout — OTP-based bank payment flow (CRDB, NMB).
- Payment partners —
getPaymentPartners()to list available MNO partners. - RSA callback verification — callbacks are signed with RSA-SHA256; the SDK verifies them automatically.
- Disbursement routing — separate base URL for disbursement endpoints (
disbursementBaseUrlconfig). - Provider tracking —
raw._providerNameandraw._checkoutProvideron order results for status queries.
Snippe
- Card payments —
createOrderwithredirectUrl(config or payload) routes to Snippe's card endpoint (Visa/Mastercard). ReturnscheckoutUrlfor the payment page. - Hosted checkout sessions —
createCheckoutSession()/getCheckoutSession()/listCheckoutSessions()/cancelCheckoutSession()provider-specific methods for Snippe's hosted payment page. ReturnscheckoutUrl+paymentLinkUrlfor sharing via SMS/WhatsApp. - USSD push retrigger —
retriggerPush(reference)re-sends the PIN prompt for a pending mobile payment. - Payout fee preview —
previewDisburse()returns the transaction fee before committing a payout. - Search —
listOrders()accepts optional Snippe-specific filters:status,paymentType, and free-textq. - Payout failure reason —
raw.failure_reasonon failed disbursements for debugging. - HMAC webhook verification — signature verification with replay protection (5-minute window).
Writing a provider
Every provider extends BasePaymentProvider. Four methods are required, eight are optional.
import { BasePaymentProvider } from '@borapesa/pesa'
class MyProvider extends BasePaymentProvider {
readonly name = 'my-provider'
// Required
async createOrder(payload) {
/* ... */
}
async getPaymentStatus(orderId) {
/* ... */
}
async handleWebhook(body, headers) {
/* ... */
}
async disburse(payload) {
/* ... */
}
// Optional — override what you support
async getNameLookup(phoneOrAccount) {
/* ... */
}
}See the Architecture page for the full provider lifecycle.