A clean, developer-friendly subscription billing abstraction layer for PHP that brings Stripe-like subscription and proration features to Paystack and Flutterwave.
Paystack and Flutterwave provide billing rails, not billing logic. That means:
- ❌ No prorations
- ❌ No upgrade/downgrade handling
- ❌ No deferred upgrades
- ❌ No billing portal
- ❌ No unified "subscription experience" like Stripe's Billing
Teller solves this by providing a "Billing Logic Layer" on top of Paystack and Flutterwave - essentially "Stripe Billing for Africa."
- 🧾 Plan Management - Abstract plan creation for both Paystack + Flutterwave
- 💰 Proration Engine - Handles fair billing when upgrading or downgrading mid-cycle
- ⏱ Deferred Upgrades - Schedule upgrades for next billing cycle
- 🔄 Renewal Logic - Sync local DB cycles with gateway events
- 💳 Payment Gateway Abstraction - Plug-and-play support for Paystack & Flutterwave
- 📦 Webhook Handling - Standardized events (subscription.renewed, payment.failed, etc.)
- 🔐 Card Management - Generate and send "update card" links via API
- 🧮 Fair Billing Policies - Configurable rules (cutoff day for proration, rounding, etc.)
- 🪙 Wallet / Credit Balance - Automatically credit leftover time or unused value
- 🧠 Smart Billing Policy Engine - Define rules like "After 25 days, always defer upgrades"
- 🌍 Multi-gateway sync - Allow users to switch gateway without breaking subscriptions
- 📊 Dashboard - For merchants to monitor subscriptions, churn, renewals
- ⚙️ API + SDK - REST API and SDK (PHP/JS) for non-Laravel users
composer require josephajibodu/tellerConfigure Teller with your gateway credentials:
use JosephAjibodu\Teller\Helpers\TellerConfig;
TellerConfig::set([
'default_gateway' => 'paystack',
'gateways' => [
'paystack' => [
'secret_key' => 'sk_test_...',
'public_key' => 'pk_test_...',
],
'flutterwave' => [
'secret_key' => 'FLWSECK_TEST-...',
'public_key' => 'FLWPUBK_TEST-...',
],
],
'proration' => [
'enabled' => true,
'rounding' => 'up',
],
]);use JosephAjibodu\Teller\Teller;
// Initialize with default gateway
$billing = Teller::make();
// Or specify a gateway
$billing = Teller::gateway('flutterwave');
// Or use the fluent API
$billing = Teller::for($user)->onGateway('paystack');// Create a plan
$plan = $billing->plans()->create([
'name' => 'Pro Plan',
'amount' => 25000, // 250 NGN in kobo
'interval' => 'monthly',
'description' => 'Access to premium features',
]);
// Get all plans
$plans = $billing->plans()->all();
// Update a plan
$billing->plans()->update('pro-plan-id', [
'amount' => 30000,
]);// Create a customer
$customer = $billing->customers()->create([
'email' => 'joseph@example.com',
'name' => 'Joseph Ajibodu',
'metadata' => ['team_id' => 4],
]);
// Update customer's card
$billing->customers()->updateCard('customer-id', 'new-card-token');// Create a subscription
$subscription = $billing->subscriptions()->create('customer-id', [
'plan_id' => 'pro-monthly',
'trial_days' => 7,
]);
// Cancel a subscription
$billing->subscriptions()->cancel('subscription-id');
// Resume a subscription (if within grace period)
$billing->subscriptions()->resume('subscription-id');This is Teller's core differentiator:
// Upgrade with proration (charge immediately)
$billing->subscriptions()->upgrade('subscription-id', [
'to_plan' => 'business-monthly',
'prorate' => true,
'immediate' => true,
]);
// Schedule upgrade for next billing cycle
$billing->subscriptions()->upgrade('subscription-id', [
'to_plan' => 'business-monthly',
'prorate' => false,
'effective' => 'next_cycle',
]);
// Downgrade with proration
$billing->subscriptions()->downgrade('subscription-id', [
'to_plan' => 'basic-monthly',
'prorate' => true,
]);// Create an invoice
$invoice = $billing->invoices()->create([
'customer_id' => 'customer-id',
'amount' => 5000, // 50 NGN in kobo
'description' => 'Extra storage space',
]);
// List invoices for a customer
$invoices = $billing->invoices()->all(['customer_id' => 'customer-id']);// In your webhook endpoint
$event = Teller::webhooks()->handle($_POST);
switch ($event->type) {
case 'subscription.created':
// Handle new subscription
break;
case 'subscription.renewed':
// Handle subscription renewal
break;
case 'subscription.upgraded':
// Handle subscription upgrade
break;
case 'subscription.cancelled':
// Handle subscription cancellation
break;
case 'payment.failed':
// Handle failed payment
break;
}// Chain operations for better readability
Teller::for($user)
->onGateway('paystack')
->subscriptions()
->upgrade('subscription-id', [
'to_plan' => 'premium-monthly',
'prorate' => true,
]);use JosephAjibodu\Teller\Support\Money;
// Create money objects
$amount = Money::fromNaira(250.00); // 250 NGN
$amount = Money::fromKobo(25000); // 250 NGN in kobo
// Perform calculations
$total = $amount->add(Money::fromNaira(50.00)); // 300 NGN
$discount = $amount->multiply(0.1); // 10% discount
$formatted = $amount->format(); // "250.00 NGN"use JosephAjibodu\Teller\Support\DateHelper;
$date = new \DateTime();
// Get billing information
$daysInMonth = DateHelper::daysInMonth($date);
$daysRemaining = DateHelper::daysRemainingInMonth($date);
$nextBilling = DateHelper::nextBillingDate($date, 'monthly');use JosephAjibodu\Teller\Helpers\TellerConfig;
TellerConfig::set([
'default_gateway' => 'paystack',
'gateways' => [
'paystack' => [
'secret_key' => 'sk_test_...',
'public_key' => 'pk_test_...',
'webhook_secret' => 'whsec_...',
],
'flutterwave' => [
'secret_key' => 'FLWSECK_TEST-...',
'public_key' => 'FLWPUBK_TEST-...',
'webhook_secret' => 'your_webhook_secret',
],
],
'proration' => [
'enabled' => true,
'rounding' => 'up',
'cutoff_day' => 25,
],
]);# Run individual examples
php examples/plan-management.php
php examples/customer-management.php
php examples/subscription-management.php
php examples/invoice-management.php
php examples/money-helper.php
php examples/date-helper.php
php examples/fluent-api.php
# Run all examples
php examples/run-all-examples.phpPlease see CONTRIBUTING.md for details.
The MIT License (MIT). Please see License File for more information.
- Inspired by Stripe's billing API design
- Built for the African developer community
- Special thanks to Paystack and Flutterwave for their excellent APIs
- 📧 Email: josephajibodu@gmail.com
- 🐛 Issues: GitHub Issues
- 📖 Documentation: GitHub Wiki
Made with ❤️ for African developers