Aller au contenu

Scénarios de paiement

Ce guide détaille les deux scénarios de paiement supportés et comment les implémenter.

Scénario 1 : Empreinte bancaire (IMPRINT)

L'empreinte bancaire enregistre la carte du client comme garantie de réservation. Aucun débit n'est effectué. En cas de no-show, l'hôtel peut capturer le montant garanti.

Flux complet

1. PMS ──► API : Créer IMPRINT (montant = garantie max)
2. Client reçoit le lien par email
3. Client enregistre sa carte (aucun débit)
4. Statut : AUTHORISED (alias carte enregistré)

   ... le temps passe ...

5. J-2 avant checkin : M-Pay crée automatiquement
   un paiement avec capture manuelle via le token carte
6. Statut : AUTHORISED_TO_VALIDATE (montant bloqué)

Si le client se présente :
7a. Hôtel ──► API : POST /{uuid}/release/
8a. Statut : CANCELLED (montant libéré)

Si no-show :
7b. Hôtel ──► API : POST /{uuid}/capture/
8b. Statut : CAPTURED (montant débité)

Implémentation

# 1. Créer la demande d'empreinte
#    IMPORTANT : inclure checkin dans reservation_data pour le déclenchement J-2
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{
    "request_type": "IMPRINT",
    "amount": 350.00,
    "guest_email": "client@example.com",
    "guest_first_name": "Jean",
    "guest_last_name": "Dupont",
    "reservation_ref": "RES-2026-200",
    "reservation_data": {
      "checkin": "2026-09-15",
      "checkout": "2026-09-18",
      "room_type": "Double Supérieure"
    }
  }' \
  https://votre-domaine.com/api/payment-requests/
# 2. Vérifier le statut après enregistrement de la carte
curl -H "X-API-Key: $KEY" \
  https://votre-domaine.com/api/payment-requests/statuses/?refs=RES-2026-200
{
  "status": "AUTHORISED",
  "amount": 350.00,
  "amount_captured": 0.00
}

Pré-autorisation automatique (J-2)

M-Pay exécute une tâche quotidienne qui détecte les empreintes dont le checkin est dans 2 jours. Pour chaque empreinte AUTHORISED avec un token carte :

  1. Un paiement silencieux (mode SILENT) est créé via PayZen avec manualValidation=YES
  2. Le client n'intervient pas — le token carte stocké est utilisé
  3. Le statut passe à AUTHORISED_TO_VALIDATE
  4. Le montant est "bloqué" sur la carte du client pendant 6 jours max

Champ checkin obligatoire

La date checkin dans reservation_data est indispensable pour le déclenchement automatique. Format attendu : YYYY-MM-DD.

Configuration Celery Beat

La tâche create_deferred_payments doit être planifiée quotidiennement (ex: tous les jours à 06:00) via l'interface django-celery-beat dans l'administration.

Capture en cas de no-show

Deux options :

Via l'API (recommandé pour intégration PMS) :

curl -X POST -H "X-API-Key: $KEY" \
  https://votre-domaine.com/api/payment-requests/{uuid}/capture/
{
  "uuid": "550e8400-...",
  "reference": "IMP-2026-0200",
  "status": "CAPTURED"
}

Via l'administration :

  1. Allez dans Demandes de paiement
  2. Sélectionnez la demande en statut AUTHORISED_TO_VALIDATE
  3. Cliquez sur Capturer les paiements sélectionnés

Libération si le client se présente

Si le client se présente, l'hôtel peut libérer la pré-autorisation pour débloquer les fonds sur la carte du client :

Via l'API :

curl -X POST -H "X-API-Key: $KEY" \
  https://votre-domaine.com/api/payment-requests/{uuid}/release/
{
  "uuid": "550e8400-...",
  "reference": "IMP-2026-0200",
  "status": "CANCELLED"
}

Via l'administration :

  1. Sélectionnez la demande en statut AUTHORISED_TO_VALIDATE
  2. Cliquez sur Libérer les pré-autorisations sélectionnées

Bonne pratique

Libérez toujours les pré-autorisations quand le client se présente. Sinon le montant reste bloqué sur sa carte jusqu'à expiration (6 jours), ce qui est désagréable pour le client.

Délai de capture

Le délai maximum de capture dépend de votre contrat PayZen (généralement 6 jours). Passé ce délai, l'autorisation expire automatiquement.


Scénario 2 : Pré-paiement (DEPOSIT)

Le pré-paiement débite immédiatement le client du montant indiqué (arrhes, acompte).

Flux complet

1. PMS ──► API : Créer DEPOSIT (montant = arrhes)
2. Client reçoit le lien par email
3. Client paie le montant
4. Statut : CAPTURED (montant débité)
5. Email de confirmation envoyé au client

Si annulation par le client :
6. Hôtel ──► Admin : Action "Rembourser"
7. Statut : REFUNDED

Implémentation

# 1. Créer la demande de pré-paiement
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{
    "request_type": "DEPOSIT",
    "amount": 150.00,
    "currency": "EUR",
    "guest_email": "client@example.com",
    "guest_first_name": "Marie",
    "guest_last_name": "Martin",
    "reservation_ref": "RES-2026-201"
  }' \
  https://votre-domaine.com/api/payment-requests/
# 2. Vérifier après paiement
curl -H "X-API-Key: $KEY" \
  https://votre-domaine.com/api/payment-requests/statuses/?refs=RES-2026-201
{
  "status": "CAPTURED",
  "amount": 150.00,
  "amount_captured": 150.00
}

Comparatif

Empreinte (IMPRINT) Pré-paiement (DEPOSIT)
Débit immédiat Non Oui
Usage Garantie de réservation Arrhes / acompte
Token carte Oui (alias sauvegardé) Non
Pré-auth J-2 Automatique N/A
Capture Manuelle (no-show) ou API Automatique
Libération API ou admin N/A
Remboursement N/A (avant pré-auth) Via API ou admin
Statut final OK CANCELLED (libéré) ou CAPTURED (no-show) CAPTURED

Cycle de vie d'une empreinte

IMPRINT créé
NOT_INITIATED ──► Client enregistre sa carte ──► AUTHORISED
                                          J-2 : pré-auth automatique
                                            AUTHORISED_TO_VALIDATE
                                                   /       \
                                                  /         \
                                         Client OK      No-show
                                            │               │
                                            ▼               ▼
                                  release/ ──► CANCELLED   capture/ ──► CAPTURED

Gestion des erreurs

Si le paiement échoue (carte refusée, erreur technique), le statut passe à REFUSED ou ERROR. Le client peut réessayer en recliquant sur le lien de paiement tant que la demande n'est pas en statut terminal.

{
  "status": "REFUSED",
  "amount": 150.00,
  "amount_captured": 0.00
}