smartguycodes/billing
Composer 安装命令:
composer require smartguycodes/billing
包简介
A configurable Laravel billing package for SaaS businesses in Kenya. Handles M-Pesa STK Push, subscriptions, dunning, and renewal reminders.
README 文档
README
Drop-in Laravel billing for the Kenyan SaaS market.
Zero plumbing. Define your plans, configure M-Pesa, and your billing loop is sorted — subscriptions, STK Push, callbacks, dunning, renewal reminders, and an admin UI all included.
Architecture
smart-billing/
├── src/
│ ├── BillingServiceProvider.php # Auto-discovery, publishes, registers
│ ├── Concerns/Billable.php # Trait for your User model
│ ├── Contracts/PaymentDriver.php # Interface for all payment drivers
│ ├── Drivers/MpesaDriver.php # Full Daraja API — STK Push, C2B, Refunds
│ ├── Facades/Billing.php
│ ├── Services/
│ │ ├── BillingManager.php # Extensible driver manager
│ │ ├── PaymentService.php # Orchestrates initiate → callback → renew
│ │ ├── SubscriptionService.php # subscribe, cancel, resume, changePlan
│ │ └── DunningService.php # Retry logic, suspension, cancellation
│ ├── Models/ # 5 Eloquent models with scopes & helpers
│ ├── Http/Controllers/ # Admin UI + REST API + Webhook handlers
│ ├── Events/ # 8 events for your listeners
│ ├── Notifications/RenewalReminder
│ ├── Console/Commands/ # billing:install/renewals/dunning/reminders
│ └── Support/PaymentResult.php # Typed result value object
├── config/billing.php # All configurable — driver, dunning, reminders
├── database/migrations/ # 6 tables created in one migration
├── resources/views/admin/ # Dark-themed admin UI — dashboard, all CRUD
└── routes/web.php + api.php
- Drivers: M-Pesa (Daraja) out of the box, with a simple interface to add Stripe, Flutterwave, etc.
- Transactions: Every payment attempt is stored with a unique reference, status, and metadata
- Subscriptions: Plan-based billing with trial periods, grace periods, and dunning support
- Admin UI: View transactions, manage plans, and monitor subscription statuses
- Scheduler: Daily commands to process renewals, send reminders, and handle dunning
Requirements
| PHP | ^8.2 |
| Laravel | ^11 | ^12 |
| M-Pesa | Daraja API credentials |
Installation
composer require smartguycodes/billing
php artisan billing:install
This publishes the config, runs migrations, and optionally seeds sample plans.
Configuration
.env
# Core BILLING_DRIVER=mpesa BILLING_CURRENCY=KES BILLING_CURRENCY_SYMBOL=KSh # M-Pesa (Daraja) MPESA_ENV=sandbox # sandbox | production MPESA_CONSUMER_KEY=your_key MPESA_CONSUMER_SECRET=your_secret MPESA_SHORTCODE=174379 MPESA_PASSKEY=your_passkey MPESA_TYPE=paybill # paybill | till MPESA_CALLBACK_URL=https://yourapp.com/api/billing/webhooks/mpesa/callback MPESA_TIMEOUT_URL=https://yourapp.com/api/billing/webhooks/mpesa/timeout # Admin UI BILLING_ADMIN_PREFIX=billing-admin # Invoice BILLING_INVOICE_PREFIX=INV BILLING_COMPANY_NAME="My SaaS Co" BILLING_REF_PREFIX=TXN
Billable Model
Add the Billable trait to your User model:
use SmartGuyCodes\Billing\Concerns\Billable; class User extends Authenticatable { use Billable; }
Usage
Initiate an M-Pesa STK Push
use SmartGuyCodes\Billing\Facades\Billing; $result = $user->charge([ 'amount' => 999, 'account_number' => '0712345678', // Customer phone 'account_type' => 'mobile', 'transaction_type' => 'income', 'description' => 'Starter Plan - March 2025', ]); if ($result->isPending()) { // STK Push sent — wait for callback $checkoutId = $result->checkoutRequestId; }
Subscribe to a Plan
// By slug $plan = BillingPlan::where('slug', 'pro')->first(); $subscription = $user->subscribeTo($plan); // With trial $subscription = $user->subscribeTo($plan, ['trial_days' => 14]);
Check Subscription Status
$user->isSubscribed(); // bool $user->onTrial(); // bool $user->subscribedTo('pro'); // bool $sub = $user->activeSubscription(); $sub->daysUntilRenewal(); // int $sub->isActive(); // bool $sub->onGracePeriod(); // bool
Cancel & Resume
$user->cancelSubscription(); // At period end $user->cancelSubscription(immediately: true); // Right now // Resume within grace period $sub->resume();
Verify a Transaction
$transaction = BillingTransaction::where('reference_no', 'TXN-...')->first(); $result = app(\SmartGuyCodes\Billing\Services\PaymentService::class)->verify($transaction);
Transaction Data Model
Every transaction stores the full API User Layer:
| Field | Description |
|---|---|
reference_no |
System-generated — TXN-250101120000-ABCD |
invoice_number |
INV-2025-00042 |
client_no |
Your identifier for the customer |
account_number |
Mobile number, bank account, or card number |
account_type |
mobile | bank | card |
transaction_type |
income | expense |
amount |
Float |
currency |
e.g. KES |
status |
pending → completed | failed | refunded |
driver |
mpesa | stripe | flutterwave |
gateway_ref |
M-Pesa receipt number or Stripe charge ID |
M-Pesa Webhook URLs
Register these in the Safaricom Daraja portal:
| Type | URL |
|---|---|
| STK Callback | https://yourapp.com/api/billing/webhooks/mpesa/callback |
| STK Timeout | https://yourapp.com/api/billing/webhooks/mpesa/timeout |
| C2B Validation | https://yourapp.com/api/billing/webhooks/mpesa/validation |
| C2B Confirmation | https://yourapp.com/api/billing/webhooks/mpesa/confirmation |
REST API
All routes are prefixed /api/billing and require auth:sanctum.
POST /api/billing/pay
GET /api/billing/transactions/{ref}
POST /api/billing/transactions/{ref}/verify
GET /api/billing/plans
POST /api/billing/subscribe
GET /api/billing/subscription
DELETE /api/billing/subscription
POST /api/billing/subscription/resume
GET /api/billing/invoices
GET /api/billing/invoices/{id}
Example: Initiate Payment
POST /api/billing/pay
Authorization: Bearer {token}
Content-Type: application/json
{
"amount": 999,
"account_number": "0712345678",
"account_type": "mobile",
"transaction_type": "income",
"description": "Pro Plan Renewal"
}
Response:
{
"success": true,
"status": "pending",
"reference": "TXN-250401120000-XKQZ",
"checkout_request_id": "ws_CO_...",
"message": "Enter your M-Pesa PIN to complete the payment."
}
Admin Interface
Visit /{BILLING_ADMIN_PREFIX} (default: /billing-admin).
The middleware billing.admin gates access. By default it checks $user->is_admin. Override by publishing the middleware:
php artisan vendor:publish --tag=billing-views
Or define a gate in your AuthServiceProvider:
Gate::define('billing-admin', fn($user) => $user->role === 'superadmin');
Scheduler
Add to routes/console.php (Laravel 11+):
use Illuminate\Support\Facades\Schedule; Schedule::command('billing:renewals')->dailyAt('00:05'); Schedule::command('billing:dunning')->dailyAt('02:00'); Schedule::command('billing:reminders')->dailyAt('08:00');
| Command | Purpose |
|---|---|
billing:renewals |
Charge subscriptions due today |
billing:dunning |
Retry failed payments per dunning config |
billing:reminders |
Send renewal reminder notifications |
billing:install |
One-time setup wizard |
Adding a Custom Driver
// In a ServiceProvider app('billing')->extend('mpesa', function ($app) { return new MpesaDriver(config('billing.drivers.mpesa')); });
Your driver must implement SmartGuyCodes\Billing\Contracts\PaymentDriver:
interface PaymentDriver { public function initiate(array $payload): PaymentResult; public function verify(string $reference): PaymentResult; public function handleCallback(array $payload): PaymentResult; public function refund(string $reference, float $amount): PaymentResult; public function driverName(): string; public function validateConfig(): void; }
Events
Listen to these events in your application:
use SmartGuyCodes\Billing\Events\PaymentCompleted; Event::listen(PaymentCompleted::class, function ($event) { // $event->transaction — BillingTransaction // $event->result — PaymentResult Log::info("Payment received: {$event->transaction->reference_no}"); });
| Event | Fired When |
|---|---|
PaymentInitiated |
STK Push sent |
PaymentCompleted |
Callback confirms success |
PaymentFailed |
Callback confirms failure |
SubscriptionCreated |
User subscribes to a plan |
SubscriptionRenewed |
Subscription period renewed |
SubscriptionCancelled |
Subscription cancelled |
SubscriptionSuspended |
Dunning suspension |
DunningAttempted |
Retry payment attempted |
Plans Config (Optional)
If you prefer config-driven plans over database plans, set BILLING_PLANS_SOURCE=config and define in config/billing.php:
'plans' => [ [ 'name' => 'Starter', 'slug' => 'starter', 'price' => 999, 'interval' => 'monthly', 'trial_days' => 14, 'features' => ['Up to 5 users', '10GB storage'], ], ],
Usage
- Integrate billing features into your Laravel app using provided facades, models, and API endpoints.
- Access the billing dashboard at
/billing(if enabled).
Contributing
Contributions are welcome! Please open issues or submit pull requests to help improve the package.
License
This package is open-sourced software licensed under the MIT License.
smartguycodes/billing 适用场景与选型建议
smartguycodes/billing 是一款 基于 Blade 开发的 Composer 扩展包,目前已累计 0 次下载、GitHub Stars 达 2, 最近一次更新时间为 2026 年 04 月 06 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「payments」 「laravel」 「saas」 「billing」 「subscriptions」 「kenya」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 smartguycodes/billing 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 smartguycodes/billing 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 smartguycodes/billing 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Laravel Plans is a package for SaaS apps that need management over plans, features, subscriptions, events for plans or limited, countable features.
Dealing with payments through the Egyptian payment gateway PayMob
Librería para la gestión sencilla de pagos mediante TPV Redsys y Paypal
Asaas.com PHP API v3 Wrapper
Adaptaçao Asaas.com PHP API Wrapper para PHP 5.6
Alfabank REST API integration
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 3
- 点击次数: 47
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-04-06