keenops / laravel-tcb-cms
Laravel package for integrating with Tanzania Commercial Bank's Cash Management System (CMS) API
Requires
- php: ^8.2
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/events: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/routing: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^2.0|^3.0|^4.0
- pestphp/pest-plugin-laravel: ^2.0|^3.0|^4.0
README
A Laravel package for integrating with Tanzania Commercial Bank's Cash Management System (CMS) API. This package enables payment reference creation, IPN (Instant Payment Notification) handling, reconciliation, and reference cancellation.
Requirements
- PHP 8.2+
- Laravel 11.x, 12.x, or 13.x
Installation
Install the package via Composer:
composer require keenops/laravel-tcb-cms
The package will auto-register its service provider and facade.
Publish Configuration
php artisan vendor:publish --tag=tcb-cms-config
Run Migrations
php artisan migrate
This creates the tcb_cms_transactions table for transaction logging.
Configuration
Add the following environment variables to your .env file:
TCB_CMS_API_KEY=your-api-key TCB_CMS_PARTNER_CODE=PART-YOURCODE TCB_CMS_PROFILE_ID=1234567890 TCB_CMS_BASE_URL=https://partners.tcbbank.co.tz TCB_CMS_RECONCILIATION_BASE_URL=https://partners.tcbbank.co.tz:8444 TCB_CMS_IPN_ROUTE=/tcb-cms/ipn TCB_CMS_VERIFY_IP=false TCB_CMS_ALLOWED_IPS= TCB_CMS_TIMEOUT=30 TCB_CMS_RETRY_TIMES=3 TCB_CMS_LOGGING_ENABLED=true
Configuration Options
| Option | Description | Default |
|---|---|---|
api_key |
Your TCB CMS API key | - |
partner_code |
Your partner code assigned by TCB | - |
profile_id |
Your profile ID | - |
base_url |
TCB CMS API base URL | https://partners.tcbbank.co.tz |
reconciliation_base_url |
Reconciliation API base URL | https://partners.tcbbank.co.tz:8444 |
ipn.route |
IPN callback route | /tcb-cms/ipn |
ipn.middleware |
Middleware for IPN route | ['api'] |
verify_ip |
Enable IP verification for IPN | false |
allowed_ips |
Comma-separated allowed IPs | - |
timeout |
HTTP request timeout (seconds) | 30 |
retry_times |
Number of retry attempts | 3 |
logging.enabled |
Enable transaction logging | true |
Usage
Using the Facade
use Keenops\LaravelTcbCms\Facades\TcbCms;
Create a Payment Reference
Create a payment reference for a customer to make payments:
$response = TcbCms::createReference( reference: '999MYREF001', name: 'John Doe', mobile: '0712345678', message: 'Invoice #12345', ); if ($response->isSuccessful()) { echo "Account No: " . $response->accountNo; echo "Reference No: " . $response->referenceNo; } else { echo "Error: " . $response->message; }
With optional amount and expiry date:
$response = TcbCms::createReference( reference: '999MYREF001', name: 'John Doe', mobile: '0712345678', message: 'Invoice #12345', amount: 50000.00, expiryDate: '2024-12-31', );
Cancel a Payment Reference
$response = TcbCms::cancelReference(referenceNo: '999MYREF001'); if ($response->isSuccessful()) { echo "Reference cancelled successfully"; }
Reconciliation
Fetch all transactions within a date range:
use Carbon\Carbon; $response = TcbCms::reconcile( startDate: Carbon::now()->subDays(7), endDate: Carbon::now(), ); if ($response->isSuccessful()) { echo "Total Transactions: " . $response->totalCount; echo "Total Amount: " . $response->totalAmount; foreach ($response->transactions as $transaction) { echo $transaction->transactionId; echo $transaction->reference; echo $transaction->amount; echo $transaction->payerName; echo $transaction->transactionDate->format('Y-m-d H:i:s'); } }
Handling Payment Notifications (IPN)
The package automatically registers an IPN route at /tcb-cms/ipn (configurable). When TCB Bank sends a payment notification, the package dispatches a PaymentReceived event.
Create an Event Listener
// app/Listeners/HandleTcbPayment.php namespace App\Listeners; use Keenops\LaravelTcbCms\Events\PaymentReceived; class HandleTcbPayment { public function handle(PaymentReceived $event): void { $payload = $event->payload; // Access payment details $transactionId = $payload->transactionId; $reference = $payload->reference; $amount = $payload->amount; $payerName = $payload->payerName; $payerMobile = $payload->payerMobile; $transactionDate = $payload->transactionDate; // Update your order/invoice $order = Order::where('payment_reference', $reference)->first(); if ($order) { $order->markAsPaid($transactionId, $amount); } } }
Register the Listener
// app/Providers/EventServiceProvider.php use Keenops\LaravelTcbCms\Events\PaymentReceived; use App\Listeners\HandleTcbPayment; protected $listen = [ PaymentReceived::class => [ HandleTcbPayment::class, ], ];
Or using Laravel 11+ event discovery, the listener will be auto-registered.
Available Events
| Event | Description | Payload |
|---|---|---|
PaymentReceived |
Dispatched when IPN callback is received | IpnPayload $payload |
ReferenceCreated |
Dispatched on successful reference creation | CreateReferenceRequest $request, CreateReferenceResponse $response |
ReferenceCancelled |
Dispatched on successful reference cancellation | CancelReferenceRequest $request, CancelReferenceResponse $response |
ReconciliationCompleted |
Dispatched after successful reconciliation | ReconciliationRequest $request, ReconciliationResponse $response |
Payment Channel Helper
Get payment instructions for different channels:
use Keenops\LaravelTcbCms\Enums\PaymentChannel; // Get instructions for a specific channel $instructions = PaymentChannel::TcbMobile->getPaymentInstructions('999MYREF001'); // "Open TCB Mobile App > Payments > Bill Payments > Enter Reference: 999MYREF001" // Get all channels with instructions $channels = PaymentChannel::allWithInstructions('999MYREF001'); foreach ($channels as $value => $channel) { echo $channel['label']; // "TCB Mobile Banking" echo $channel['instructions']; // Payment instructions }
Available channels:
TCB_MOBILE- TCB Mobile BankingTCB_BRANCH- TCB BranchTCB_ATM- TCB ATMUSSD- USSD BankingINTERNET_BANKING- Internet BankingAGENT_BANKING- Agent BankingPESALINK- PesaLink
Transaction Logging
All API requests and IPN callbacks are logged to the tcb_cms_transactions table when logging is enabled.
Query Transaction Logs
use Keenops\LaravelTcbCms\Models\TcbTransaction; // Get all transactions for a reference $transactions = TcbTransaction::forReference('999MYREF001')->get(); // Get all successful transactions $successful = TcbTransaction::successful()->get(); // Get all failed transactions $failed = TcbTransaction::failed()->get(); // Get transactions by type $ipnLogs = TcbTransaction::ofType('ipn')->get(); $createLogs = TcbTransaction::ofType('create_reference')->get(); $cancelLogs = TcbTransaction::ofType('cancel_reference')->get(); $reconcileLogs = TcbTransaction::ofType('reconciliation')->get();
Disable Logging
Set TCB_CMS_LOGGING_ENABLED=false in your .env file or update the config:
// config/tcb-cms.php 'logging' => [ 'enabled' => false, ],
IP Verification for IPN
For additional security, enable IP verification to only accept IPN callbacks from TCB Bank's servers:
TCB_CMS_VERIFY_IP=true TCB_CMS_ALLOWED_IPS=192.168.1.1,192.168.1.2
Error Handling
The package throws specific exceptions for different error scenarios:
use Keenops\LaravelTcbCms\Exceptions\TcbCmsException; use Keenops\LaravelTcbCms\Exceptions\ApiConnectionException; use Keenops\LaravelTcbCms\Exceptions\InvalidApiKeyException; use Keenops\LaravelTcbCms\Exceptions\InvalidReferenceException; try { $response = TcbCms::createReference(...); } catch (InvalidApiKeyException $e) { // Invalid API key } catch (ApiConnectionException $e) { // Connection failed } catch (TcbCmsException $e) { // General API error $context = $e->context(); // Additional error context }
Response Status Codes
use Keenops\LaravelTcbCms\Enums\ResponseStatus; ResponseStatus::Success; // 0 - Successful operation ResponseStatus::Failure; // 1 - Operation failed ResponseStatus::ConnectionError; // 2 - Connection error ResponseStatus::ApiKeyError; // 4 - Invalid API key
Testing
Run the package tests:
composer test
Or using Pest directly:
vendor/bin/pest
Mocking in Your Application Tests
use Illuminate\Support\Facades\Http; use Keenops\LaravelTcbCms\Facades\TcbCms; it('creates an order with payment reference', function () { Http::fake([ '*/api/v1/cms/reference/create' => Http::response([ 'status' => 0, 'message' => 'Reference created successfully', 'accountNo' => '240123456789', 'referenceNo' => '999MYREF001', 'partnerCode' => 'TEST-PARTNER', ], 200), ]); $response = TcbCms::createReference( reference: '999MYREF001', name: 'John Doe', mobile: '0712345678', message: 'Test Order', ); expect($response->isSuccessful())->toBeTrue(); });
Changelog
Please see CHANGELOG for more information on what has changed recently.
License
The MIT License (MIT). Please see License File for more information.
Credits
Support
For issues and feature requests, please use the GitHub issue tracker.