serenity_technologies / gtbghana-partner-api
GTBGhana Partner API
Package info
gitlab.com/serenity-technologies-library/gtbghana-partner-api
pkg:composer/serenity_technologies/gtbghana-partner-api
Requires
- illuminate/cache: ^10.0|^11.0|^12.0|^13.0
- illuminate/console: ^10.0|^11.0|^12.0|^13.0
- illuminate/database: ^10.0|^11.0|^12.0|^13.0
- illuminate/events: ^10.0|^11.0|^12.0|^13.0
- illuminate/http: ^10.0|^11.0|^12.0|^13.0
- illuminate/routing: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
Requires (Dev)
- phpunit/phpunit: ^12.3
This package is auto-updated.
Last update: 2026-03-30 23:46:05 UTC
README
A Laravel package to integrate with GTB Ghana's Partner API, allowing you to access various financial services like airtime topup, bill payments, bank transfers, and more.
Installation
You can install the package via composer:
composer require serenity_technologies/gtbghana-partner-api
Quick Setup
After installing the package, you can quickly publish all necessary files with the install command:
php artisan gtb-api:install
This command will publish:
- Configuration file to config/partner-api.php
- Migration files to
database/migrations/ - Bank model to
app/Models/
Manual Configuration
Alternatively, you can manually publish the files:
After installing the package, you need to publish the configuration file:
php artisan vendor:publish --provider="SerenityTechnologies\GtbGhana\PartnerApi\PartnerApiServiceProvider" --tag="config"
This will create a config/partner-api.php file. You can also set your configuration values in your .env file:
GTB_PARTNER_API_ENV=live
GTB_PARTNER_SECRET_KEY=your_secret_key_here
GTB_PARTNER_MERCHANT_ID=your_merchant_id_here
GTB_PARTNER_MERCHANT_KEY=your_merchant_key_here
Database Setup
To use the bank database feature, you need to run the migrations:
php artisan vendor:publish --provider="SerenityTechnologies\GtbGhana\PartnerApi\PartnerApiServiceProvider" --tag="migrations"
php artisan migrate
If you want to customize the Bank model, you can publish it to your application:
php artisan vendor:publish --provider="SerenityTechnologies\GtbGhana\PartnerApi\PartnerApiServiceProvider" --tag="models"
Configuration Options
GTB_PARTNER_API_ENV: Set to eithertestorlive(default:test)GTB_PARTNER_SECRET_KEY: Your secret key for generating secure hashesGTB_PARTNER_MERCHANT_ID: Your merchant ID for API authenticationGTB_PARTNER_MERCHANT_KEY: Your merchant key for API authentication
Usage
With Facade (Recommended)
First, make sure to import the facade:
use SerenityTechnologies\GtbGhana\PartnerApi\Facades\PartnerApi;
Get Available Services
This api call returns a list of services available on Partner API
To make a request to the Partner API, you can use the PartnerApi facade:
Then, you can use the facade to make requests to the Partner API:
$response = PartnerApi::getServices('2022011800004');
or
$response = PartnerApi::getServices(uniquePartnerTransId());
or
$response = PartnerApi::getServices(timeBasedUniqueInt());
Query Customer Details
This api call allows you to lookup or verify customer details on a service on Partner API
To query customer details, you can use the PartnerApi facade:
Query Definitions
| Parameter | Required | Details |
|---|---|---|
| PartnerTransId | YES | Unique Id or reference for request Type: Numeric Max length 20. |
| ServiceName | YES | Service Name of the service being accessed. (Found in response of the services endpoint) |
| ServiceNumber | YES | The account number / identifier of service being verified Eg. Bank account Number, Mobile Money Wallet Number, Student ID. |
| SecondaryServiceNumber | NO | Required for specific Services that need extra data for verification. eg. For Bank Transfer: ServiceNumber is the account number, SecondaryServiceNumber is the bank code as specified in the banks list ( to get banks list, use the services endpoint with banks as service name) |
$response = PartnerApi::query(
'2022011800007851', // PartnerTransId
'banktrf', // ServiceName
'0013014480189501', // ServiceNumber
'300312'
); // SecondaryServiceNumber (optional) );
Make a Payment
This api call allows you to pay for a service or complete a merchant transaction on Partner API
To make a payment, you can use the PartnerApi facade:
| Parameter | Required | Details |
|---|---|---|
| PartnerTransId | YES | Unique Id or reference for request Type: Numeric Max length 20. |
| CallbackURL | YES | a URI the API will send response payload to on completion of request |
| SecureHash | YES | An HMACSHA256 hash of some of the parameters in the request. Below are the required parameters and the order they should be concatenated. _ServiceName + ServiceNumber+ SecondaryServiceNumber+Amount+Remarks+PartnerTransId_ key for hashing will be provided as part of your credentials. |
| ServiceName | YES | Service Name of the service being accessed. (Found in response of the services endpoint) |
| ServiceNumber | YES | The account number / identifier of service. Eg. Bank account Number, Mobile Money Wallet Number, Student ID. |
| SecondaryServiceNumber | NO | Required for specific Services that need extra data for payment. Eg. Bank Transfer using GIP and NRT require this field. NOTE Bank Transfer (GIP) uses the GIP bank code as specified in the banks list ( to get banks list, use the services endpoint with banks as service name). Bank Transfer (NRT) uses the bank sort codes provided by BOG. If GIP Code is provided for NRT transactions, the main sort code of the receiving bank is used. BOG Sort Codes |
| Amount | YES | Amount to be paid |
| Remarks | YES | Narration/Remarks of transaction |
| PaidBy | YES | Name of Person making the transaction |
| PayeeName | NO | Name of Person receiving the transaction |
$response = PartnerApi::pay(
'20220118000556', // PartnerTransId
'https://webhook.site/4c4d2ec6-542f-45fe-b555-6bfbc51fd019', // CallbackURL
'banktrf', // ServiceName
'201113000110', // ServiceNumber
'0.10', // Amount
'credit account', // Remarks
'Benedicta Ghansah', // PaidBy
'300322', // SecondaryServiceNumber (optional)
'Mary-Ann Edem Afeku' // PayeeName (optional)
);
Check Transaction Status
$response = PartnerApi::getStatus('202201140008');
Get List of Banks
To get a list of banks, you can use the PartnerApi facade:
$response = PartnerApi::getBanks('2022011800004');
Verify Account Details
Before making transfers, you can verify account details to ensure they exist and match expected names:
// Verify bank account details
$verification = PartnerApi::verifyBankAccount(
'20220118000556', // PartnerTransId
'0013014480189501', // Account Number
'300312', // Bank Code
'John Doe' // Expected Account Name (optional)
);
if ($verification['verified']) {
echo "Account verified: " . $verification['account_name'];
} else {
echo "Verification failed: " . $verification['message'];
}
Without Facade
You can also resolve the service from the container:
$partnerApi = app('partner-api');
// Or using the service container directly
$partnerApi = app(SerenityTechnologies\GtbGhana\PartnerApi\PartnerAPI::class);
// Then use any of the methods
$response = $partnerApi->getServices('2022011800004');
Available Methods
| Method | Description |
|---|---|
getServices(string $partnerTransId) | Get a list of all available services |
query(string $partnerTransId, string $serviceName, string $serviceNumber, string $secondaryServiceNumber = '') | Query customer details for a specific service |
pay(string $partnerTransId, string $callbackUrl, string $serviceName, string $serviceNumber, string $amount, string $remarks, string $paidBy, string $secondaryServiceNumber = '', string $payeeName = '') | Make a payment for a service |
getStatus(string $partnerTransId) | Check the status of a transaction |
getBanks(string $partnerTransId) | Get a list of all supported banks |
bankTransfer(string $partnerTransId, string $callbackUrl, string $accountNumber, string $amount, string $remarks, string $paidBy, string $bankCode = '', string $payeeName = '') | Make a bank transfer |
creditWallet(string $partnerTransId, string $callbackUrl, string $walletNumber, string $amount, string $remarks, string $paidBy, string $payeeName = '') | Credit a wallet |
debitWallet(string $partnerTransId, string $callbackUrl, string $walletNumber, string $amount, string $remarks, string $paidBy, string $payeeName = '') | Debit a wallet |
verifyBankAccount(string $partnerTransId, string $accountNumber, string $bankCode, string $expectedAccountName = null) | Verify bank account details |
verifyWallet(string $partnerTransId, string $walletNumber, string $expectedAccountName = null) | Verify wallet details |
PaymentResponse Methods
When working with verified transactions, the PaymentResponse object includes additional methods for accessing verification data:
| Method | Description |
|---|---|
wasVerified() | Check if verification was performed |
isVerified() | Check if verification was successful |
getVerificationData() | Get the complete verification data array |
Wallet Services Details
Make a Bank Transfer
Bank Transfer (banktrf)
- Used to transfer funds to a bank account
- Requires a bank code for the recipient's bank when using GIP
- Supports both GIP and NRT transfer methods
To make a bank transfer, you can use the bankTransfer method:
// Standard bank transfer
$response = PartnerApi::bankTransfer(
'20220118000556', // PartnerTransId
'https://webhook.site/4c4d2ec6-542f-45fe-b555-6bfbc51fd019', // CallbackURL
'201113000110', // Account Number
'0.10', // Amount
'credit account', // Remarks
'Benedicta Ghansah', // PaidBy
'300322', // Bank Code (optional)
'Mary-Ann Edem Afeku' // PayeeName (optional)
);
// Verified bank transfer (automatically verifies account before transfer)
$response = PartnerApi::bankTransfer(
'20220118000556', // PartnerTransId
'https://webhook.site/4c4d2ec6-542f-45fe-b555-6bfbc51fd019', // CallbackURL
'201113000110', // Account Number
'0.10', // Amount
'credit account', // Remarks
'Benedicta Ghansah', // PaidBy
'300322', // Bank Code (required for verification)
'Mary-Ann Edem Afeku', // PayeeName (optional, used for name verification)
true // Verify account before transfer
);
// Access verification data from the response
if ($response->wasVerified()) {
if ($response->isVerified()) {
echo "Account verified: " . $response->getVerificationData()['account_name'];
} else {
echo "Verification failed: " . $response->getVerificationData()['message']; }
}
Credit a Wallet
Credit Wallet (CREDITWALLET)
- Used to add funds to a mobile money wallet
- Typically used for customer refunds or top-ups
To credit a wallet, you can use the
creditWalletmethod:
// Standard wallet credit
$response = PartnerApi::creditWallet(
'20220118000557', // PartnerTransId
'https://webhook.site/4c4d2ec6-542f-45fe-b555-6bfbc51fd019', // CallbackURL
'0241234567', // Wallet Number
'50.00', // Amount
'wallet credit', // Remarks
'John Doe', // PaidBy
'Jane Doe' // PayeeName (optional)
);
// Verified wallet credit (automatically verifies wallet before crediting)
$response = PartnerApi::creditWallet(
'20220118000557', // PartnerTransId
'https://webhook.site/4c4d2ec6-542f-45fe-b555-6bfbc51fd019', // CallbackURL
'0241234567', // Wallet Number
'50.00', // Amount
'wallet credit', // Remarks
'John Doe', // PaidBy
'Jane Doe', // PayeeName (optional, used for name verification)
true // Verify wallet before crediting
);
Debit a Wallet
Debit Wallet (DEBITWALLET)
- Used to deduct funds from a mobile money wallet
- Typically used for payments or withdrawals
To debit a wallet, you can use the debitWallet method:
// Standard wallet debit
$response = PartnerApi::debitWallet(
'20220118000558', // PartnerTransId
'https://webhook.site/4c4d2ec6-542f-45fe-b555-6bfbc51fd019', // CallbackURL
'0241234567', // Wallet Number
'25.00', // Amount
'wallet debit', // Remarks
'John Doe', // PaidBy
'Jane Doe' // PayeeName (optional)
);
// Verified wallet debit (automatically verifies wallet before debiting)
$response = PartnerApi::debitWallet(
'20220118000558', // PartnerTransId
'https://webhook.site/4c4d2ec6-542f-45fe-b555-6bfbc51fd019', // CallbackURL
'0241234567', // Wallet Number
'25.00', // Amount
'wallet debit', // Remarks
'John Doe', // PaidBy
'Jane Doe', // PayeeName (optional, used for name verification)
true // Verify wallet before debiting
);
Working with Responses
All methods return response objects that provide helpful methods for working with the API responses:
// Get services
$response = PartnerApi::getServices('2022011800004');
// Check if request was successful
if ($response->isSuccessful()) {
// Get the services data
$services = $response->getServices();
// Find a specific service
$bankTransferService = $response->getServiceByName('banktrf');
}
// Make a payment
$paymentResponse = PartnerApi::bankTransfer(
'20220118000556',
'https://webhook.site/4c4d2ec6-542f-45fe-b555-6bfbc51fd019',
'201113000110',
'0.10',
'credit account',
'Benedicta Ghansah',
'300322'
);
// Check payment status
if ($paymentResponse->isSuccessful()) {
echo "Transaction completed successfully";
} elseif ($paymentResponse->isPending()) {
echo "Transaction is pending";
} elseif ($paymentResponse->isFailed()) {
echo "Transaction failed: " . $paymentResponse->getMessage();
}
// Get transaction reference
$transactionRef = $paymentResponse->getTransactionReference();
Error Handling
The package will throw an Illuminate\Http\Client\ConnectionException if there are network issues or if the API is unreachable. You should wrap your calls in try-catch blocks:
use Illuminate\Http\Client\ConnectionException;
use SerenityTechnologies\GtbGhana\PartnerApi\Facades\PartnerApi;
try {
$response = PartnerApi::getServices('2022011800004');
} catch (ConnectionException $e) {
// Handle connection error
Log::error('Failed to connect to GTB Partner API: ' . $e->getMessage());
}
For verification errors, the package will throw exceptions when using automatic verification:
use SerenityTechnologies\GtbGhana\PartnerApi\Facades\PartnerApi;
try {
// This will throw an exception if verification fails
$response = PartnerApi::bankTransfer(
'20220118000556',
'https://webhook.site/4c4d2ec6-542f-45fe-b555-6bfbc51fd019',
'201113000110',
'0.10',
'credit account',
'Benedicta Ghansah',
'300322',
'Expected Name',
true // Enable verification
);
} catch (Exception $e) {
echo "Transfer failed: " . $e->getMessage();
}
Available Services
Based on the API documentation, the following services are available:
Airtime
- Service Name:
airtime - Description: Airtime Topup
- Service Name:
Utilities
- Service Name:
dstv- DSTV - Service Name:
gotv- GOTV - Service Name:
gwcl- Ghana Water Payment - Service Name:
ecg- ECG Postpaid Payment - Service Name:
box_office- BOX OFFICE
- Service Name:
Investment/Pensions
- Service Name:
mfund- DATABANK MFUND - Service Name:
epack- DATABANK EPACK - Service Name:
bfund- DATABANK BFUND
- Service Name:
Education
- Service Name:
sltf- STUDENT LOAN TRUST FUND
- Service Name:
Transfers
- Service Name:
banktrf- Bank Transfer - Service Name:
CREDITWALLET- Credit Wallet - Service Name:
DEBITWALLET- Debit Wallet
- Service Name:
Account Verification
The package includes robust account verification features to help prevent sending money to incorrect accounts:
Name Matching
The verification system includes intelligent name matching that can handle:
- Case differences
- Extra whitespace
- Name abbreviations
- Minor spelling variations
- Partial name matches
Verification Process
- Account/Wallet Lookup: The system first queries the API to verify the account exists
- Name Comparison: If an expected name is provided, it's compared with the returned name
Match Analysis: The system uses multiple techniques to determine if names match:
- Direct comparison
- Partial matching
- Word-by-word comparison
- Levenshtein distance for minor character differences
Manual Verification
You can manually verify accounts before making transfers:
// Verify a bank account
$verification = PartnerApi::verifyBankAccount(
'20220118000556', // PartnerTransId
'0013014480189501', // Account Number
'300312', // Bank Code
'John Doe' // Expected Account Name
);
if ($verification['verified']) {
// Account is verified, proceed with transfer
$transfer = PartnerApi::bankTransfer(/* parameters */);
} else {
// Handle verification failure
echo "Verification failed: " . $verification['message'];
}
Automatic Verification
You can enable automatic verification during transfers by setting the verify parameter to true:
// This will automatically verify before transferring
$response = PartnerApi::bankTransfer(
'20220118000556',
'https://webhook.site/4c4d2ec6-542f-45fe-b555-6bfbc51fd019',
'201113000110',
'0.10',
'credit account',
'Benedicta Ghansah',
'300322', // Required for verification
'Expected Name', // Used for name verification
true // Enable automatic verification
);
Caching
This package now includes caching mechanisms to reduce the number of calls to the GTBank API, improving performance significantly.
Configuration
You can configure the cache TTL in your partner-api.php config file:
'cache_ttl' => 30 // Cache TTL in minutes
Cached Methods
verifyBankAccount()- Caches bank account verification resultsverifyWallet()- Caches wallet verification resultsverifyAccount()inBankTransferService- Caches bank transfer verification results- Wallet verification in
CreditWalletServiceandDebitWalletService
Cache Management
You can clear specific caches using these methods:
clearBankVerificationCache(string $accountNumber, string $bankCode)clearWalletVerificationCache(string $serviceName, string $walletNumber)clearBankVerificationResultCache(string $accountNumber, string $bankCode, ?string $expectedAccountName = null)clearWalletVerificationResultCache(string $serviceName, string $walletNumber, ?string $expectedAccountName = null)
To force a refresh of cached data, use the forceRefresh parameter in verification methods.
Webhook Handling (New Event-Based Approach with Signature Validation)
⚠️ DEPRECATION NOTICE: The old callback handler classes (
CallbackManager,BaseCallbackHandler,PaymentCallbackHandler, etc.) and the deprecatedCallbackControllerare deprecated as of v2.0.0. Please migrate to the new event-based webhook system described below. The old classes will be removed in a future version.
The package now provides a comprehensive webhook handling system with:
- Automatic signature validation via middleware
- Payload validation for required fields and proper structure
- Event dispatching for flexible handling in your application
- Service-specific endpoints for different transaction types
Quick Setup
1. Configure your webhook URL in GTB dashboard:
https://yourdomain.com/gtb-webhook
2. Add to your .env:
GTB_PARTNER_SECRET_KEY=your_secret_key_here
GTB_WEBHOOK_VERIFY_SIGNATURE=true
GTB_WEBHOOK_VALIDATE_PAYLOAD=true
# Optional: Enable status verification with GTB API
GTB_WEBHOOK_STATUS_VERIFY=false
3. Register event listeners in EventServiceProvider:
use Illuminate\Support\Facades\Event;
use SerenityTechnologies\GtbGhana\PartnerApi\Events\CallbackProcessed;
public function boot(): void
{
Event::listen(CallbackProcessed::class, function (CallbackProcessed $event) {
if ($event->wasSuccessful()) {
// Update your database
// Send notifications
// Update balances
}
});
}
That's it! The package handles signature validation, payload validation, and event dispatching automatically.
Webhook Routes
The package automatically registers these routes:
| Route | Method | Description |
|---|---|---|
/gtb-webhook | POST | Main webhook endpoint (handles all types) |
/gtb-webhook/bank-transfer | POST | Bank transfer specific webhook |
/gtb-webhook/wallet | POST | Wallet (credit/debit) specific webhook |
To customize routes, publish them:
php artisan vendor:publish --provider="SerenityTechnologies\GtbGhana\PartnerApi\PartnerApiServiceProvider" --tag="routes"
Middleware
All webhook routes use middleware for validation:
- ValidateWebhookPayload - Validates required fields and structure
- ValidateWebhookSignature - Validates HMAC-SHA256 signature
Signature verification is automatically disabled in testing environments.
Configuration
Publish the config file:
php artisan vendor:publish --provider="SerenityTechnologies\GtbGhana\PartnerApi\PartnerApiServiceProvider" --tag="config"
Key webhook settings in config/partner-api.php:
'webhooks' => [
'verify_signature' => env('GTB_WEBHOOK_VERIFY_SIGNATURE', true),
'validate_payload' => env('GTB_WEBHOOK_VALIDATE_PAYLOAD', true),
'logging' => env('GTB_WEBHOOK_LOGGING', true),
'route_prefix' => env('GTB_WEBHOOK_ROUTE_PREFIX', 'gtb-webhook'),
'middleware' => env('GTB_WEBHOOK_MIDDLEWARE', []),
],
Available Events
Generic Events:
CallbackReceived- Dispatched when any webhook is received (includes validation status)CallbackProcessed- Dispatched when a webhook is successfully processedCallbackFailed- Dispatched when webhook processing fails
Service-Specific Events:
BankTransferCallbackReceived- Dispatched for bank transfer webhooksBankTransferCallbackProcessed- Dispatched when bank transfer is processedWalletCallbackReceived- Dispatched for wallet (credit/debit) webhooksWalletCallbackProcessed- Dispatched when wallet transaction is processedPaymentCallbackReceived- Dispatched for generic payment webhooksPaymentCallbackProcessed- Dispatched when generic payment is processed
Event Properties
// CallbackReceived event
$event->payload // array - Full webhook payload
$event->serviceType // string - 'banktrf', 'wallet', etc.
$event->signatureVerified // bool - Whether signature was verified
$event->payloadValidated // bool - Whether payload was validated
$event->getTransactionId() // string|null - GTB transaction ID
$event->getPartnerTransId() // string|null - Your transaction ID
$event->getStatus() // string|null - Transaction status
$event->isValidated() // bool - Both signature and payload validated
// CallbackProcessed event
$event->payload // array - Full webhook payload
$event->result // array - Processing result
$event->serviceType // string - Service type
$event->wasSuccessful() // bool - Processing success
$event->getTransactionId() // string|null - GTB transaction ID
$event->getPartnerTransId() // string|null - Your transaction ID
Setting Up Event Listeners
Option 1: Inline Registration (Recommended for Simple Cases)
In your EventServiceProvider or any service provider:
use Illuminate\Support\Facades\Event;
use SerenityTechnologies\GtbGhana\PartnerApi\Events\CallbackReceived;
use SerenityTechnologies\GtbGhana\PartnerApi\Events\CallbackProcessed;
use SerenityTechnologies\GtbGhana\PartnerApi\Events\CallbackFailed;
public function boot(): void
{
// Handle all callbacks
Event::listen(CallbackReceived::class, function (CallbackReceived $event) {
\Log::info('Callback received', [
'partner_trans_id' => $event->getPartnerTransId(),
'gtb_trans_id' => $event->getTransactionId(),
'status' => $event->getStatus(),
'service_type' => $event->serviceType,
'signature_verified' => $event->isSignatureVerified(),
'payload_validated' => $event->isPayloadValidated(),
]);
// Your business logic here
});
// Handle successfully processed callbacks
Event::listen(CallbackProcessed::class, function (CallbackProcessed $event) {
if ($event->wasSuccessful()) {
// Update your database
// Send notifications
// Update balances
}
});
// Handle failed callbacks
Event::listen(CallbackFailed::class, function (CallbackFailed $event) {
\Log::error('Callback failed', [
'error' => $event->errorMessage,
'partner_trans_id' => $event->getPartnerTransId(),
]);
// Alert administrators
// Retry logic
});
}
Option 2: Service-Specific Listeners
For more granular control, listen to service-specific events:
use Illuminate\Support\Facades\Event;
use SerenityTechnologies\GtbGhana\PartnerApi\Events\BankTransferCallbackReceived;
use SerenityTechnologies\GtbGhana\PartnerApi\Events\WalletCallbackReceived;
public function boot(): void
{
// Handle bank transfer callbacks
Event::listen(BankTransferCallbackReceived::class, function (BankTransferCallbackReceived $event) {
$accountNumber = $event->getAccountNumber();
$bankCode = $event->getBankCode();
$status = $event->getStatus();
// Update bank transfer in your database
// Send notifications to recipient
});
// Handle wallet callbacks
Event::listen(WalletCallbackReceived::class, function (WalletCallbackReceived $event) {
if ($event->isCreditWallet()) {
// Handle credit wallet
} elseif ($event->isDebitWallet()) {
// Handle debit wallet
}
$walletNumber = $event->getWalletNumber();
$network = $event->getNetwork();
});
}
Option 3: Listener Classes (Recommended for Complex Logic)
Create listener classes for better organization:
// In app/Listeners/HandlePaymentCallback.php
namespace App\Listeners;
use SerenityTechnologies\GtbGhana\PartnerApi\Events\CallbackProcessed;
class HandlePaymentCallback
{
public function handle(CallbackProcessed $event): void
{
// Access callback data
$payload = $event->payload;
$result = $event->result;
$serviceType = $event->serviceType;
// Your business logic
if ($event->wasSuccessful()) {
// Update transaction status
// Send notifications
// Update user balances
}
}
}
Register in EventServiceProvider:
protected $listen = [
\SerenityTechnologies\GtbGhana\PartnerApi\Events\CallbackReceived::class => [
\App\Listeners\HandlePaymentCallback::class,
],
\SerenityTechnologies\GtbGhana\PartnerApi\Events\BankTransferCallbackReceived::class => [
\App\Listeners\HandleBankTransferCallback::class,
],
\SerenityTechnologies\GtbGhana\PartnerApi\Events\WalletCallbackReceived::class => [
\App\Listeners\HandleWalletCallback::class,
],
];
Setting up Callback Routes
Even with the event-based system, you still need to set up routes to receive callbacks:
// In routes/web.php or routes/api.php
use SerenityTechnologies\GtbGhana\PartnerApi\Http\Controllers\CallbackController;
// Generic callback endpoint (recommended)
Route::post('/gtb-api/callback', [CallbackController::class, 'handleCallback']);
// Service-specific endpoints (optional, deprecated but still supported)
Route::post('/gtb-api/callback/bank-transfer', [CallbackController::class, 'handleBankTransferCallback']);
Route::post('/gtb-api/callback/wallet', [CallbackController::class, 'handleWalletCallback']);
Example: Complete Implementation
Here's a complete example of handling callbacks in a production application:
// In app/Providers/EventServiceProvider.php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
use SerenityTechnologies\GtbGhana\PartnerApi\Events\CallbackProcessed;
use SerenityTechnologies\GtbGhana\PartnerApi\Events\BankTransferCallbackProcessed;
use SerenityTechnologies\GtbGhana\PartnerApi\Events\WalletCallbackProcessed;
use App\Models\Transaction;
use App\Services\NotificationService;
class EventServiceProvider extends ServiceProvider
{
public function boot(): void
{
// Handle successful payment callbacks
Event::listen(CallbackProcessed::class, function (CallbackProcessed $event) {
if (!$event->wasSuccessful()) {
return;
}
$partnerTransId = $event->getPartnerTransId();
$transaction = Transaction::where('partner_trans_id', $partnerTransId)->first();
if ($transaction) {
$transaction->update([
'status' => strtolower($event->payload['Status']),
'gtb_trans_id' => $event->getTransactionId(),
'completed_at' => now(),
]);
// Send notification to user
app(NotificationService::class)->sendTransactionNotification(
$transaction->user,
$transaction
);
}
});
// Handle bank transfer callbacks
Event::listen(BankTransferCallbackProcessed::class, function (BankTransferCallbackProcessed $event) {
if (!$event->wasSuccessful()) {
return;
}
// Update bank transfer specific data
$accountNumber = $event->payload['Data']['ServiceNumber'] ?? null;
$bankCode = $event->payload['Data']['SecondaryServiceNumber'] ?? null;
\Log::info('Bank transfer completed', [
'account' => $accountNumber,
'bank_code' => $bankCode,
]);
});
// Handle wallet callbacks
Event::listen(WalletCallbackProcessed::class, function (WalletCallbackProcessed $event) {
if (!$event->wasSuccessful()) {
return;
}
// Update wallet transaction
$walletNumber = $event->payload['Data']['ServiceNumber'] ?? null;
$network = $event->payload['Data']['Network'] ?? null;
\Log::info('Wallet transaction completed', [
'wallet' => $walletNumber,
'network' => $network,
'type' => $event->walletType,
]);
});
}
}
Migrating from the Old System
If you're using the deprecated callback handlers, here's how to migrate:
Before (Old System):
class CustomBankTransferHandler extends BankTransferCallbackHandler
{
protected function handleCompletedPayment(PaymentResponse $response): array
{
$result = parent::handleCompletedPayment($response);
// Custom logic
$this->updateDatabase($response);
$this->sendNotification($response);
return $result;
}
}
After (New Event System):
Event::listen(BankTransferCallbackProcessed::class, function (BankTransferCallbackProcessed $event) {
if (!$event->wasSuccessful()) {
return;
}
// Your custom logic
$this->updateDatabase($event->payload);
$this->sendNotification($event->payload);
});
Callback Payload
The callback payload from GTB API typically looks like this:
{
"SecureHash": "47ff0d9b1e7bbdaa97ba3441e76d0a308d7d23aba38882a3277bc4bb0bc30585",
"GTBTransId": "1075202201140008",
"PartnerTransId": "202201140008",
"ProviderTransId": "",
"Status": "COMPLETED",
"Message": "Transaction Successful",
"Data": null
}
Testing Callbacks
You can test your event listeners by dispatching test events:
use SerenityTechnologies\GtbGhana\PartnerApi\Events\CallbackReceived;
// In a test or tinker session
Event::dispatch(new CallbackReceived([
'GTBTransId' => '12345',
'PartnerTransId' => '67890',
'Status' => 'COMPLETED',
'Message' => 'Test transaction',
], 'banktrf'));
Or send test HTTP requests:
curl -X POST https://yourdomain.com/gtb-api/callback \
-H "Content-Type: application/json" \
-d '{
"SecureHash": "test-hash",
"GTBTransId": "12345",
"PartnerTransId": "67890",
"Status": "COMPLETED",
"Message": "Test transaction"
}'
Testing
To run the tests:
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The GNU AFFERO GENERAL PUBLIC LICENSE. Please see License File for more information.