ghanem/dtone
Laravel Dtone
A package that provides an interface between Laravel and DT One DVS API.
Requirements
- PHP ^7.2.5 | ^8.0
- Laravel ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0 | ^12.0
Installation
You can install the package via composer:
composer require ghanem/dtone
Publish the config file:
php artisan vendor:publish --provider="Ghanem\Dtone\DtoneServiceProvider" --tag="config"
Configuration
Add the following to your .env file:
DTONE_KEY=your-production-api-key
DTONE_SECRET=your-production-api-secret
DTONE_TEST_KEY=your-sandbox-api-key
DTONE_TEST_SECRET=your-sandbox-api-secret
DTONE_IS_PRODUCTION=false
DTONE_RETRIES=0
DTONE_RETRY_DELAY=100
Set DTONE_IS_PRODUCTION=true when you are ready to use the production API.
Retry Configuration
You can enable automatic retries for failed requests:
DTONE_RETRIES=3
DTONE_RETRY_DELAY=100
This will retry failed requests up to 3 times with a 100ms delay between attempts.
Usage
You can use the Dtone facade or resolve it from the container.
use Ghanem\Dtone\Facades\Dtone;
Services
// List all services (returns PaginatedResponse of Service DTOs)
$services = Dtone::services($page, $per_page);
$services->getData(); // array of Service DTOs
$services->getMeta()->getTotal(); // total count
// Get a service by ID
$service = Dtone::serviceById(1);
$service->getId();
$service->getName();
Countries
// List all countries
$countries = Dtone::countries($page, $per_page);
// Get a country by ISO code
$country = Dtone::countryByIsoCode('US');
$country->getIsoCode(); // 'US'
$country->getName(); // 'United States'
$country->getRegions(); // array
Operators
// List all operators (optionally filter by country)
$operators = Dtone::operators('US', $page, $per_page);
// Get an operator by ID
$operator = Dtone::operatorById(5);
$operator->getId();
$operator->getName();
$operator->getCountry(); // Country DTO
$operator->getCountry()->getName(); // nested access
// Lookup operators by mobile number
$result = Dtone::lookupOperatorsByMobileNumber('+1234567890');
Products
// List products with filters
$products = Dtone::products(
$type, // e.g. 'FIXED_VALUE_RECHARGE'
$service_id, // e.g. 1
$country_iso_code, // e.g. 'US'
$benefit_types, // e.g. ['Airtime']
$page,
$per_page
);
// Get a product by ID
$product = Dtone::productById(99);
$product->getId();
$product->getType();
$product->getService(); // Service DTO
$product->getOperator(); // Operator DTO
$product->getAttribute('prices'); // raw array for nested fields
Campaigns
// List active campaigns
$campaigns = Dtone::campaigns($page, $per_page);
// Get a campaign by ID
$campaign = Dtone::campaignById(7);
$campaign->getId();
$campaign->getName();
Promotions
// List promotions
$promotions = Dtone::promotions($page, $per_page);
// Get a promotion by ID
$promotion = Dtone::promotionById(3);
$promotion->getOperator(); // Operator DTO or null
Benefit Types
// List all benefit types
$benefitTypes = Dtone::benefitTypes($page, $per_page);
Balances
$balances = Dtone::balances(); // array of Balance DTOs
foreach ($balances as $balance) {
$balance->getAmount(); // 150.50
$balance->getCurrency(); // 'USD'
}
Transactions
// List transactions
$transactions = Dtone::transactions($page, $per_page);
// Get a transaction by ID
$transaction = Dtone::transactionById(456);
$transaction->getId();
$transaction->getStatus();
$transaction->getExternalId();
$transaction->getAttribute('product'); // raw array
// Create a transaction (async)
$transaction = Dtone::createTransaction(
'external-id-123', // external_id
99, // product_id
['mobile_number' => '+1234567890'], // credit_party_identifier
false // auto_confirm (default: false)
);
// Create a transaction (sync - waits for completion)
$transaction = Dtone::createTransactionSync(
'external-id-123',
99,
['mobile_number' => '+1234567890'],
true // auto_confirm
);
// Confirm a transaction (async)
$confirmed = Dtone::confirmTransaction($transaction_id);
// Confirm a transaction (sync - waits for completion)
$confirmed = Dtone::confirmTransactionSync($transaction_id);
// Cancel a transaction
$cancelled = Dtone::cancelTransaction($transaction_id);
Lookups
// Lookup operators by mobile number
$operators = Dtone::lookupOperatorsByMobileNumber('+1234567890');
// Statement inquiry
$statement = Dtone::statementInquiry(
99, // product_id
['account_number' => '123456'] // credit_party_identifier
);
// Get remaining benefits for a credit party
$benefits = Dtone::creditPartyBenefits(
99, // product_id
['mobile_number' => '+1234567890'] // credit_party_identifier
);
// Get status for a credit party
$status = Dtone::creditPartyStatus(
99, // product_id
['mobile_number' => '+1234567890'] // credit_party_identifier
);
Response DTOs
All API responses are returned as typed DTO objects. Every DTO has a toArray() method for backward compatibility:
$service = Dtone::serviceById(1);
$service->toArray(); // ['id' => 1, 'name' => 'Mobile']
$response = Dtone::services();
$response->toArray(); // ['data' => [...], 'meta' => [...]]
Available DTOs: Service, Country, Operator, Product, Balance, Transaction, Campaign, Promotion, BenefitType, PaginatedResponse, Meta.
Paginated Responses
List endpoints return a PaginatedResponse DTO:
$response = Dtone::services(1, 10);
$response->getData(); // array of DTOs
$response->getMeta()->getTotal(); // total items
$response->getMeta()->getTotalPages(); // total pages
$response->getMeta()->getPage(); // current page
$response->getMeta()->getPerPage(); // items per page
$response->getMeta()->getNextPage(); // next page number
$response->getMeta()->getPrevPage(); // previous page number
Webhooks
The package automatically registers a webhook endpoint to receive DT One transaction callbacks.
Configuration
DTONE_WEBHOOK_PATH=dtone/webhook
DTONE_WEBHOOK_SECRET=your-webhook-secret
DTONE_WEBHOOK_LOGGING=false
You can also configure middleware in config/dtone.php:
'webhook_middleware' => ['api'],
Set webhook_path to null to disable the webhook route.
Events
The webhook controller dispatches the following events:
| Event | Dispatched when |
|---|---|
TransactionStatusChanged |
Every webhook (always dispatched) |
TransactionCompleted |
Status is COMPLETED |
TransactionFailed |
Status is FAILED or DECLINED |
TransactionConfirmed |
Status is CONFIRMED |
TransactionCancelled |
Status is CANCELLED |
Listen to events in your EventServiceProvider:
use Ghanem\Dtone\Events\TransactionCompleted;
protected $listen = [
TransactionCompleted::class => [
YourTransactionCompletedListener::class,
],
];
Each event has $payload, $status, and $transactionId properties:
public function handle(TransactionCompleted $event)
{
$event->transactionId; // 123
$event->status; // 'COMPLETED'
$event->payload; // full webhook payload array
}
Signature Verification
If DTONE_WEBHOOK_SECRET is set, the VerifyWebhookSignature middleware is available:
// config/dtone.php
'webhook_middleware' => [
\Ghanem\Dtone\Http\Middleware\VerifyWebhookSignature::class,
],
This verifies the X-Dtone-Signature header using HMAC-SHA256.
Laravel Notifications Channel
Send top-ups as Laravel notifications using the DtoneChannel:
use Ghanem\Dtone\Notifications\DtoneChannel;
use Ghanem\Dtone\Notifications\DtoneMessage;
use Illuminate\Notifications\Notification;
class SendAirtimeNotification extends Notification
{
public function via($notifiable)
{
return [DtoneChannel::class];
}
public function toDtone($notifiable)
{
return DtoneMessage::create(99) // product_id
->externalId('order-123') // optional external ID
->toMobileNumber('+1234567890') // recipient
->autoConfirm(); // auto-confirm the transaction
}
}
DtoneMessage API
DtoneMessage::create($productId) // create with product ID
->externalId('ext-123') // set external ID (auto-generated if not set)
->toMobileNumber('+1234567890') // set mobile number recipient
->to(['account_number' => '123456']) // or set custom identifier
->autoConfirm() // enable auto-confirm
->sync(); // use synchronous API (default: async)
On-Demand Notifications
use Illuminate\Support\Facades\Notification;
Notification::route(DtoneChannel::class, null)
->notify(new SendAirtimeNotification());
Caching
Discovery endpoints (services, countries, operators, products, campaigns, promotions, benefit-types) can be cached to reduce API calls:
DTONE_CACHE_TTL=3600
Override TTL per endpoint:
DTONE_CACHE_TTL_SERVICES=7200
DTONE_CACHE_TTL_COUNTRIES=86400
DTONE_CACHE_TTL_OPERATORS=3600
DTONE_CACHE_TTL_PRODUCTS=1800
Set DTONE_CACHE_TTL=0 (default) to disable caching. Transactions, balances, and lookups are never cached.
Clear cache programmatically:
use Ghanem\Dtone\Request;
Request::clearCache(); // clear all DT One cache
Request::clearCache('services'); // clear specific endpoint cache
Artisan Commands
| Command | Description |
|---|---|
dtone:balance |
Display account balances |
dtone:products |
List products (supports --country, --type, --service, --page, --per-page) |
dtone:transaction {id?} |
List transactions or get details by ID |
dtone:cache-clear {endpoint?} |
Clear DT One cache (all or specific endpoint) |
dtone:health |
Check API connectivity, balances, and service availability |
Examples:
php artisan dtone:balance
php artisan dtone:products --country=US --type=FIXED_VALUE_RECHARGE
php artisan dtone:transaction 456
php artisan dtone:cache-clear services
php artisan dtone:health
Done
- Services (list, get by ID)
- Countries (list, get by ISO code)
- Operators (list, get by ID)
- Products (list with filters, get by ID)
- Campaigns (list, get by ID)
- Promotions (list, get by ID)
- Benefit types (list)
- Balances
- Transactions (list, get by ID, create async/sync, confirm async/sync, cancel)
- Lookup operators by mobile number
- Statement inquiry lookup
- Credit party lookup (remaining benefits, status)
- Pagination support with meta data
- Production / Sandbox environment switching
- Retry mechanism for failed requests
- Response DTOs (typed objects with toArray() compatibility)
- Webhook / Callback support with event dispatching
- Signature verification middleware
- Laravel notifications channel integration
- Caching layer for discovery endpoints
- Configurable cache TTL per endpoint
- Rate limiting awareness (respects
X-RateLimitheaders) - Artisan commands (
dtone:balance,dtone:products,dtone:transaction,dtone:cache-clear,dtone:health) - Health check command
- Test suite (110 tests, 248 assertions)
- Support for Laravel 7 - 12
Roadmap
- Batch transactions support
- Auto-paginate helper (fetch all pages automatically)
- Webhook queue integration (dispatch events to queue)
Testing
composer test
License
The MIT License (MIT). Please see License File for more information.