Guides

Payment Providers

Configure global and African payment providers to process payments for your tenants.

PimaBill supports multiple payment providers for global and African markets. Configure one or more providers to process payments for your tenants.

Supported Providers

ProviderCoveragePayment Methods
StripeGlobal (195+ countries)Cards, Bank Transfers, Wallets (Apple Pay, Google Pay)
FlutterwaveNigeria, Kenya, Ghana, South Africa, Uganda, TanzaniaCards, Bank Transfer, Mobile Money, USSD
PaystackNigeria, Ghana, South Africa, KenyaCards, Bank Transfer, USSD
PesapalKenya, Uganda, TanzaniaM-Pesa, Airtel Money, cards
PayPalGlobalPayPal balance, cards
PayUSouth Africa and othersCards, EFT
DPOAcross AfricaCards, mobile money

These process invoice payments through a checkout. Collections and payouts outside invoices also use MTN MoMo, Airtel Money, Yo! Payments, Eversend, NOWPayments, Binance Pay and your own crypto wallet: see the Money movement section of the SDK guides.

List providers

GET /api/payment-providers lists every provider PimaBill supports, configured or not, so your integration can show what can be connected. Each entry says whether it's configured and active, its priority, which environment its saved credentials are for (live or sandbox, when that can be told from them), and the webhook URL to give the provider. Credentials are never returned. Pass configured=true to list only the providers you've set up.

[
  { "id": "clx123", "providerName": "flutterwave", "name": "Flutterwave", "isConfigured": true, "isActive": true, "priority": 1, "environment": "live", "webhookUrl": "https://api.pimabill.com/webhooks/flutterwave/ten_123" },
  { "id": null, "providerName": "stripe", "name": "Stripe", "isConfigured": false, "isActive": false, "priority": null, "environment": null, "webhookUrl": "https://api.pimabill.com/webhooks/stripe/ten_123" }
]

Live and the sandbox each keep their own providers. In the sandbox, connect each provider with its test credentials. On live, a provider whose credentials are for its test environment is never used as a fallback for live payments.

Configure Stripe

Stripe is the recommended provider for international payments and customers outside Africa.

curl -X POST http://localhost:4000/api/payment-providers \
  -H "Authorization: Bearer sk_live_a1b2c3d4..." \
  -H "Content-Type: application/json" \
  -d '{
    "providerName": "STRIPE",
    "credentials": {
      "secretKey": "sk_live_xxxxx",
      "webhookSecret": "whsec_xxxxx"
    },
    "isActive": true,
    "priority": 1
  }'
Get your Stripe API keys from the Stripe Dashboard. Use test keys (sk_test_...) for development and live keys (sk_live_...) for production.
The webhookSecret (the signing secret of your Stripe webhook endpoint) is required: Stripe notifications without a valid stripe-signature are refused, so payments are not confirmed until it is set.

Configure Flutterwave

curl -X POST http://localhost:4000/api/payment-providers \
  -H "Authorization: Bearer sk_live_a1b2c3d4..." \
  -H "Content-Type: application/json" \
  -d '{
    "providerName": "FLUTTERWAVE",
    "credentials": {
      "publicKey": "FLWPUBK-xxxxx",
      "secretKey": "FLWSECK-xxxxx",
      "encryptionKey": "xxxxx",
      "secretHash": "the secret hash set under Webhooks in your Flutterwave dashboard"
    },
    "isActive": true,
    "priority": 2
  }'
Set the same secret hash in the Flutterwave dashboard (Settings → Webhooks) and as secretHash here. Notifications without a matching verif-hash header are refused, and every accepted one is confirmed with Flutterwave's verify API before an invoice is marked paid.

Configure Paystack

curl -X POST http://localhost:4000/api/payment-providers \
  -H "Authorization: Bearer sk_live_a1b2c3d4..." \
  -H "Content-Type: application/json" \
  -d '{
    "providerName": "PAYSTACK",
    "credentials": {
      "publicKey": "pk_live_xxxxx",
      "secretKey": "sk_live_xxxxx"
    },
    "isActive": true,
    "priority": 3
  }'

Test a Connection

Before going live, test that your provider credentials are valid:

curl -X POST http://localhost:4000/api/payment-providers/provider_abc123/test \
  -H "Authorization: Bearer sk_live_a1b2c3d4..."

Provider Priority

When several providers are configured, a checkout uses the one it names, otherwise the active provider with the lowest priority number. There is no automatic failover: deactivate a provider to stop using it.

A recommended setup for a global billing system:

PriorityProviderUse Case
1StripeInternational cards and wallets
2FlutterwaveAfrican markets (NG, KE, GH, ZA, UG, TZ)
3PaystackNigerian market fallback
4PesapalM-Pesa and Airtel Money in East Africa

Credential Security

All provider credentials are encrypted at rest using AES-256 encryption. They are never logged or exposed in API responses.

How payments are confirmed

  • Signed notifications (Stripe, Paystack, Flutterwave, PayPal) are verified with the provider's signature; unsigned ones are refused.
  • Unsigned notifications (PayU, DPO, Pesapal) only prompt a status request to the provider; the outcome, amount and reference come from the provider's API, never from the notification.
  • A success whose amount or currency differs from the payment, or that reports no amount, is held for review (payment.held_for_review) instead of marking the invoice paid.
  • Money that arrives for an invoice already paid or voided is flagged with payment.duplicate for you to refund.

No provider account yet?

On the hosted cloud, managed payments takes card, mobile money and PayPal payments for you and pays you out to mobile money, a bank account or PayPal. It is used only when none of your own providers can take a payment, so you can connect your own later without changing your integration.

Mobile money (MTN MoMo, Airtel Money, Yo! Payments), PayPal and crypto providers are also used for one-time collections and payouts; see Money movement.

Copyright © 2026