Ce brief décrit le contrat frontend DiddiFood vérifié le 22 octobre 2026. Il s'adresse aux développeurs client, Restaurant Pro, QA et intégrateurs DiddiFreeID.
Brief frontend DiddiFood — v2026.10.22
1. Résultat de la vérification
- Contrat local après le lot client 2 :
docs/api/openapi.json, 70 routes. - Staging vérifié :
https://diddifood-backend-staging.diddifree.com. /health,/docs,/openapi.jsonet/metricsrépondent HTTP 200.- L'OpenAPI staging contient 66 routes. Il manque actuellement :
POST /food/v1/internal/backoffice/refund-requests/{refund_request_id}/approve;POST /food/v1/internal/backoffice/refund-requests/{refund_request_id}/reject;GET /internal/v1/support/order/{order_id}/context.- Le contrat local ajoute
POST /food/v1/restaurants/{restaurant_id}/quote. Il calcule le sous-total, le frais Food configuré et le total XOF avant création de commande. - Le devis utilise encore
DIDDIFOOD_DELIVERY_FEE_XOF. Il ne remplace pas encore un devis dynamique DiddiSend. - Les statuts de livraison sont maintenant documentés dans OpenAPI :
requested,assigned,picked_up,in_transit,deliveredetfailed. - La liste publique des restaurants répond 200. Le restaurant QA contrôlé avait
image_url: nullet un menu vide. Le frontend doit donc prévoir un fallback image et un état catalogue vide.
Le contrat local est la référence de développement. Pour une recette staging, le frontend doit utiliser uniquement les routes réellement présentes dans l'OpenAPI staging jusqu'au prochain déploiement.
2. Adresse et authentification
API staging: https://diddifood-backend-staging.diddifree.com
Identité staging: https://auth-staging.diddifree.com
Les routes privées utilisent :
Authorization: Bearer <JWT DiddiFreeID>
Le frontend ne déduit jamais le rôle Pro d'un rôle global du JWT. Il appelle GET /food/v1/me/restaurants, puis utilise le role et le restaurant.id renvoyés par Food. Les rôles manager et kitchen appartiennent à DiddiFood.
3. Conventions d'affichage
- Devise :
XOF(franc CFA). Les montants sont des entiers*_minor_units; les afficher comme des francs XOF sans inventer de décimales. - Les dates sont ISO 8601, souvent en UTC (
Z). Les afficher dans le fuseauAfrica/Abidjan. image_urletlogo_file_id/photo_file_idsont optionnels. Utiliserimage_urlquand elle est renseignée; ne jamais construire une URL à partir d'unfile_id.is_open,rating,review_count,delivery_time_minutesetdelivery_fee_minor_unitspeuvent êtrenull.
4. Parcours client ordinaire
Catalogue
GET /food/v1/restaurants?limit=20&offset=0.GET /food/v1/restaurants/{restaurant_id}pour le détail.GET /food/v1/restaurants/{restaurant_id}/menupour les plats disponibles.- Afficher
price_minor_units,currency,is_available,image_url, l'ouverture et le délai estimé.
Devis avant commande
Appeler POST /food/v1/restaurants/{restaurant_id}/quote avec items,
pickup_mode et les coordonnées de livraison. Afficher subtotal_minor_units,
delivery_fee_minor_units et total_minor_units renvoyés par Food. Ne pas
recalculer ces montants dans le frontend.
Création de commande
Pour le MVP, envoyer pickup_mode=delivery et l'un des deux moyens de paiement acceptés :
{
"items": [{"dish_id": "<uuid>", "quantity": 2}],
"payment_method": "cash_on_delivery",
"pickup_mode": "delivery",
"dropoff_address": "Cocody, Abidjan",
"dropoff_latitude": 5.3611,
"dropoff_longitude": -4.0079,
"recipient_name": "Nom du destinataire",
"recipient_phone": "+2250700000000"
}
POST /food/v1/orders exige au moins un article. Pour la livraison, l'adresse, les coordonnées et les contacts du destinataire sont nécessaires. payment_method accepte exactement cash_on_delivery ou online.
Paiement
- Cash : afficher l'état de paiement depuis
GET /food/v1/orders/{order_id}/payment; ne pas considérer la commandereadycomme un paiement confirmé. - En ligne : appeler
POST /food/v1/orders/{order_id}/payments/prepareavec{ "customer_email": "..." }. Ouvrirnext_action_urlsi elle est fournie, puis appelerGET /paymentouPOST /payment/refreshpour la confirmation. - Le client choisit « paiement en ligne »; il ne choisit pas Orange Money, Wave ou un autre réseau dans DiddiFood. DiddiPay gère ce choix.
- Les statuts publiés sont
pending,requires_action,succeeded,failed,cancelled,expired,collected_cash,refundedetpartially_refunded. - Le retour navigateur
/food/v1/payments/return/{token}ne confirme jamais le paiement. Après retour, relireGET /food/v1/payment-returns/{token}puisGET /food/v1/orders/{order_id}/payment.
Suivi, annulation et notation
GET /food/v1/ordersetGET /food/v1/orders/{order_id}sont les lectures de référence.POST /food/v1/orders/{order_id}/cancelest disponible seulement avant l'acceptation du restaurant.- La commande suit
placed → preparing → ready → picked_up → deliveredou devientrejected/cancelled. - Après
delivered,POST /food/v1/orders/{order_id}/ratingacceptestarsde 1 à 5 et un commentaire optionnel. - Pour un incident, relire
GET /food/v1/orders/{order_id}/delivery. Un statutfaileddoit afficher une action Support ou remboursement ; DiddiSend reste la source de vérité du coursier, de la position et de la preuve de remise.
Litige et remboursement
- Créer une demande avec
POST /food/v1/orders/{order_id}/refund-requests. - Suivre les demandes avec
GET /food/v1/orders/{order_id}/refund-requests. RefundRequestOut.statuspeut êtresubmitted,approved,rejected,processing,refundedoufailed.- Le client envoie uniquement un motif texte libre. Food calcule le montant comme le total du paiement enregistré pour la commande ; le client ne choisit pas le montant et les remboursements partiels ne sont pas proposés.
- L'approbation ou le rejet passent par le circuit Backoffice audité avec
food:payments:write. Les agents Support autorisés et les administrateurs métier Food peuvent décider ; DiddiPay exécute ensuite le mouvement.
5. Temps réel et notifications
Connexion optionnelle :
wss://diddifood-backend-staging.diddifree.com/food/v1/ws?token=<JWT>
Le WebSocket envoie une enveloppe {event,title,body,data}. Il n'y a pas de replay : après reconnexion, relire la commande, le paiement et la livraison par HTTP. Le push FCM est best-effort et peut être absent.
Enregistrer le token avec POST /food/v1/devices/register (fcm_token, platform)
après authentification, puis le retirer avec /food/v1/devices/unregister au
changement de compte. Les événements Food principaux sont order_confirmed,
order_ready, order_delivered et order_cancelled. Le push ne remplace jamais
la lecture HTTP.
6. Parcours Restaurant Pro
GET /food/v1/me/restaurants: restaurant(s) liés à l'utilisateur et rôle.- Manager : profil, horaires, zones, logo, plats, commandes et wallet.
- Kitchen : file de préparation et disponibilité des plats sans prix.
- Acceptation :
POST /food/v1/restaurants/{restaurant_id}/orders/{order_id}/acceptavec{ "prep_minutes": 15 }(1à180). - Prêt :
POST /food/v1/restaurants/{restaurant_id}/orders/{order_id}/ready. - Les livraisons, livreurs, collecte et remise cash restent la propriété de DiddiSend.
Images de plat : photo_file_id à l'écriture, image_url à la lecture si DiddiFiles est configuré. Logo restaurant : même principe avec logo_file_id et image_url.
7. Wallet restaurant
Le frontend Pro peut lire :
GET /food/v1/restaurants/{restaurant_id}/wallet;GET /food/v1/restaurants/{restaurant_id}/wallet/entries;GET /food/v1/restaurants/{restaurant_id}/wallet/withdrawal-quote;POST /food/v1/restaurants/{restaurant_id}/wallet/withdrawals;GET /food/v1/restaurants/{restaurant_id}/wallet/withdrawals/{withdrawal_id}.
Afficher séparément pending_balance, available_balance et reserved_balance. Le minimum de retrait staging est actuellement 5 000 XOF; les frais renvoyés par l'API restent la source d'affichage. La recharge restaurant est hors MVP.
8. Erreurs à traiter
| HTTP | Code ou situation | UX attendue |
|---|---|---|
| 401 | JWT absent/expiré | Reconnecter avec DiddiFreeID. |
| 403 | Rôle ou restaurant interdit | Masquer l'action et afficher un accès refusé. |
| 404 | Ressource absente | Recharger la liste ou afficher « introuvable ». |
| 409 | Transition interdite, pickup désactivé, paiement déjà traité | Relire l'état avant de proposer une action. |
| 422 | Corps invalide | Afficher les champs à corriger. |
| 503 | DiddiPay, DiddiSend ou DiddiFiles indisponible | Conserver l'état local, proposer de réessayer et ne pas inventer un succès. |
9. Fonctionnalités à ne pas afficher en MVP
pickup_mode=pickup: le backend répondPICKUP_DISABLEDtant queDIDDIFOOD_PICKUP_ENABLED=false.- Substitution de plat : porte
DIDDIFOOD_SUBSTITUTION_ENABLED=false. - Recharge du wallet restaurant : hors MVP.
- Routes Backoffice de remboursement et Support absentes du staging actuel : ne pas les appeler avant déploiement.
10. Scénarios QA frontend
- Catalogue vide et
image_url=null. - Catalogue avec image et fallback si URL absente.
- Commande cash en livraison, suivi
placedjusqu'àdelivered. - Commande online, redirection
next_action_url, refresh et confirmation. - Annulation avant acceptation puis refus après acceptation.
- Manager avec deux restaurants et kitchen sans accès aux prix.
- Wallet avec soldes séparés et retrait inférieur au minimum refusé.
- Reconnexion WebSocket suivie d'une relecture HTTP.
Contrat complet : docs/api/openapi.json. Parcours backend : customer-order-flow.md, restaurant-pro-flow.md, payments-wallet.md et delivery-flow.md.