ghanem/dtone
Laravel package providing a clean interface to the DT One DVS API. Configure sandbox/production credentials via .env, optional request retries, and use the Dtone facade to browse services, countries, and operators with paginated DTO responses.
A package that provides an interface between Laravel and DT One DVS API.
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"
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.
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.
You can use the Dtone facade or resolve it from the container.
use Ghanem\Dtone\Facades\Dtone;
// 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();
// 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
// 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');
// 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
// List active campaigns
$campaigns = Dtone::campaigns($page, $per_page);
// Get a campaign by ID
$campaign = Dtone::campaignById(7);
$campaign->getId();
$campaign->getName();
// List promotions
$promotions = Dtone::promotions($page, $per_page);
// Get a promotion by ID
$promotion = Dtone::promotionById(3);
$promotion->getOperator(); // Operator DTO or null
// List all benefit types
$benefitTypes = Dtone::benefitTypes($page, $per_page);
$balances = Dtone::balances(); // array of Balance DTOs
foreach ($balances as $balance) {
$balance->getAmount(); // 150.50
$balance->getCurrency(); // 'USD'
}
// 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);
// 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
);
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.
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
The package automatically registers a webhook endpoint to receive DT One transaction callbacks.
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.
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
}
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.
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::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)
use Illuminate\Support\Facades\Notification;
Notification::route(DtoneChannel::class, null)
->notify(new SendAirtimeNotification());
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
| 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
X-RateLimit headers)dtone:balance, dtone:products, dtone:transaction, dtone:cache-clear, dtone:health)composer test
The MIT License (MIT). Please see License File for more information.
How can I help you explore Laravel packages today?