# Comportement du tunnel de paiement
Source: https://docs.lomi.africa/build/accept/checkout-behavior

Sessions, liens de paiement, montants, codes promo et règles d’expiration pour le tunnel hébergé.

***

title: Comportement du tunnel de paiement
description: Sessions, liens de paiement, montants, codes promo et règles d’expiration pour le tunnel hébergé.
--------------------------------------------------------------------------------------------------------------

Le tunnel combine **sessions de paiement**, **liens de paiement**, **produits / tarifs**, **codes promo** et parcours **prestataires**. Cette page décrit les règles transverses ; les détails d’endpoint restent sur chaque ressource.

## Sessions de paiement

* Les sessions sont en général **limitées dans le temps** (la fenêtre d’expiration par défaut est documentée sous [Sessions de paiement](/build/accept/checkout)).
* Le statut évolue (par ex. ouvert → terminé / expiré) lorsque le client paie ou que le temps est écoulé.
* Le **`price_id`** sur la session choisit le tarif du produit appliqué ; le montant peut être dérivé ou validé par rapport aux tarifs du produit.

## Liens de paiement

* Les liens **produit** déterminent le montant à partir des tarifs catalogue. Si **`price_id`** est défini, la session initiale utilise le `amount` suggéré de cette ligne de tarif (pas seulement le tarif par défaut).
* Les liens **instantanés** utilisent un montant fixe que vous fournissez.
* Les flux multi-lignes peuvent définir **`has_line_items`** dans les métadonnées de session ; les totaux agrègent les lignes, les frais d’expédition / taxes optionnels et les frais liés au produit lorsqu’ils sont configurés.
* Les produits **payez ce que vous voulez** exigent un lien ou une session **mono-produit**. Les paniers multi-lignes rejettent le PWYW (`line_items_pwyw_not_supported`).

## Payez ce que vous voulez

La tarification flexible est disponible pour les produits **ponctuels** uniquement (`pricing_model: pay_what_you_want` sur une ligne de tarif) :

* **Prix unitaire**: le client choisit un prix unitaire ; total = unité × `quantity`.
* **`amount` sur le tarif**: prix unitaire suggéré prérempli au tunnel (par défaut `minimum_amount` si omis à la création du produit).
* **Bornes**: `minimum_amount` (obligatoire) et `maximum_amount` optionnel plafonnent le prix unitaire. L’API et la base **rejettent** un `amount` suggéré hors de `[minimum_amount, maximum_amount]`.
* **Liens de paiement**: les liens produit préremplissent le montant suggéré depuis le `price_id` lié (ou le tarif par défaut) ; le tunnel hébergé permet à l’acheteur d’ajuster dans les bornes avant de payer.
* **Sessions API**: à la création d’une session avec `product_id`/`price_id`, passez `amount` comme sous-total produit ou omettez-le pour utiliser le prix suggéré × quantité. Voir [Sessions de paiement](/build/accept/checkout) et [Produits](/build/billing/products).

<Callout type="warning">
  **N’incluez pas de tarifs payez ce que vous voulez dans un panier multi-produits.** Utilisez un lien ou une session dédiée pour les dons et tarifs flexibles. Les APIs panier renvoient `line_items_pwyw_not_supported`.
</Callout>

## Abonnements

Les produits récurrents (`product_type: recurring`) créent une **instance d’abonnement** à la fin de l’inscription. Cela s’applique au tunnel hébergé, aux liens de paiement, à la vitrine (bouton S’abonner) et aux sessions API avec un `product_id` récurrent.

<Callout type="warning">
  **Un panier a un seul type de produit.** Les produits ponctuels peuvent partager un panier. Les packs d’usage peuvent partager un panier. Un produit récurrent reste dans sa propre session ou son propre lien. Mélanger un produit ponctuel et un pack d’usage renvoie `line_items_mixed_product_types`.
</Callout>

### Premier paiement et essais

Les champs produit définissent le **premier prélèvement au tunnel** (voir [Produits](/build/billing/products)) :

| Réglage                           | Montant à l’inscription                                            |
| --------------------------------- | ------------------------------------------------------------------ |
| `first_payment_type: initial`     | Tarif récurrent (ou part au prorata).                              |
| `first_payment_type: non_initial` | **0** à l’inscription ; premier prélèvement à `next_billing_date`. |
| `first_payment_type: prorated`    | Montant partiel pour le reste de la période.                       |
| `trial_enabled: true`             | **0** pendant `trial_period_days` ; facturation après l’essai.     |

Le tunnel hébergé et la vitrine affichent le total résolu côté serveur. Le **`amount` de la session** peut refléter le **prix catalogue** pour respecter la validation (`amount` > 0), tandis que le client paie **0** ou enregistre uniquement sa carte pour l’essai.

### Moyens de paiement à l’inscription

| Prestataire             | Essai / inscription à 0                                                                               |
| ----------------------- | ----------------------------------------------------------------------------------------------------- |
| **Cartes**              | SetupIntent enregistre la carte ; pas de prélèvement avant la fin de l’essai ou la première échéance. |
| **Wave / MTN**          | Inscription sans paiement immédiat lorsque `requires_payment` est false.                              |
| **Inscription payante** | Flux de paiement normal ; première transaction de type `instalment` liée à l’abonnement.              |

### Renouvellements et échecs

* **Cartes** : prélèvement automatique sur le moyen enregistré à `next_billing_date`.
* **Mobile money** : sans moyen enregistré, lomi. peut envoyer un **lien de renouvellement manuel** par e-mail.
* Les échecs peuvent passer le statut en `past_due` ou `paused` selon `failed_payment_action` (`continue`, `pause`, `cancel`).
* Écoutez **`SUBSCRIPTION_RENEWED`** et **`PAYMENT_FAILED`** (transactions de renouvellement).

### Abonnements à durée fixe

Clés **metadata** produit optionnelles (API `metadata` ou tableau de bord) :

| Clé                   | Description                                       |
| --------------------- | ------------------------------------------------- |
| `subscription_length` | `"automatic"` (défaut) ou stratégie à durée fixe. |
| `fixed_charges`       | Nombre de cycles de facturation avant expiration. |

Voir [Abonnements](/build/billing/subscriptions) pour le cycle de vie et les webhooks.

## Codes promo

* La validation applique les **dates**, **plafonds d’usage**, **type de client** (nouveau ou récurrent), **périmètre** (organisation entière ou produits précis) et plafonds de **quantité**.
* Plusieurs codes (lorsque c’est pris en charge) s’appliquent **séquentiellement** sur un montant courant, voir [Codes de réduction](/build/billing/discount-coupons) et [Exemples de logique des codes](/build/billing/discount-coupons).

## Multi-devises

Lorsque la devise de la session diffère de celle d’un tarif produit, la logique serveur peut **convertir** pour valider que le montant payé correspond au tarif configuré dans l’autre devise.

## Champs du formulaire checkout

Contrôlez quels champs client apparaissent sur le tunnel hébergé et s’ils sont obligatoires. Surchargez par [lien de paiement](/build/accept/payment-links) ou définissez les indicateurs sur chaque [session checkout](/build/accept/checkout). Les défauts d’organisation pour **l’e-mail**, le **téléphone** et **l’adresse de facturation** se configurent dans le tableau de bord sous **Paramètres → Checkout**. **`require_name`** est disponible sur les liens de paiement et les sessions checkout via l’API (obligatoire par défaut si omis).

Chaque indicateur `require_*` est un booléen. Quand il vaut `true`, le champ est **affiché et obligatoire** avant le paiement. Quand il vaut `false`, le comportement dépend du champ (voir ci-dessous). Passer `customer_name`, `customer_email` ou `customer_phone` sur une session **préremplit** seulement les valeurs ; cela ne change pas les champs affichés.

### Indicateurs des champs système

| Champ                  | Indicateur API            | Défaut si omis | Quand `true`                              | Quand `false`                                                      |
| ---------------------- | ------------------------- | -------------- | ----------------------------------------- | ------------------------------------------------------------------ |
| Nom                    | `require_name`            | `true`         | Champ nom affiché et obligatoire          | Champ nom masqué                                                   |
| E-mail                 | `require_email`           | `true`         | Champ e-mail affiché et obligatoire       | Champ e-mail masqué                                                |
| Téléphone              | `require_phone`           | `false`        | Champ téléphone affiché et obligatoire    | Voir [E-mail et téléphone ensemble](#e-mail-et-téléphone-ensemble) |
| Adresse de facturation | `require_billing_address` | `false`        | Champs d’adresse affichés et obligatoires | Adresse masquée                                                    |

### `require_name`

* **`true` (défaut) :** Le champ nom complet est affiché. Le client doit saisir une valeur non vide pour continuer.
* **`false` :** Le champ nom est masqué. Le tunnel crée ou met à jour quand même le client avec un nom d’affichage, en prenant la première valeur disponible : nom (s’il est fourni ailleurs), e-mail, téléphone, puis `"Customer"`.

Utilisez `customer_name` sur une session checkout pour préremplir le nom quand le champ est visible.

### `require_email`

* **`true` (défaut) :** Le champ e-mail est affiché et obligatoire. La valeur doit contenir `@`.
* **`false` :** Le champ e-mail est masqué. Le tunnel ne demande pas d’e-mail, même si `customer_email` a été passé pour préremplir.

Masquer l’e-mail modifie l’interprétation de `require_phone` (voir ci-dessous).

### `require_phone`

* **`true` :** Le champ téléphone est affiché et obligatoire. Le numéro doit être valide pour le pays sélectionné.
* **`false` (défaut) :** Le comportement dépend de la visibilité de l’e-mail :
  * Si l’e-mail est **visible**, le téléphone est affiché en **optionnel**.
  * Si l’e-mail est **masqué**, le téléphone est **masqué** aussi (sauf si vous passez `require_phone: true`).

Utilisez `customer_phone` sur une session checkout pour préremplir le téléphone quand le champ est visible.

### E-mail et téléphone ensemble

Une fois les indicateurs résolus, le tunnel hébergé applique ces règles :

| `require_email` | `require_phone` | Ce que voit le client                     |
| --------------- | --------------- | ----------------------------------------- |
| `true`          | `false`         | E-mail obligatoire, téléphone optionnel   |
| `true`          | `true`          | E-mail obligatoire, téléphone obligatoire |
| `false`         | `true`          | E-mail masqué, téléphone obligatoire      |
| `false`         | `false`         | Ni e-mail ni téléphone                    |

<Callout type="warning">
  **Au moins un champ de contact :** l’API rejette `require_name: false` avec `require_email: false` et `require_phone: false` sauf si vous passez `customer_name` ou `customer_id` sur la session. Le tunnel doit pouvoir identifier le client via le nom, l’e-mail ou le téléphone.
</Callout>

### Combinaisons courantes

| Objectif                                       | Indicateurs                                                                                            |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Checkout téléphone seul                        | `require_name: false`, `require_email: false`, `require_phone: true`, `require_billing_address: false` |
| E-mail seul (sans téléphone)                   | `require_email: true`, `require_phone: false` (téléphone optionnel tant que l’e-mail est visible)      |
| Masquer le nom, collecter l’e-mail             | `require_name: false`, `require_email: true`                                                           |
| Contact minimal (e-mail + téléphone optionnel) | Défauts, omettre les indicateurs ou `require_phone: false`                                             |

Exemple, session **téléphone seul** :

```json
{
  "amount": 1000,
  "currency_code": "XOF",
  "title": "Checkout téléphone seul",
  "require_name": false,
  "require_email": false,
  "require_phone": true,
  "require_billing_address": false
}
```

Le client ne voit que le champ téléphone (plus le basculement WhatsApp optionnel). Nom, e-mail et adresse de facturation sont masqués.

<Callout type="info">
  **Mobile money :** Quand le téléphone est visible mais optionnel, **Wave** et **MTN** exigent quand même un numéro valide au moment du paiement. Carte et autres moyens peuvent continuer sans téléphone quand il est optionnel.
</Callout>

### Ordre de résolution

À l’ouverture du tunnel, les indicateurs se combinent du plus spécifique au plus général :

1. **Session checkout** (`require_name`, `require_email`, `require_phone`, `require_billing_address` sur `POST /checkout-sessions`)
2. **Lien de paiement** (mêmes indicateurs sur le lien ayant créé la session)
3. **Paramètres checkout de l’organisation** (défauts du tableau de bord)
4. **Défauts plateforme** (nom obligatoire, e-mail obligatoire, téléphone optionnel, adresse masquée)

Passez `customer_name`, `customer_email`, `customer_phone` et les champs d’adresse sur la session pour **préremplir** le formulaire ; cela ne change pas les champs affichés.

### Champs personnalisés

Les organisations peuvent définir des **champs checkout personnalisés** (texte, case à cocher, CGU, etc.) dans les paramètres checkout du tableau de bord. Ils s’appliquent au tunnel hébergé, aux liens de paiement et à la vitrine, sauf surcharge dans `metadata.custom_fields` de la session ou du lien.

### Avancé : tableau `fields`

Sur `POST /checkout-sessions` et `POST /payment-links`, vous pouvez passer un tableau `fields`: liste ordonnée de champs système et personnalisés avec `visibility` à `hidden`, `optional` ou `required`. S’il est présent, il **remplace** les booléens `require_*`. La plupart des intégrations utilisent les booléens ; réservez `fields` à un ordre fin ou des champs personnalisés mixtes optionnels/obligatoires via l’API.

## Identité client

Le tunnel peut **créer automatiquement ou fusionner** des fiches clients lorsque les champs de contact sont fournis, pour que les webhooks et transactions en aval s’associent lorsque possible à un `customer_id` stable.

## Pages connexes

* [Sessions de paiement](/build/accept/checkout)
* [Liens de paiement](/build/accept/payment-links)
* [Produits](/build/billing/products)
* [Abonnements](/build/billing/subscriptions)
* [Cycle de vie des paiements](/build/reliability/payment-lifecycle)
