Documentation J+SERVICES Guides Référence API

Contrat API — Gestion des profils de vente TiketMOMO (app mobile)

Version : 1.0 — 2026-06-23 Backend : jservices-api (déployé) Pour : équipe app mobile MikhmoAI

But

Permettre au manager, depuis l’app mobile, de piloter ce qu’il vend sans attendre la synchronisation automatique depuis le routeur (cron toutes les 30 min) :

⚠️ Changement important : avant cette version, l’endpoint de sync écrivait dans un champ que rien ne lisait → aucun effet réel sur la vente. C’est désormais effectif : ce que l’app envoie est respecté par le catalogue acheteur ET par le moteur de stock (plus de re-création de stock pour un profil caché).

Généralités


1. Lister les profils de vente courants

GET /api/v1/clients/store/profiles

Réponse 200

{
  "success": true,
  "data": [
    {
      "name": "500F-7d-10G",
      "price": 500,
      "data_limit": "10G",
      "time_limit": "7d",
      "validity": null,
      "voucher_format": "vc",
      "is_active": true
    }
  ]
}

data reflète la dernière liste poussée par l’app (vide si jamais synchronisé).


2. Synchroniser / mettre à jour / retirer des profils ⭐ endpoint principal

POST /api/v1/clients/store/profiles/sync

Corps

{
  "nas_id": "HM40BD8YVTG",
  "profiles": [
    {
      "name": "500F-7d-10G",
      "price": 500,
      "data_limit": "10G",
      "time_limit": "7d",
      "voucher_format": "vc",
      "is_active": true
    },
    {
      "name": "1000F-30d-50G",
      "price": 1200,
      "data_limit": "50G",
      "time_limit": "30d",
      "display_name": "Pack Pro",
      "is_active": true
    },
    { "name": "200F-1d", "is_active": false },
    { "name": "ancien-profil", "action": "delete" }
  ],
  "metadata": { "source": "mobile", "app_version": "x.y.z" }
}

Champs par profil

Champ Type Rôle
name string (requis) Nom EXACT du profil hotspot (clé d’identité, ex. 500F-7d-10G).
price number Prix de vente (override le prix dérivé du nom/config).
data_limit string Limite data affichée (ex. 10G).
time_limit string Durée (ex. 7d).
validity string Validité éventuelle.
voucher_format string Optionnel. vc = code unique ; up = utilisateur + mot de passe. Stocké sur store_settings.sales_profiles[] et utilisé par le stock TiketMOMO quand il génère des coupons locaux.
display_name string Libellé affiché à l’acheteur (sinon = name).
is_active bool false = retiré de la vente (caché du catalogue + non réapprovisionné). Défaut true.
action string "delete" = oublie l’entrée (le profil redevient auto-détecté depuis le routeur s’il y existe).

Sémantique à retenir (important pour l’UX)

Réponse 200

{
  "success": true,
  "message": "Profils de la boutique mis à jour avec la source de vérité locale.",
  "updated_count": 2,
  "deleted_count": 1
}

Erreurs : 400 { success:false, error } si profiles n’est pas un tableau.


3. Vérifier les profils RÉELS du routeur (contrôle d’écart)

GET /api/v1/clients/router/hotspot-profiles?force=true&allocation_id=<optionnel>

Lit les profils en direct depuis le routeur (sans force=true : dernier état connu côté backend). À utiliser pour comparer ce qui est sur le routeur vs ce que le manager vend, et proposer une synchro.

Réponse 200

{ "success": true, "result": [{ "name": "500F-7d-10G", "rate-limit": "…", "...": "…" }] }

4. Config boutique (branding, prix, libellés avancés)

GET   /api/v1/clients/store-config
PUT   /api/v1/clients/store-config
PATCH /api/v1/clients/store-config

Gère store_settings.portal_editor (branding, plans détaillés, paiement) + display_name, logo_url, store_slug. Complémentaire au point 2 : utiliser le point 2 pour le pilotage rapide des profils de vente, le point 4 pour la config riche de la boutique.


Flux recommandé côté app

  1. Au besoin, GET /router/hotspot-profiles?force=true pour lire les profils réels.
  2. L’utilisateur ajuste (prix, libellé, masquer/afficher).
  3. POST /store/profiles/sync avec la liste complète des profils gérés.
  4. Effet immédiat : le catalogue public (/store/:slug) et le réapprovisionnement de stock respectent exclusions + overrides.

Notes techniques (côté backend, pour info)