Bora PesaBora Pesa

Providers

Available payment providers — Selcom, ClickPesa, AzamPay, Snippe — and their capabilities.

Added in v0.1.0

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.

SelcomClickPesaAzamPaySnippe
MNO USSD push✅✅✅✅
MNO redirect checkout✅✅——
Card payment—✅—✅
Bank checkout (OTP)——✅—
Hosted payment page—✅✅✅
Disburse (wallet)✅✅✅✅
Disburse (bank)✅✅—✅
Webhook signatureHMAC-SHA256HMAC-SHA256RSA-PKCS#1HMAC-SHA256
Order cancellation✅———
Order listing✅——✅
Balance query✅——✅
Name lookup✅✅✅—
Credential validation✅✅✅✅
Auth methodHMAC per-requestBearer tokenBearer tokenBearer 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'
FieldRequiredDescription
clientId✅Client ID from ClickPesa dashboard
apiKey✅API key from ClickPesa dashboard
checksumKeyHMAC-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.
sandboxtrue → https://api-sandbox.clickpesa.com. false (default) → https://api.clickpesa.com.
baseUrlExplicit base URL override — takes precedence over sandbox.
redirectUrlDefault 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'
FieldRequiredDescription
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
baseUrlDefaults to https://apigw.selcommobile.com. Set to the sandbox URL for testing.
senderAccountSource account number for Qwiksend bank transfers. Defaults to vendor.
senderNameAccount holder display name for bank transfers. Defaults to vendor.
senderPhoneSender mobile number for bank transfers.
redirectUrlDefault redirect URL for checkout orders. Overridable per-payment via payload.redirectUrl.
cancelUrlCancel URL for checkout orders. Customer is sent here if they abandon payment.
webhookUrlWebhook 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'
FieldRequiredDescription
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
senderBankMerchant bank name for disbursement. Defaults to "AzamPay".
sandboxTarget sandbox (default: true).
authBaseUrlOverride auth base URL.
checkoutBaseUrlOverride checkout base URL.
disbursementBaseUrlOverride 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'
FieldRequiredDescription
apiKey✅Snippe API key (snp_...) from the Snippe dashboard
webhookSecret✅HMAC-SHA256 signing key for webhook verification. Found in Snippe dashboard → Settings → Webhook Secret.
baseUrlDefaults to https://api.snippe.sh. Override for proxies or staging.
webhookUrlDefault webhook URL set on create/disburse calls. Provider callbacks POST here.
redirectUrlDefault redirect URL for card payments. Overridable per-payment via payload.redirectUrl.
cancelUrlCancel URL for card payments. Falls back to redirectUrl if not set.
timeoutMsRequest 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, senderPhone config.
  • 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 (disbursementBaseUrl config).
  • Provider tracking — raw._providerName and raw._checkoutProvider on order results for status queries.

Snippe

  • Card payments — createOrder with redirectUrl (config or payload) routes to Snippe's card endpoint (Visa/Mastercard). Returns checkoutUrl for the payment page.
  • Hosted checkout sessions — createCheckoutSession() / getCheckoutSession() / listCheckoutSessions() / cancelCheckoutSession() provider-specific methods for Snippe's hosted payment page. Returns checkoutUrl + paymentLinkUrl for 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-text q.
  • Payout failure reason — raw.failure_reason on 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.

On this page