Overview
Payment providers are the backbone of FundKit. Each provider represents a different payment service (like HoneyCoin, EasyPay, or Tola) with its own API, authentication, and capabilities. FundKit abstracts these differences behind a unified interface.
Think of providers as different payment “channels” - each with their own strengths, coverage
areas, and supported features. FundKit lets you use them all through one consistent API.
Provider Architecture
BaseProvider Class
All payment providers extend the BaseProvider
class, ensuring consistent behavior:
import { BaseProvider } from '@fundkit/core' ;
abstract class BaseProvider {
abstract collection ( transaction : Transaction ) : Promise < CollectionResponse >;
abstract getTransaction ( txId : string ) : Promise < TransactionStatus >;
abstract validateTransaction ( transaction : Transaction ) : Promise < boolean >;
// Common functionality
getName () : string ;
getCapabilities () : ProviderCapabilities ;
isHealthy () : Promise < boolean >;
}
Provider Lifecycle
Initialization
Provider is configured with API credentials and settings const honeycoin = new HoneyCoin ({
apiKey: 'your_api_key' ,
publicKey: 'your_public_key'
});
Registration
Provider is added to PaymentClient const client = new PaymentClient ({
providers: [ honeycoin , easypay , tola ]
});
Health Check
FundKit periodically checks provider health and availability
Selection
PaymentClient selects the best provider for each transaction
Execution
Transaction is sent to the selected provider
Supported Providers
HoneyCoin
Coverage: Uganda, Kenya, Tanzania
Specialties: Mobile money, multi-country support
import { HoneyCoin } from '@fundkit/honeycoin' ;
const honeycoin = new HoneyCoin ({
apiKey: process . env . HONEYCOIN_API_KEY ! ,
publicKey: process . env . HONEYCOIN_PUBLIC_KEY ! ,
environment: 'sandbox' , // or 'production'
});
Capabilities:
Collections (charge customers)
Payouts (send money)
Status checking
Webhook notifications
Multi-currency (UGX, KES, TZS)
Multiple operators (MTN, Airtel, Safaricom)
Limits:
Minimum: 500 UGX / 50 KES / 1000 TZS
Maximum: 10,000,000 UGX / 1,000,000 KES / 20,000,000 TZS
EasyPay
Coverage: Uganda
Specialties: Fast processing, webhook support
import { EasyPay } from '@fundkit/easypay' ;
const easypay = new EasyPay ({
apiKey: process . env . EASYPAY_SECRET ! ,
clientId: process . env . EASYPAY_CLIENT_ID ! ,
environment: 'sandbox' ,
});
Capabilities:
Collections
Real-time webhooks
Fast processing (< 30 seconds)
MTN and Airtel support
Payouts (coming soon)
Limits:
Minimum: 1,000 UGX
Maximum: 5,000,000 UGX
Tola
Coverage: Uganda
Specialties: Banking integration, reconciliation
import { Tola } from '@fundkit/tola' ;
const tola = new Tola ({
apiKey: process . env . TOLA_API_KEY ! ,
merchantId: process . env . TOLA_MERCHANT_ID ! ,
environment: 'sandbox' ,
});
Capabilities:
Collections
Payouts
Bank transfers
Transaction reconciliation
Detailed reporting
Multiple payment methods
Limits:
Minimum: 500 UGX
Maximum: 20,000,000 UGX
Provider Configuration
Basic Configuration
Each provider requires specific configuration:
const honeycoin = new HoneyCoin ({
apiKey: 'your_api_key' , // Required
publicKey: 'your_public_key' , // Required
environment: 'sandbox' , // 'sandbox' | 'production'
timeout: 30000 , // Request timeout (ms)
retries: 3 // Retry attempts
});
Advanced Configuration
const honeycoin = new HoneyCoin ({
apiKey: process . env . HONEYCOIN_API_KEY ! ,
publicKey: process . env . HONEYCOIN_PUBLIC_KEY ! ,
environment: 'production' ,
// Advanced options
timeout: 45000 ,
retries: 5 ,
retryDelay: 2000 ,
// Rate limiting
rateLimit: {
requests: 100 ,
window: 60000 , // Per minute
},
// Custom headers
headers: {
'User-Agent' : 'MyApp/1.0' ,
'X-Custom-Header' : 'value' ,
},
// Webhook configuration
webhook: {
url: 'https://api.myapp.com/webhooks/honeycoin' ,
secret: 'webhook_secret_key' ,
events: [ 'transaction.completed' , 'transaction.failed' ],
},
// Logging
logger: customLogger ,
logLevel: 'debug' ,
});
Provider Selection Strategies
Smart Selection (Default)
FundKit automatically selects the best provider based on multiple factors:
const client = new PaymentClient ({
providers: [ honeycoin , easypay , tola ],
strategy: 'smart' , // Default
});
// FundKit will automatically choose the best provider
const result = await client . collection ( transaction );
Selection Criteria:
Currency Support - Does the provider support this currency?
Amount Limits - Can the provider handle this transaction amount?
Operator Support - Does the provider support this mobile operator?
Provider Health - Is the provider currently healthy?
Success Rate - Historical success rate for similar transactions
Response Time - Average response time
Fees - Transaction fees (if optimization enabled)
Round Robin
Distribute transactions evenly across providers:
const client = new PaymentClient ({
providers: [ honeycoin , easypay , tola ],
strategy: 'round-robin' ,
});
// First transaction -> HoneyCoin
// Second transaction -> EasyPay
// Third transaction -> Tola
// Fourth transaction -> HoneyCoin (cycles back)
Priority-Based
Use providers in order of preference:
const client = new PaymentClient ({
providers: [
{ provider: honeycoin , priority: 1 }, // Try first
{ provider: easypay , priority: 2 }, // Try second
{ provider: tola , priority: 3 }, // Try last
],
strategy: 'priority' ,
});
Manual Selection
Override automatic selection for specific transactions:
// Force use of specific provider
const result = await client . collection ( transaction , {
preferredProvider: 'easypay' ,
});
// Exclude certain providers
const result = await client . collection ( transaction , {
excludeProviders: [ 'tola' ],
});
Provider Health Monitoring
FundKit continuously monitors provider health:
Health Metrics
// Check provider health
const health = await honeycoin . getHealth ();
console . log ( health );
// {
// status: 'healthy',
// uptime: 99.9,
// responseTime: 1200,
// successRate: 98.5,
// lastCheck: '2024-01-15T10:30:00Z',
// errors: []
// }
Health States
Provider is functioning normally:
Response time < 5 seconds
Success rate > 95%
No recent errors
Provider is working but with issues:
Response time 5-15 seconds
Success rate 85-95%
Some recent errors
Provider has significant problems:
Response time > 15 seconds
Success rate < 85%
Multiple recent errors
Provider is completely unavailable:
Connection timeouts
Authentication failures
Service unavailable responses
Health-Based Fallback
// Configure health-based fallback
const client = new PaymentClient ({
providers: [ honeycoin , easypay , tola ],
fallbackStrategy: {
enabled: true ,
healthThreshold: 'degraded' , // Fall back if provider is degraded or worse
maxFallbacks: 2 , // Try up to 2 alternative providers
fallbackDelay: 1000 , // Wait 1 second between attempts
},
});
Provider Capabilities
Querying Capabilities
// Check what a provider supports
const capabilities = honeycoin . getCapabilities ();
console . log ( capabilities );
// {
// collections: true,
// payouts: true,
// currencies: ['UGX', 'KES', 'TZS'],
// operators: ['mtn', 'airtel', 'safaricom'],
// features: ['webhooks', 'status_check', 'refunds'],
// limits: {
// min: { UGX: 500, KES: 50, TZS: 1000 },
// max: { UGX: 10000000, KES: 1000000, TZS: 20000000 }
// }
// }
Capability-Based Selection
// Find providers that support specific features
const providers = client . getProvidersWithCapability ( 'payouts' );
const kenyaProviders = client . getProvidersForCurrency ( 'KES' );
const fastProviders = client . getProvidersByResponseTime ( '< 5s' );
Provider Events
Monitor provider activities:
// Listen for provider events
honeycoin . on ( 'request:started' , request => {
console . log ( 'API request started:' , request . url );
});
honeycoin . on ( 'request:completed' , ( request , response ) => {
console . log ( 'API request completed:' , response . status );
});
honeycoin . on ( 'request:failed' , ( request , error ) => {
console . error ( 'API request failed:' , error . message );
});
honeycoin . on ( 'health:changed' , ( oldHealth , newHealth ) => {
console . log ( `Health changed from ${ oldHealth } to ${ newHealth } ` );
});
// PaymentClient also emits provider events
client . on ( 'provider:selected' , ( provider , transaction ) => {
console . log ( `Selected ${ provider . getName () } for ${ transaction . externalId } ` );
});
client . on ( 'provider:fallback' , ( fromProvider , toProvider , reason ) => {
console . log ( `Falling back from ${ fromProvider } to ${ toProvider } : ${ reason } ` );
});
Best Practices
Use Multiple Providers // Good: Multiple providers for redundancy
const client = new PaymentClient ({
providers: [ honeycoin , easypay , tola ]
});
Monitor Provider Health // Good: Regular health checks
setInterval ( async () => {
for ( const provider of providers ) {
const health = await provider . getHealth ();
if ( health . status === 'unhealthy' ) {
await notifyTeam ( provider , health );
}
}
}, 300000 ); // Every 5 minutes
Secure Credential Storage // Good: Use environment variables
const honeycoin = new HoneyCoin ({
apiKey: process . env . HONEYCOIN_API_KEY ! ,
publicKey: process . env . HONEYCOIN_PUBLIC_KEY !
});
// Bad: Hardcoded credentials
// const honeycoin = new HoneyCoin({
// apiKey: 'hc_123456789',
// publicKey: 'pk_abcdefghi'
// });
Configure Webhooks // Good: Use webhooks for real-time updates
const easypay = new EasyPay ({
apiKey: process . env . EASYPAY_SECRET ! ,
clientId: process . env . EASYPAY_CLIENT_ID ! ,
webhookUrl: 'https://api.myapp.com/webhooks/easypay'
});
Troubleshooting
// Check credentials are correct
try {
const health = await provider . getHealth ();
console . log ( 'Provider healthy:' , health . status );
} catch ( error ) {
if ( error . code === 'AUTHENTICATION_FAILED' ) {
console . error ( 'Invalid API credentials' );
// Check environment variables
// Verify credentials with provider
}
}
// Increase timeout for slow providers
const honeycoin = new HoneyCoin ({
apiKey: process . env . HONEYCOIN_API_KEY ! ,
publicKey: process . env . HONEYCOIN_PUBLIC_KEY ! ,
timeout: 60000 // 60 seconds
});
// Handle rate limiting gracefully
try {
const result = await provider . collection ( transaction );
} catch ( error ) {
if ( error . code === 'RATE_LIMITED' ) {
const retryAfter = error . details . retryAfter ;
await sleep ( retryAfter * 1000 );
// Retry the request
}
}
Next Steps