ghanem / dtone
A package that provides an interface between Laravel and DT One DVS API
Requires
- php: ^7.2.5|^8.0
- guzzlehttp/guzzle: ^6.3|^7.0
- illuminate/http: ^7.0|^8.0|^9.0|^10.0|^11.0|^12.0
- illuminate/support: ^7.0|^8.0|^9.0|^10.0|^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^5.0|^6.0|^7.0|^8.0|^9.0|^10.0
- phpunit/phpunit: ^8.4|^9.0|^10.0|^11.0
README
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.