Vue d’ensemble
L’endpoint des produits retourne les cartes de recharge internet disponibles pour les services ADSL ou 4G LTE avec les tarifs en temps réel et la disponibilité du stock. Les produits doivent être mis en cache brièvement pour minimiser les appels API.Mettez en cache les données de produit pendant 5-10 minutes. Les niveaux de stock changent plus fréquemment que pour les cartes-cadeaux.
Référence API
GET /v3/internet/products
Documentation complète de l’endpoint
Chargement de base des produits
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`);
import requests
import os
def load_internet_products(service_type):
if service_type not in ['ADSL', '4G']:
raise ValueError('Invalid type. Must be ADSL or 4G')
response = requests.get(
'https://api.oneclickdz.com/v3/internet/products',
headers={'X-Access-Token': os.getenv('API_KEY')},
params={'type': service_type}
)
response.raise_for_status()
return response.json()['data']['products']
# Usage
adsl_products = load_internet_products('ADSL')
lte_products = load_internet_products('4G')
print(f"ADSL: {len(adsl_products)} products")
print(f"4G: {len(lte_products)} products")
<?php
function loadInternetProducts($type) {
if (!in_array($type, ['ADSL', '4G'])) {
throw new InvalidArgumentException('Invalid type. Must be ADSL or 4G');
}
$url = "https://api.oneclickdz.com/v3/internet/products?type={$type}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Access-Token: ' . getenv('API_KEY')
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception("Failed to load products: " . $httpCode);
}
$result = json_decode($response, true);
return $result['data']['products'];
}
// Usage
$adslProducts = loadInternetProducts('ADSL');
$lteProducts = loadInternetProducts('4G');
echo "ADSL: " . count($adslProducts) . " products\n";
echo "4G: " . count($lteProducts) . " products\n";
?>
# Produits ADSL
curl "https://api.oneclickdz.com/v3/internet/products?type=ADSL" \
-H "X-Access-Token: YOUR_API_KEY"
# Produits 4G
curl "https://api.oneclickdz.com/v3/internet/products?type=4G" \
-H "X-Access-Token: YOUR_API_KEY"
Structure de la réponse
{
"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"
}
}
Implémentation du cache
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); // TTL 10 minutes
// First call - fetches from API
const adsl1 = await cache.get('ADSL');
// Second call within 10 minutes - returns cached
const adsl2 = await cache.get('ADSL');
// Type différent - récupère séparément
const lte = await cache.get('4G');
// Force refresh
cache.invalidate('ADSL');
const adsl3 = await cache.get('ADSL'); // Récupère de nouvelles données
from datetime import datetime, timedelta
import requests
import os
class ProductCache:
def __init__(self, ttl_minutes=10):
self.cache = {}
self.ttl = timedelta(minutes=ttl_minutes)
def get(self, service_type):
cached = self.cache.get(service_type)
now = datetime.now()
# Return cached if still valid
if cached and (now - cached['timestamp']) < self.ttl:
print(f"Returning cached {service_type} products")
return cached['data']
# Fetch fresh data
print(f"Fetching fresh {service_type} products")
products = load_internet_products(service_type)
self.cache[service_type] = {
'data': products,
'timestamp': now
}
return products
def invalidate(self, service_type=None):
if service_type:
self.cache.pop(service_type, None)
else:
self.cache.clear()
# Usage
cache = ProductCache(ttl_minutes=10)
adsl1 = cache.get('ADSL')
adsl2 = cache.get('ADSL')
lte = cache.get('4G')
cache.invalidate('ADSL')
adsl3 = cache.get('ADSL')
<?php
class ProductCache {
private $cache = [];
private $ttl;
public function __construct($ttlMinutes = 10) {
$this->ttl = $ttlMinutes * 60;
}
public function get($type) {
$now = time();
// Return cached if still valid
if (isset($this->cache[$type]) &&
($now - $this->cache[$type]['timestamp']) < $this->ttl) {
error_log("Returning cached {$type} products");
return $this->cache[$type]['data'];
}
// Fetch fresh data
error_log("Fetching fresh {$type} products");
$products = loadInternetProducts($type);
$this->cache[$type] = [
'data' => $products,
'timestamp' => $now
];
return $products;
}
public function invalidate($type = null) {
if ($type) {
unset($this->cache[$type]);
} else {
$this->cache = [];
}
}
}
// Usage
$cache = new ProductCache(10);
$adsl1 = $cache->get('ADSL');
$adsl2 = $cache->get('ADSL');
$lte = $cache->get('4G');
$cache->invalidate('ADSL');
$adsl3 = $cache->get('ADSL');
?>
Filtrage des produits disponibles
N’afficher que les produits en 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');
}
def get_available_products(products):
return [p for p in products if p['available']]
# Usage
all_products = cache.get('ADSL')
available = get_available_products(all_products)
print(f"{len(available)}/{len(all_products)} products in stock")
if not available:
print('No products available at the moment')
<?php
function getAvailableProducts($products) {
return array_filter($products, function($p) {
return $p['available'];
});
}
// Usage
$allProducts = $cache->get('ADSL');
$available = array_values(getAvailableProducts($allProducts));
echo count($available) . "/" . count($allProducts) . " products in stock\n";
if (empty($available)) {
echo "No products available at the moment\n";
}
?>
Application de la marge client
Ajoutez votre marge bénéficiaire aux prix de gros :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`);
// Ne pas journaliser wholesaleCost en production !
});
import math
def apply_markup(products, markup_percent=5):
available = [p for p in products if p['available']]
return [
{
'value': p['value'],
'wholesaleCost': p['cost'], # Store but don't show
'customerPrice': math.ceil(p['cost'] * (1 + markup_percent / 100)),
'available': True
}
for p in available
]
# Usage
products = cache.get('ADSL')
customer_prices = apply_markup(products, markup_percent=5)
for p in customer_prices:
print(f"{p['value']} DA card: {p['customerPrice']} DA")
<?php
function applyMarkup($products, $markupPercent = 5) {
$available = array_filter($products, function($p) {
return $p['available'];
});
return array_map(function($p) use ($markupPercent) {
return [
'value' => $p['value'],
'wholesaleCost' => $p['cost'], // Store but don't show
'customerPrice' => ceil($p['cost'] * (1 + $markupPercent / 100)),
'available' => true
];
}, $available);
}
// Usage
$products = $cache->get('ADSL');
$customerPrices = applyMarkup($products, 5); // 5% markup
foreach ($customerPrices as $p) {
echo "{$p['value']} DA card: {$p['customerPrice']} DA\n";
}
?>
Construction de l’Interface Produit
Exemple de structure pour afficher les produits aux clients :async function buildProductOptions(type, markupPercent = 5) {
const products = await cache.get(type);
return {
serviceType: type,
serviceName: type === 'ADSL' ? 'Internet ADSL' : 'Internet 4G LTE',
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) // Trier par valeur croissante
};
}
// Utilisation
const adslOptions = await buildProductOptions('ADSL', 5);
const lteOptions = await buildProductOptions('4G', 5);
// Retourne une structure prête pour l'interface :
// {
// serviceType: 'ADSL',
// serviceName: 'Internet ADSL',
// options: [
// { value: 500, label: '500 DA', price: 515, inStock: true },
// { value: 1000, label: '1000 DA', price: 1029, inStock: true },
// ...
// ]
// }
Chargement des deux types de service
Récupérez les produits ADSL et 4G en parallèle :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');
Bonnes pratiques
Cache 5-10 minutes
Équilibre entre fraîcheur et réduction de la charge API
Filtrer les disponibles uniquement
N’afficher que les produits où
available: trueMasquer les prix de gros
Ne jamais afficher les valeurs brutes de
cost aux clientsGérer les erreurs gracieusement
Revenir aux données en cache si l’API est temporairement indisponible
Étapes suivantes
Valider les numéros
Vérifier les numéros de téléphone avant la commande
Envoyer les recharges
Soumettre des commandes avec gestion des erreurs
Référence API
Documentation complète de l’endpoint
Vue d'ensemble
Retour à la vue d’ensemble de l’intégration
Suivre le statut
Surveiller l’exécution des commandes
Livrer les cartes
Livrer les codes de carte en toute sécurité

