Payment Providers
PimaBill supports multiple payment providers for global and African markets. Configure one or more providers to process payments for your tenants.
Supported Providers
| Provider | Coverage | Payment Methods |
|---|---|---|
| Stripe | Global (195+ countries) | Cards, Bank Transfers, Wallets (Apple Pay, Google Pay) |
| Flutterwave | Nigeria, Kenya, Ghana, South Africa, Uganda, Tanzania | Cards, Bank Transfer, Mobile Money, USSD |
| Paystack | Nigeria, Ghana, South Africa, Kenya | Cards, Bank Transfer, USSD |
| Pesapal | Kenya, Uganda, Tanzania | M-Pesa, Airtel Money, cards |
| PayPal | Global | PayPal balance, cards |
| PayU | South Africa and others | Cards, EFT |
| DPO | Across Africa | Cards, 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
}'
import { PimaBillClient } from 'pimabill';
const client = new PimaBillClient({ token: 'sk_live_a1b2c3d4...' });
const provider = await client.paymentProviders.configure({
providerName: 'STRIPE',
credentials: {
secretKey: 'sk_live_xxxxx',
webhookSecret: 'whsec_xxxxx'
},
isActive: true,
priority: 1
});
from pimabill import PimaBillClient
client = PimaBillClient(token="sk_live_a1b2c3d4...")
provider = client.payment_providers.configure(
provider_name="STRIPE",
credentials={
"secret_key": "sk_live_xxxxx",
"webhook_secret": "whsec_xxxxx"
},
is_active=True,
priority=1
)
sk_test_...) for development and live keys (sk_live_...) for production.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
}'
const provider = await client.paymentProviders.configure({
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
});
provider = client.payment_providers.configure(
provider_name="FLUTTERWAVE",
credentials={
"public_key": "FLWPUBK-xxxxx",
"secret_key": "FLWSECK-xxxxx",
"encryption_key": "xxxxx",
"secret_hash": "the secret hash set under Webhooks in your Flutterwave dashboard"
},
is_active=True,
priority=2
)
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
}'
const provider = await client.paymentProviders.configure({
providerName: 'PAYSTACK',
credentials: {
publicKey: 'pk_live_xxxxx',
secretKey: 'sk_live_xxxxx'
},
isActive: true,
priority: 3
});
provider = client.payment_providers.configure(
provider_name="PAYSTACK",
credentials={
"public_key": "pk_live_xxxxx",
"secret_key": "sk_live_xxxxx"
},
is_active=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..."
const result = await client.paymentProviders.testConnection('provider_abc123');
console.log(result.success); // true
result = client.payment_providers.test_connection("provider_abc123")
print(result.success) # True
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:
| Priority | Provider | Use Case |
|---|---|---|
| 1 | Stripe | International cards and wallets |
| 2 | Flutterwave | African markets (NG, KE, GH, ZA, UG, TZ) |
| 3 | Paystack | Nigerian market fallback |
| 4 | Pesapal | M-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.duplicatefor 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.