# Comment utiliser le checkout ?
Source: https://docs.lomi.africa/build/accept/checkout

Créez une session de checkout, redirigez le client vers lomi., puis confirmez le résultat avec redirections, dashboard et webhooks.

***

title: 'Comment utiliser le checkout ?'
description: 'Créez une session de checkout, redirigez le client vers lomi., puis confirmez le résultat avec redirections, dashboard et webhooks.'
--------------------------------------------------------------------------------------------------------------------------------------------------

import { Tabs, Tab } from 'fumadocs-ui/components/tabs';
import { Callout } from '@/components/docs/docs-callout';

Le checkout hébergé est l’intégration recommandée pour la plupart des équipes. Votre serveur crée une session, le client paie sur une page hébergée par lomi., puis votre système confirme le résultat.

Ne traitez pas la réponse de création comme un paiement réussi. Le Mobile Money live est asynchrone.

<TaskSurfaces task="take-a-payment" />

<Callout type="info">
  **À savoir :** les sessions expirent par défaut après **60 minutes**. Les clés test s’exécutent en [mode sandbox](/start/sandbox-payments). Règles transverses (codes promo, lignes, état du paiement) : **[Comportement du tunnel](/build/accept/checkout-behavior)**.
</Callout>

## Quand utiliser le checkout hébergé ?

Utilisez-le si vous voulez :

* Une page de paiement complète avec plusieurs moyens de paiement.
* Moins de travail UI et conformité dans votre propre application.
* Un flux clair avec URL de succès et d’annulation.
* Un checkout basé sur un montant ou sur un produit.
* Un chemin fiable de la sandbox vers le live.

## Créer une session de paiement

Votre serveur appelle `POST /checkout-sessions` et redirige le client vers `checkout_url`.

**Référence API (schéma complet) :** [Créer une session de checkout](/api/checkout-sessions/CheckoutSessionsController_create)

**Champs essentiels :**

| Champ                                                                          | Notes                                                                                                                                     |
| ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `currency_code`                                                                | Obligatoire, `XOF`, `USD` ou `EUR`                                                                                                        |
| `amount`                                                                       | Obligatoire sauf si vous passez `product_id` ou `line_items`                                                                              |
| `product_id` / `price_id`                                                      | Checkout catalogue ; montant dérivé du tarif                                                                                              |
| `success_url` / `cancel_url`                                                   | Redirection après paiement ou annulation                                                                                                  |
| `customer_email`                                                               | Optionnel, préremplit le formulaire hébergé                                                                                               |
| `require_name` / `require_email` / `require_phone` / `require_billing_address` | Optionnel, contrôle les champs du formulaire ; voir [Champs du formulaire checkout](/build/accept/checkout-behavior#checkout-form-fields) |

Passez `metadata` pour vos propres identifiants de commande. Voir la page API pour tous les champs optionnels.

### Produits « payez ce que vous voulez »

Lorsque `product_id` ou `price_id` pointe vers un tarif avec `pricing_model: pay_what_you_want` :

* **Omettre `amount`**: la session utilise le prix unitaire suggéré (`amount` sur le tarif) × `quantity`.
* **Fournir `amount`**: doit être un sous-total valide : prix unitaire dans `[minimum_amount, maximum_amount]` × `quantity`. Le serveur rejette les valeurs hors bornes.
* **Tunnel hébergé**: l’acheteur peut modifier le prix unitaire avant de payer ; validation côté client et serveur.
* **Flux programmatiques**: définissez `amount` à la création de session si le total est déjà connu.

### Panier multi-produits (`line_items`)

Avec **`line_items`**, chaque ligne doit référencer un tarif **ponctuel** en tarification **standard** (fixe). L’API renvoie **400** avec l’un des codes :

| Code                                   | Signification                                                            |
| -------------------------------------- | ------------------------------------------------------------------------ |
| `line_items_pwyw_not_supported`        | Une ligne utilise `pay_what_you_want`, utilisez une session mono-produit |
| `line_items_recurring_not_supported`   | Une ligne référence un produit récurrent                                 |
| `line_items_usage_based_not_supported` | Tarification à l’usage (pas encore prise en charge en panier)            |
| `line_items_mixed_product_types`       | Mélange ponctuel et récurrent dans un même panier                        |

### Produits récurrents (abonnements)

Lorsque `product_id` désigne un produit **récurrent** :

* **Omettre `amount`**: la session utilise le prix catalogue du `price_id` choisi (la quantité n’est pas multipliée pour les abonnements).
* **Fournir `amount`**: doit correspondre au prix récurrent catalogue pour la validation (sauf prorata au moment du paiement ; voir [Comportement du tunnel: Abonnements](/build/accept/checkout-behavior#subscription-checkout)).
* **Essai ou inscription `non_initial`**: le client peut payer **0** au tunnel, mais la session enregistre souvent le **prix catalogue** pour que `create_checkout_session` respecte `amount > 0`. N’envoyez pas `amount: 0` sans vous appuyer sur le calcul produit.
* La finalisation crée un **abonnement** (webhook `SUBSCRIPTION_CREATED`), pas seulement une transaction ponctuelle.

Exemple :

```typescript
const subscriptionSession = await lomi.checkoutSessions.create({
  product_id: 'prod_recurring_abc...',
  price_id: 'price_monthly_xyz...',
  currency_code: 'XOF',
  customer_email: 'customer@example.com',
  success_url: 'https://your-site.com/welcome',
  allow_coupon_code: true,
});
```

<Tabs items={["TypeScript", "Python", "cURL"]}>
  <Tab value="TypeScript">
    ```typescript
    import { LomiSDK } from '@lomi./sdk';

    const lomi = new LomiSDK({
      apiKey: process.env.LOMI_SECRET_KEY!,
      environment: 'live',
    });

    // Tunnel simple avec montant
    const session = await lomi.checkoutSessions.create({
      amount: 10000,
      currency_code: 'XOF',
      title: 'Order #12345',
      description: 'Paiement des articles du panier',
      customer_email: 'customer@example.com',
      success_url: 'https://your-site.com/success',
      cancel_url: 'https://your-site.com/cancel',
      metadata: {
        order_id: 'ORD-12345',
      },
    });

    // Tunnel basé sur un produit
    const productSession = await lomi.checkoutSessions.create({
      product_id: 'prod_abc123...',
      currency_code: 'XOF',
      quantity: 2,
      allow_coupon_code: true,
      success_url: 'https://your-site.com/success',
    });

    // Produit PWYW avec montant personnalisé et quantité
    const pwywSession = await lomi.checkoutSessions.create({
      product_id: 'prod_tip_jar...',
      currency_code: 'XOF',
      amount: 1500,
      quantity: 2,
      success_url: 'https://your-site.com/success',
    });

    console.log(`Redirect to: ${session.checkout_url}`);
    ```
  </Tab>

  <Tab value="Python">
    ```python
    from lomi import LomiClient
    import os

    client = LomiClient(
        api_key=os.environ["LOMI_SECRET_KEY"],
        environment="test"
    )

    session = client.checkout_sessions.create({
        "amount": 10000,
        "currency_code": "XOF",
        "title": "Order #12345",
        "description": "Payment for items in cart",
        "customer_email": "customer@example.com",
        "success_url": "https://your-site.com/success",
        "cancel_url": "https://your-site.com/cancel",
        "metadata": {
            "order_id": "ORD-12345"
        }
    })

    # Produit PWYW avec montant personnalisé et quantité
    pwyw_session = client.checkout_sessions.create({
        "product_id": "prod_tip_jar...",
        "currency_code": "XOF",
        "amount": 1500,
        "quantity": 2,
        "success_url": "https://your-site.com/success"
    })

    print(f"Redirect to: {session['checkout_url']}")
    ```
  </Tab>

  <Tab value="cURL">
    ```bash
    curl -X POST "https://api.lomi.africa/checkout-sessions" \
      -H "X-API-KEY: $LOMI_SECRET_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "amount": 10000,
        "currency_code": "XOF",
        "title": "Order #12345",
        "description": "Payment for items in cart",
        "customer_email": "customer@example.com",
        "success_url": "https://your-site.com/success",
        "cancel_url": "https://your-site.com/cancel",
        "metadata": {
          "order_id": "ORD-12345"
        }
      }'
    ```
  </Tab>
</Tabs>

### Réponse

```json
{
  "id": "cs_abc123...",
  "checkout_url": "https://checkout.lomi.africa/cs_abc123...",
  "status": "open",
  "amount": 10000,
  "currency_code": "XOF",
  "title": "Order #12345",
  "customer_email": "customer@example.com",
  "expires_at": "2024-01-15T11:30:00Z",
  "created_at": "2024-01-15T10:30:00Z"
}
```

***

## Intégrer sans redirection

Pour garder le client sur votre page, utilisez le SDK embed avec le même `checkout_url` :

```javascript
import { loadLomiCheckout } from '@lomi./embed';

loadLomiCheckout({
  checkoutUrl: session.checkout_url,
  mode: 'modal',
  onComplete: (payload) => console.log(payload.transactionId),
});
```

Voir [Widget de paiement intégré](/build/accept/embed-widget) pour modal, inline et self-hosted.

## Confirmer le paiement

Les redirections servent à l’expérience client, pas à la réconciliation finale. Votre serveur doit s’appuyer sur les webhooks ou une lecture API côté serveur avant de livrer une commande.

Vérifiez :

* Le statut de la session de checkout.
* Le statut de la transaction liée.
* Le montant, la devise, le client et les métadonnées.
* La signature webhook et l’ID d’événement.

## Variantes courantes

| Variante            | À utiliser quand                      |
| ------------------- | ------------------------------------- |
| Montant fixe        | Votre backend connaît déjà le montant |
| Checkout produit    | Le montant vient d’un produit ou prix |
| Checkout abonnement | Le client démarre un plan récurrent   |
| Coupons activés     | Vous acceptez des codes promo         |
| Checkout test       | Vous validez avec `lomi_sk_test_...`  |

## Lister et récupérer les sessions

Utilisez la référence API pour lister ou récupérer une session par ID :

* [Lister les sessions](/api/checkout-sessions/CheckoutSessionsController_findAll)
* [Obtenir une session](/api/checkout-sessions/CheckoutSessionsController_findOne)

## Webhooks

Les **webhooks sortants marchands** (lomi. appelle **votre** URL) utilisent l’énumération **`webhook_event`** documentée dans [Webhooks](/build/reliability). Le champ HTTP `` `event` `` et l’en-tête `` `X-Lomi-Event` `` reprennent ces valeurs (par ex. `` `PAYMENT_SUCCEEDED` ``).

Pour les résultats de tunnel **côté votre serveur**, souscrivez au minimum à :

| Valeur `authorized_events` | Déclenchement                                                                                             |
| -------------------------- | --------------------------------------------------------------------------------------------------------- |
| `` `PAYMENT_SUCCEEDED` ``  | Paiement terminé ; le corps `` `data` `` suit la forme transaction (voir [Webhooks](/build/reliability)). |
| `` `PAYMENT_FAILED` ``     | Tentative échouée (y compris certains chemins d’échec prestataire).                                       |

Il **n’existe pas** de valeur séparée `` `checkout.session.*` `` dans l’énumération `` `webhook_event` ``. L’expiration de session sans paiement réussi se voit sur les **enregistrements session** et **transaction** (statut `` `expired` `` par ex.) ; utilisez `` `GET /checkout-sessions/{id}` `` ou une liste avec `` `status=expired` `` / `` `completed` `` plutôt qu’un webhook marchand nommé comme `checkout.session.expired`.

***

## Réponses d’erreur

| Statut | Description                             |
| ------ | --------------------------------------- |
| `400`  | Entrée invalide ou erreur de validation |
| `401`  | Clé API invalide ou manquante           |
| `404`  | Session introuvable ou accès refusé     |

## API liée

* [Créer une session de checkout](/api/checkout-sessions/CheckoutSessionsController_create)
* [Lister les sessions](/api/checkout-sessions/CheckoutSessionsController_findAll)
* [Obtenir une session](/api/checkout-sessions/CheckoutSessionsController_findOne)
* [Comportement checkout](/build/accept/checkout-behavior)
* [Webhooks](/build/reliability)
