> ## Documentation Index
> Fetch the complete documentation index at: https://docs.oneclickdz.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Chargement des Produits Internet

> Récupérer et mettre en cache les cartes internet ADSL et 4G LTE disponibles

## 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.

<Info>
  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.
</Info>

## Référence API

<Card title="GET /v3/internet/products" icon="list" href="/fr/api-reference/internet/list-products">
  Documentation complète de l'endpoint
</Card>

## Chargement de base des produits

<CodeGroup>
  ```javascript Node.js theme={null}
  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`);
  ```

  ```python Python theme={null}
  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 PHP theme={null}
  <?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";
  ?>
  ```

  ```bash cURL theme={null}
  # 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"
  ```
</CodeGroup>

## Structure de la réponse

```json theme={null}
{
  "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

<CodeGroup>
  ```javascript Node.js theme={null}
  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
  ```

  ```python Python theme={null}
  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 PHP theme={null}
  <?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');
  ?>
  ```
</CodeGroup>

## Filtrage des produits disponibles

N'afficher que les produits en stock :

<CodeGroup>
  ```javascript Node.js theme={null}
  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');
  }
  ```

  ```python Python theme={null}
  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 PHP theme={null}
  <?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";
  }
  ?>
  ```
</CodeGroup>

## Application de la marge client

Ajoutez votre marge bénéficiaire aux prix de gros :

<CodeGroup>
  ```javascript Node.js theme={null}
  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 !
  });
  ```

  ```python Python theme={null}
  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 PHP theme={null}
  <?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";
  }
  ?>
  ```
</CodeGroup>

## Construction de l'Interface Produit

Exemple de structure pour afficher les produits aux clients :

```javascript theme={null}
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 :

```javascript theme={null}
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

<CardGroup cols={2}>
  <Card title="Cache 5-10 minutes" icon="clock">
    Équilibre entre fraîcheur et réduction de la charge API
  </Card>

  <Card title="Filtrer les disponibles uniquement" icon="filter">
    N'afficher que les produits où `available: true`
  </Card>

  <Card title="Masquer les prix de gros" icon="eye-slash">
    Ne jamais afficher les valeurs brutes de `cost` aux clients
  </Card>

  <Card title="Gérer les erreurs gracieusement" icon="triangle-exclamation">
    Revenir aux données en cache si l'API est temporairement indisponible
  </Card>
</CardGroup>

## Étapes suivantes

<CardGroup cols={3}>
  <Card title="Valider les numéros" icon="check" href="/fr/internet-topup-guides/2-validation">
    Vérifier les numéros de téléphone avant la commande
  </Card>

  <Card title="Envoyer les recharges" icon="paper-plane" href="/fr/internet-topup-guides/3-sending-topups">
    Soumettre des commandes avec gestion des erreurs
  </Card>

  <Card title="Référence API" icon="book" href="/fr/api-reference/internet/list-products">
    Documentation complète de l'endpoint
  </Card>

  <Card title="Vue d'ensemble" icon="book-open" href="/fr/internet-topup-guides/overview">
    Retour à la vue d'ensemble de l'intégration
  </Card>

  <Card title="Suivre le statut" icon="chart-line" href="/fr/internet-topup-guides/4-status-tracking">
    Surveiller l'exécution des commandes
  </Card>

  <Card title="Livrer les cartes" icon="file-code" href="/fr/internet-topup-guides/5-card-delivery">
    Livrer les codes de carte en toute sécurité
  </Card>
</CardGroup>
