Overview
The products endpoint returns available internet recharge cards for ADSL or 4G LTE services with real-time pricing and stock availability. Products should be cached briefly to minimize API calls.
Cache product data for 5-10 minutes . Stock levels change more frequently than gift cards.
API Reference
GET /v3/internet/products Complete endpoint documentation
Basic Product Loading
async function loadInternetProducts ( type ) {
if ( ! [ 'ADSL' , '4G' ]. includes ( type )) {
throw new Error ( 'Invalid type. Must be ADSL or 4G' );
}
const response = await fetch (
`https://api.oneclickdz.com/v3/internet/products?type= ${ type } ` ,
{
headers: {
"X-Access-Token" : process . env . API_KEY ,
},
}
);
if ( ! response . ok ) {
throw new Error ( `Failed to load products: ${ response . status } ` );
}
const result = await response . json ();
return result . data . products ;
}
// Usage
const adslProducts = await loadInternetProducts ( 'ADSL' );
const lteProducts = await loadInternetProducts ( '4G' );
console . log ( `ADSL: ${ adslProducts . length } products` );
console . log ( `4G: ${ lteProducts . length } products` );
Response Structure
{
"success" : true ,
"data" : {
"products" : [
{
"value" : 500 ,
"cost" : 490 ,
"available" : true
},
{
"value" : 1000 ,
"cost" : 980 ,
"available" : true
},
{
"value" : 2000 ,
"cost" : 1960 ,
"available" : true
},
{
"value" : 5000 ,
"cost" : 4900 ,
"available" : false
}
]
},
"meta" : {
"timestamp" : "2025-11-01T12:00:00.000Z" ,
"type" : "ADSL"
}
}
Caching Implementation
class ProductCache {
constructor ( ttlMinutes = 10 ) {
this . cache = new Map ();
this . ttl = ttlMinutes * 60 * 1000 ;
}
async get ( type ) {
const cached = this . cache . get ( type );
const now = Date . now ();
// Return cached if still valid
if ( cached && ( now - cached . timestamp ) < this . ttl ) {
console . log ( `Returning cached ${ type } products` );
return cached . data ;
}
// Fetch fresh data
console . log ( `Fetching fresh ${ type } products` );
const products = await loadInternetProducts ( type );
this . cache . set ( type , {
data: products ,
timestamp: now ,
});
return products ;
}
invalidate ( type ) {
if ( type ) {
this . cache . delete ( type );
} else {
this . cache . clear ();
}
}
}
// Usage
const cache = new ProductCache ( 10 ); // 10 minutes TTL
// First call - fetches from API
const adsl1 = await cache . get ( 'ADSL' );
// Second call within 10 minutes - returns cached
const adsl2 = await cache . get ( 'ADSL' );
// Different type - fetches separately
const lte = await cache . get ( '4G' );
// Force refresh
cache . invalidate ( 'ADSL' );
const adsl3 = await cache . get ( 'ADSL' ); // Fetches fresh
Filtering Available Products
Only show products that are in stock:
function getAvailableProducts ( products ) {
return products . filter ( p => p . available );
}
// Usage
const allProducts = await cache . get ( 'ADSL' );
const available = getAvailableProducts ( allProducts );
console . log ( ` ${ available . length } / ${ allProducts . length } products in stock` );
if ( available . length === 0 ) {
console . log ( 'No products available at the moment' );
}
Applying Customer Markup
Add your profit margin to wholesale prices:
function applyMarkup ( products , markupPercent = 5 ) {
return products
. filter ( p => p . available )
. map ( p => ({
value: p . value ,
wholesaleCost: p . cost , // Store but don't show
customerPrice: Math . ceil ( p . cost * ( 1 + markupPercent / 100 )),
available: true ,
}));
}
// Usage
const products = await cache . get ( 'ADSL' );
const customerPrices = applyMarkup ( products , 5 ); // 5% markup
customerPrices . forEach ( p => {
console . log ( ` ${ p . value } DA card: ${ p . customerPrice } DA` );
// Don't log wholesaleCost in production!
});
Building Product UI
Example structure for displaying products to customers:
async function buildProductOptions ( type , markupPercent = 5 ) {
const products = await cache . get ( type );
return {
serviceType: type ,
serviceName: type === 'ADSL' ? 'ADSL Internet' : '4G LTE Internet' ,
options: products
. filter ( p => p . available )
. map ( p => ({
value: p . value ,
label: ` ${ p . value } DA` ,
price: Math . ceil ( p . cost * ( 1 + markupPercent / 100 )),
inStock: true ,
}))
. sort (( a , b ) => a . value - b . value ) // Sort by value ascending
};
}
// Usage
const adslOptions = await buildProductOptions ( 'ADSL' , 5 );
const lteOptions = await buildProductOptions ( '4G' , 5 );
// Returns UI-ready structure:
// {
// serviceType: 'ADSL',
// serviceName: 'ADSL Internet',
// options: [
// { value: 500, label: '500 DA', price: 515, inStock: true },
// { value: 1000, label: '1000 DA', price: 1029, inStock: true },
// ...
// ]
// }
Loading Both Service Types
Fetch ADSL and 4G products in parallel:
async function loadAllInternetProducts () {
const [ adsl , lte ] = await Promise . all ([
cache . get ( 'ADSL' ),
cache . get ( '4G' ),
]);
return {
adsl: applyMarkup ( adsl , 5 ),
lte: applyMarkup ( lte , 5 ),
};
}
// Usage
const allProducts = await loadAllInternetProducts ();
console . log ( 'ADSL:' , allProducts . adsl . length , 'products' );
console . log ( '4G:' , allProducts . lte . length , 'products' );
Best Practices
Cache 5-10 Minutes Balance freshness with API load reduction
Filter Available Only Only display products where available: true
Hide Wholesale Prices Never show raw cost values to customers
Handle Errors Gracefully Fall back to cached data if API is temporarily unavailable
Error Handling
async function loadProductsSafely ( type ) {
try {
return await cache . get ( type );
} catch ( error ) {
console . error ( `Failed to load ${ type } products:` , error );
// Return stale cached data if available
const cached = cache . cache . get ( type );
if ( cached ) {
console . log ( 'Using stale cached data' );
return cached . data ;
}
throw new Error ( ` ${ type } products unavailable` );
}
}
Next Steps