Codes de réduction
Créez, validez, cumulez et suivez les coupons selon les mêmes règles qu’au tunnel de paiement.
L’API Discount coupons assure une validation serveur stricte et un suivi d’usage pour les tunnels.
Cette page décrit les règles de validation et d’application des coupons utilisées au tunnel et par l’API, notamment :
- validation à la création ;
- validation au tunnel (client, périmètre, quantité, fréquence) ;
- application mono ou multi-coupon ;
- réservation d’usage dédupliquée et liaison transaction.
Fonctionnement de la logique coupon (bout en bout)
En résumé :
- Création via
POST /couponsavec vos contraintes. - Validation au tunnel (règles serveur : client, périmètre, quantité, fréquence).
- Calcul et application de la remise sur la session checkout, y compris cumul lorsque autorisé.
- Réservation d’usage liée à la session jusqu’au paiement.
- Liaison de la réservation à la transaction finale en cas de succès.
- Incrément des compteurs d’usage lorsque la transaction passe en
completed.
Cela aligne l’aperçu tableau de bord, le tunnel hébergé et les intégrations API.
Créer un coupon de réduction
Corps de la requête
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
code | string | Oui | Code unique (passé en majuscules automatiquement) |
discount_type | string | Non | percentage ou fixed (défaut : percentage) |
discount_percentage | number | Si pourcentage | Pourcentage (> 0 et <= 100) |
discount_fixed_amount | number | Si fixe | Montant fixe |
description | string | Non | Description |
is_active | boolean | Non | Actif (défaut : true) |
max_uses | number | Non | Plafond total d’utilisations |
max_quantity_per_use | number | Non | Quantité max par utilisation |
valid_from | string | Non | Début (ISO 8601) |
expires_at | string | Non | Fin (ISO 8601) |
customer_type | string | Non | all, new, returning |
usage_frequency_limit | string | Non | total, per_customer, per_day, per_week, per_month |
usage_limit_value | number | Conditionnel | Requis si usage_frequency_limit != total |
scope_type | string | Non | organization_wide, specific_products, specific_prices |
product_ids | array | Non | IDs produits (si périmètre spécifique) |
Règles à la création
À la création, l’API impose :
- unicité du code par organisation ;
- exclusion mutuelle pourcentage vs fixe ;
valid_from < expires_atlorsque les deux existent ;usage_limit_valuerequis pour les modes autre quetotal;- pour
specific_products/specific_prices, produits liés à la même organisation.
import { LomiSDK } from '@lomi./sdk';
const lomi = new LomiSDK({
apiKey: process.env.LOMI_SECRET_KEY!,
environment: 'live',
});
// Percentage discount
const coupon = await lomi.coupons.create({
code: 'SAVE20',
discount_type: 'percentage',
discount_percentage: 20,
description: '20% off all products',
max_uses: 100,
expires_at: '2024-12-31T23:59:59Z',
});
// Fixed amount discount
const fixedCoupon = await lomi.coupons.create({
code: 'FLAT5000',
discount_type: 'fixed',
discount_fixed_amount: 5000,
description: '5000 XOF off',
customer_type: 'new',
});
console.log(`Coupon created: ${coupon.code}`);from lomi import LomiClient
import os
client = LomiClient(
api_key=os.environ["LOMI_SECRET_KEY"],
environment="test"
)
coupon = client.discount_coupons.create({
"code": "SAVE20",
"discount_type": "percentage",
"discount_percentage": 20,
"description": "20% off all products",
"max_uses": 100,
"expires_at": "2024-12-31T23:59:59Z"
})
print(f"Coupon created: {coupon['code']}")curl -X POST "https://api.lomi.africa/coupons" \
-H "X-API-KEY: $LOMI_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"code": "SAVE20",
"discount_type": "percentage",
"discount_percentage": 20,
"description": "20% off all products",
"max_uses": 100,
"expires_at": "2024-12-31T23:59:59Z"
}'Validation au tunnel
La validation combine contrôles structurels et métier. Un coupon doit satisfaire tous les points suivants :
- Existe et actif pour la même organisation.
- Fenêtre temporelle :
valid_fromdémarrée etexpires_atnon dépassée. - Plafond global :
current_uses < max_uses(simax_uses). - Fréquence client (si activée) : par client/jour/semaine/mois.
- Quantité : quantité du tunnel ≤
max_quantity_per_use. - Éligibilité client :
new: aucune transaction de paiement ou d’échéance complétée dans l’org ;returning: au moins une telle transaction ;all: sans restriction d’historique.
- Périmètre :
organization_wide: tous les produits ;specific_products/specific_prices: liaison danscoupon_product_links.
En cas d’échec, l’API renvoie un message explicite.
Calcul de la réduction
calculate_coupon_discount applique la remise sur le montant de base hors frais optionnels :
base_price = p_base_amount - p_fees_amount;- coupon pourcentage :
discount = base_price * percentage; - coupon fixe :
discount = fixed_amount; - multiplication par quantité si pertinent ;
- plafonnement pour ne pas dépasser la base éligible ;
- montant final
(base_price - discount) + frais.
Cela évite totaux négatifs et sur-remises.
Cumul multi-coupons (séquentiel)
Les coupons s’appliquent dans l’ordre sur un montant courant :
- A sur le montant initial ;
- B sur le montant après A ;
C’est un cumul séquentiel, pas une somme parallèle. La réponse détaille pour chaque coupon le montant d’origine, la remise et le résultat.
Si un coupon du tableau échoue à la validation, tout le calcul multi-coupon échoue.
Suivi d’usage et déduplication
L’usage est enregistré dans coupon_usage avec garde-fous contre les réservations en double :
- réservation unique par
(coupon_id, checkout_session_id)tant quetransaction_id IS NULL; - nettoyage des doublons obsolètes avant l’index unique partiel ;
- à paiement terminé, liaison à la transaction (sans doublon) ;
- sinon insertion directe liée à la transaction.
Comportement attendu pour nouvelles tentatives prestataire et courses entre webhooks.
Lister les coupons
Récupère tous les coupons de réduction de votre organisation.
const coupons = await lomi.coupons.list();coupons = client.discount_coupons.list()curl -X GET "https://api.lomi.africa/coupons" \
-H "X-API-KEY: $LOMI_SECRET_KEY"Obtenir un coupon
Récupère le détail d’un coupon.
const coupon = await lomi.coupons.get('dc_abc123...');coupon = client.discount_coupons.get('dc_abc123...')curl -X GET "https://api.lomi.africa/coupons/dc_abc123..." \
-H "X-API-KEY: $LOMI_SECRET_KEY"Indicateurs de performance d’un coupon
Statistiques d’usage et impact chiffre d’affaires pour un coupon (transactions completed uniquement).
const performance = await lomi.coupons.getPerformance('dc_abc123...');
console.log(`Total uses: ${performance.total_uses}`);
console.log(`Total discounted: ${performance.total_discount_amount}`);
console.log(`Revenue generated: ${performance.total_revenue}`);
console.log(`Avg order value: ${performance.average_order_value}`);performance = client.discount_coupons.get_performance('dc_abc123...')
print(f"Total uses: {performance['total_uses']}")curl -X GET "https://api.lomi.africa/coupons/dc_abc123.../performance" \
-H "X-API-KEY: $LOMI_SECRET_KEY"Réponse
{
"total_uses": 45,
"total_discounts": 25000,
"total_revenue": 150000,
"average_discount": 555.56,
"unique_customers": 38
}Objet Discount Coupon
| Champ | Type | Description |
|---|---|---|
id | string | Identifiant unique |
code | string | Code |
discount_type | string | percentage ou fixed |
discount_percentage | number | Valeur pourcentage |
discount_fixed_amount | number | Montant fixe |
customer_type | string | all, new, returning |
usage_frequency_limit | string | total, per_customer, per_day, per_week, per_month |
usage_limit_value | number | Plafond de fréquence le cas échéant |
is_active | boolean | Actif |
max_uses | number | Maximum d’utilisations |
current_uses | number | Utilisations courantes |
max_quantity_per_use | number | Quantité max par utilisation |
valid_from | string | Début de validité |
expires_at | string | Expiration |
scope_type | string | Périmètre |
product_links | array | Produits liés si périmètre spécifique |
completed_redemptions | number | Utilisations terminées |
distinct_customers_completed | number | Clients distincts avec utilisations complétées |
created_at | string | Création |
Modèles d’implémentation courants
Valider avant d’appliquer
- valider le coupon (endpoint de pré-validation),
- afficher un aperçu de la remise,
- appliquer à la création ou confirmation du tunnel.
Gérer les relances prestataires
Les callbacks peuvent être renvoyés. La réservation / liaison coupon est pensée pour être idempotente par paire session + coupon.
Fournir le client quand c’est possible
Pour les coupons new et returning, envoyez customer_id pour une évaluation d’éligibilité correcte.
Réponses d’erreur
| Statut | Description |
|---|---|
400 | Entrée invalide ou code en double |
401 | Clé API invalide ou manquante |
404 | Coupon introuvable |
Exemples concrets
Coupon réservé aux nouveaux clients
Configuration :
customer_type = newscope_type = organization_widediscount_type = percentagediscount_percentage = 20
Comportement :
- Client sans transaction de paiement / échéance complétée dans l’organisation : coupon valide.
- Client ayant au moins une transaction de paiement / échéance complétée : coupon refusé (non éligible).
- Sans contexte client, les contrôles d’éligibilité pour les types restreints peuvent échouer.
Coupon réservé aux clients existants
Configuration :
customer_type = returningusage_frequency_limit = per_monthusage_limit_value = 1
Comportement :
- Un client existant peut l’utiliser une fois par mois.
- Une deuxième utilisation le même mois est refusée.
- Un nouveau mois autorise à nouveau une utilisation (sous réserve des autres limites).
Coupon limité à des produits
Configuration :
scope_type = specific_productsproduct_ids = [Produit A, Produit B]
Comportement :
- Tunnel pour A ou B : le périmètre passe.
- Tunnel pour C : refusé (
coupon is not applicable to this product). - Les coupons organisation-wide ignorent les liens produit.
Coupon plafonné en quantité
Configuration :
max_quantity_per_use = 2
Comportement :
- Quantité
1ou2: valide. - Quantité
3+: refusée.
Remise fixe plafonnée au montant de base
Tunnel :
- Montant de base :
3 000 - Frais :
500 - Prix éligible :
2 500
Coupon :
discount_type = fixeddiscount_fixed_amount = 5 000
Résultat :
- La remise est plafonnée à
2 500. - Montant final :
(2 500 - 2 500) + 500 = 500.
Cumul séquentiel (deux coupons)
Montant : 10 000
Ordre :
SAVE20(20 %)FLAT1000(fixe 1 000)
Calcul :
- Après
SAVE20: remise2 000, montant8 000 - Après
FLAT1000: remise1 000, montant7 000 - Remise totale :
3 000
Le second coupon s’applique au montant déjà réduit, pas au montant d’origine.
Réservation en attente et liaison transaction
- Le coupon est appliqué à la session checkout.
- Une réservation
coupon_usageen attente est enregistrée pour(coupon_id, checkout_session_id). - Le callback prestataire crée / finalise la transaction.
- La réservation est liée à la transaction.
Cela évite les lignes d’usage en double lors des relances webhook, et garde le reporting exact.
Ordre de cumul et total
Les mêmes deux coupons, ordre inversé :
FLAT1000d’abord : remise1 000, montant9 000SAVE20ensuite : 20 % de9 000=1 800, montant7 200- Remise totale :
2 800(pas3 000)
Affichez l’ordre appliqué dans l’UI et conservez le même ordre côté serveur.
Fenêtre glissante vs calendrier
Selon le chemin de validation, les limites peuvent être :
- une fenêtre glissante (par exemple les 24 dernières heures pour
per_day) ; - des bornes calendaires pour
per_day/per_week/per_month.
Pour un coupon « une fois par jour », un client à 23:59 peut ou non l’utiliser à 00:01. Traitez la limite comme « au plus N utilisations sur la période configurée » et testez en sandbox.
Réappliquer un coupon sur la même session
Si la session a déjà une ligne coupon_usage en attente, le système peut mettre à jour cette ligne au lieu d’en insérer une autre, tant que la transaction n’est pas finalisée.
Après paiement réussi, l’usage repose sur la transaction.
Tunnels gratuits ou entièrement remisés
Quand la remise ramène le payable à zéro, la plateforme peut enregistrer un parcours gratuit pour garder grand livre et webhooks cohérents. Les rapports doivent toujours montrer prix catalogue, remise et net.
Notes d’intégration
- Validez toujours avant l’application finale.
- Envoyez le contexte client pour les coupons
new/returning. - Rendez l’application de coupon idempotente côté client.
- Pour plusieurs coupons, conservez l’ordre prévu.