Source: https://cryptanio.com/fr/wiki/confirmer-commandes-paiement-crypto-webhook/
Language: fr

# Comment confirmer automatiquement le paiement d’une commande par webhook

Reliez une facture à une commande\, vérifiez les signatures webhook et protégez la livraison contre les doublons et les annulations\.

Mis à jour: 2026-11-14

Pour que votre site marque automatiquement une commande comme payée\, créez une [facture](<https://cryptanio.com/fr/wiki/quest-ce-quune-facture/>) dans Cryptanio et configurez un [webhook](<https://cryptanio.com/fr/wiki/quest-ce-quun-webhook/>) \: une notification envoyée à votre serveur lorsque l\'état du paiement change\. Le serveur vérifie l\'authenticité du message\, retrouve la commande et contrôle l\'état de la facture\. Il peut ensuite accorder l\'accès acheté ou transmettre la commande pour exécution\.

Prenons l\'exemple d\'une boutique qui crée une facture dont le prix est exprimé en dollars comme unité de compte\, tandis que l\'acheteur choisit une cryptomonnaie et un réseau disponibles sur la page de paiement\. Les fonds arrivent sur les adresses de la boutique\. Les commandes et la remise du produit restent gérées par son application \; Cryptanio relie la facture au transfert et signale son état\. Cette intégration nécessite un développeur ou la possibilité de modifier le code serveur du site\.

## Préparez le projet et les adresses de paiement {#pr-parez-le-projet-et-les-adresses-de-paiement}

Dans votre compte Cryptanio\, sélectionnez un projet et ajoutez vos adresses de réception\. Connectez\-les au service Payments \: une simple surveillance de l\'adresse ne suffit pas pour émettre des factures\. Vérifiez qu\'elles sont actives et correspondent au réseau choisi\. Les paiements réels nécessitent des adresses en mode de fonctionnement réel\.

Prévoyez plusieurs adresses si les clients peuvent régler des commandes simultanément\. Une adresse peut être occupée par une facture ouverte ou temporairement indisponible après sa clôture\. L\'intégration doit afficher une erreur claire lorsqu\'aucune adresse n\'est libre et permettre une nouvelle tentative ultérieure\.

Créez une clé API et conservez\-la sur le serveur de la boutique\. Elle servira aux appels vers ` https://api.cryptanio.com/v1 `\. Ne la placez pas dans le JavaScript de la page\, une application mobile ou un dépôt public\.

## Configurez l\'adresse de réception des notifications {#configurez-l-adresse-de-r-ception-des-notifications}

Ouvrez les « Notifications » du projet choisi\. Si « Utiliser les paramètres de notification du compte » est activé\, configurez Webhook via « Paramètres du compte »\, ou désactivez l’héritage et définissez l’adresse propre au projet\.

Dans la carte Webhook\, indiquez l’adresse HTTPS de votre gestionnaire dans « Point final URL » et cliquez sur « Enregistrer »\. Le gestionnaire doit accepter directement les requêtes POST \: la redirection vers une autre URL n’est pas utilisée\. Récupérez le « Secret de signature » dans la même carte et conservez\-le sur le serveur\, séparément de la clé API\.

Vérifiez que Webhook n’est pas en pause\. Traitez ` payment.confirmed ` et ` payment.rolled_back ` dans votre gestionnaire\, et utilisez ` payment.pending ` pour un statut intermédiaire si nécessaire\. Avant d’exécuter la commande\, vérifiez le mode réel et les autres conditions ci\-dessous\. Il n’est pas nécessaire d’activer un canal Webhook séparé pour l’adresse de la facture\.

## Créez une facture pour une commande précise {#cr-ez-une-facture-pour-une-commande-pr-cise}

Enregistrez d\'abord la commande dans la base de la boutique\. Le serveur crée ensuite la facture avec ` POST /v1/invoices `\. Transmettez votre identifiant de commande dans ` reference `\. Dans cet exemple\, le prix de la commande est de 12\.50 USD \: ` invoice_currency ` et ` invoice_amount ` définissent le prix\, tandis que l\'acheteur choisit un réseau et un actif disponibles sur la page de paiement\. N\'envoyez donc pas ` chain `\, ` asset `\, ` amount `\, ` address ` ni ` tolerance ` dans cette requête\. Indiquer USD ne signifie ni accepter des fonds en monnaie fiduciaire ni convertir les cryptomonnaies reçues en dollars\.

```bash
#!/usr/bin/env bash
set -euo pipefail
: "${CRYPTANIO_API_KEY:?Set CRYPTANIO_API_KEY}"
: "${CRYPTANIO_OPERATION_ID:?Set CRYPTANIO_OPERATION_ID}"
: "${CRYPTANIO_PROJECT_ID:?Set CRYPTANIO_PROJECT_ID}"
json_string() {
  local text="$1" char code index output='"'
  for ((index=0; index<${#text}; index++)); do
    char="${text:index:1}"
    case "$char" in
      '"'|'\') output+="\\$char" ;;
      *) printf -v code '%d' "'$char"
         if ((code < 32)); then printf -v char '\\u%04x' "$code"; fi
         output+="$char" ;;
    esac
  done
  printf '%s"' "$output"
}
response_file="$(mktemp)"
trap 'rm -f "$response_file"' EXIT
transfer_status=0
http_status="$(curl \
  --silent --show-error --max-time 30 --request POST \
  --header "Authorization: Bearer $CRYPTANIO_API_KEY" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header "Idempotency-Key: $CRYPTANIO_OPERATION_ID" \
  --data-binary '{"project":'"$(json_string "$CRYPTANIO_PROJECT_ID")"',"invoice_currency":"USD","invoice_amount":"12.50","reference":"order-42"}' \
  "https://api.cryptanio.com/v1/invoices" \
  --output "$response_file" --write-out '%{http_code}')" || transfer_status=$?
if [[ "$http_status" == 2?? ]]; then
  cat "$response_file"
  exit "$transfer_status"
fi
printf 'HTTP %s\n' "$http_status" >&2
cat "$response_file" >&2
exit 1
```

[Télécharger l’exemple](</wiki/examples/ace23bd7c3cba0e47dafdcd6075a506ddb2ec8d6581c6129af815c1558f5a8fc.sh>)

Avant d\'exécuter l\'exemple\, définissez ` CRYPTANIO_API_KEY `\, ` CRYPTANIO_PROJECT_ID ` et ` CRYPTANIO_OPERATION_ID ` sur votre serveur\. Utilisez une clé API de production autorisée à créer des factures \; ` CRYPTANIO_PROJECT_ID ` doit désigner le projet Payments préparé\. ` CRYPTANIO_OPERATION_ID ` est l\'identifiant enregistré de l\'opération de création pour cette commande \: de 16 à 80 lettres ASCII\, chiffres\, tirets ou traits de soulignement\. L\'exemple le transmet dans l\'en\-tête ` Idempotency-Key `\, et non dans un champ JSON\. Générez une autre valeur pour une nouvelle commande \; conservez la même valeur pour réessayer la même requête\.

Si la création réussit\, l\'API renvoie HTTP 201\. Enregistrez l\'` id ` de la facture et le ` checkout_url ` reçu avec la commande\, puis dirigez l\'acheteur vers cette URL\. Tant qu\'aucun moyen de paiement n\'est choisi\, le réseau\, l\'adresse et le montant en cryptomonnaie ne sont pas encore attribués\. Sur la page de paiement\, l\'acheteur choisit un moyen disponible et obtient les coordonnées et l\'échéance en vigueur\. Si votre propre interface affiche les coordonnées de paiement\, utilisez les données de la tentative concernée\, le montant exact ` amount ` et son échéance ` expires_at ` \; n\'arrondissez pas le montant et ne le remplacez pas par ` asked `\. Les liens renvoyés par les requêtes GET ultérieures et la liste des factures sont en lecture seule \; conservez séparément le lien interactif reçu lors de la création et ne le communiquez qu\'à l\'acheteur concerné\.

Si la connexion est interrompue après l\'envoi\, renvoyez le même corps avec le même ` Idempotency-Key `\. Une nouvelle tentative réussie renvoie HTTP 201 et le même ` id `\. Modifier les paramètres avec une clé déjà utilisée entraîne HTTP 409\. ` reference ` relie la facture à la commande\, mais ne remplace pas la clé d\'idempotence\. Ne changez pas de clé simplement parce que la requête a expiré\.

## Vérifiez l\'authenticité du webhook {#v-rifiez-l-authenticit-du-webhook}

Cryptanio transmet ` X-Timestamp `\, ` X-Signature `\, ` X-Event-Id `\, ` X-Event-Type ` et ` X-Attempt `\. La signature se calcule ainsi \:

```text
hex(HMAC-SHA256(secret, timestamp + "." + raw_body))
```

` raw_body ` désigne les octets originaux de la requête\. Récupérez\-les avant l\'analyse du JSON \: une nouvelle sérialisation peut changer les espaces ou l\'ordre des champs et faire échouer la vérification\. Limitez la taille du corps accepté et refusez les requêtes dépourvues de signature valide\.

L\'exemple ci\-dessous utilise une fenêtre de fraîcheur de cinq minutes\. Il s\'agit d\'un réglage de votre gestionnaire \; l\'horloge du serveur doit être synchronisée pour qu\'il fonctionne correctement\.

```python
import hashlib
import hmac
import re
import time

def verify_signature(secret, timestamp, raw_body, signature):
    if not secret or not timestamp or not re.fullmatch(r"[0-9]{1,12}", timestamp):
        return False
    if not signature or not re.fullmatch(r"[0-9a-f]{64}", signature):
        return False
    if abs(time.time() - int(timestamp)) > 300:
        return False
    signed = timestamp.encode("ascii") + b"." + raw_body
    expected = hmac.new(secret.encode("utf-8"), signed,
                        hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)
```

La fonction vérifie la signature et l\'heure de la requête\. Après validation\, analysez le JSON et contrôlez ` schema_version="v1" `\, le ` project_id ` attendu\, ` mode="live" ` et ` sandbox=false `\.

Pour prendre une décision\, utilisez ` event_id ` et ` event_type ` du corps signé\. Vous pouvez comparer les en\-têtes correspondants à ces valeurs \; ces en\-têtes d\'événement ne sont pas eux\-mêmes protégés par la signature\. Ne lancez la logique de paiement que pour l\'objet ` payment ` attendu\, de direction ` in `\.

## Distinguez confirmation du transfert et règlement de la commande {#distinguez-confirmation-du-transfert-et-r-glement-de-la-commande}

Les principaux événements pour la boutique sont \:

- ` payment.pending ` \: le transfert est détecté et attend sa confirmation \;
- ` payment.confirmed ` \: le transfert a atteint le nombre de confirmations requis \;
- ` payment.rolled_back ` \: un transfert précédemment détecté a été annulé à la suite d\'un changement de la chaîne de blocs\.

Un événement ` payment.confirmed ` ne prouve pas encore qu\'une commande précise est intégralement réglée\. Vérifiez l\'objet ` invoice ` et comparez ` invoice.id ` et ` invoice.reference ` à la commande enregistrée\. Un message sans facture associée ne doit pas être rattaché automatiquement à un achat sur la seule ressemblance du montant\.

Avant toute remise\, demandez l\'état actuel avec ` GET /v1/invoices/{id} `\. Vérifiez ` id `\, ` project_id `\, ` reference `\, le prix de la commande dans ` invoice_currency ` et ` invoice_amount `\, ainsi que ` sandbox=false `\. Contrôlez le réseau\, l\'actif\, l\'adresse et la tentative de paiement choisie \; en présence de plusieurs tentatives\, tenez compte de ` settled_attempt_id ` et ` settled_attempt `\. Un paiement réussi correspond au statut ` paid ` de la facture \; ` pending `\, ` seen ` et ` partial ` n\'autorisent pas la remise\. Le retour de l\'acheteur sur votre site ou l\'ouverture du lien de paiement ne confirme pas le paiement\.

Un montant insuffisant peut avoir le statut ` underpaid `\, un paiement partiel en attente d\'un complément a le statut ` partial `\, et un paiement tardif a le statut ` late `\. En cas de trop\-perçu\, la politique du projet peut laisser la facture en ` paid ` ou lui attribuer ` overpaid ` avec ` needs_decision=true `\. Les états ` underpaid `\, ` overpaid ` et ` late ` exigent une décision distincte avant l\'exécution de la commande\. L\'API transmet ` amount_raw ` et ` paid ` sous forme de chaînes\. Pour les comparer\, interprétez\-les comme des entiers exacts dans les plus petites unités de l\'actif \; ` decimals ` définit l\'échelle\. N\'utilisez pas de calcul en virgule flottante binaire\. Définissez à l\'avance le traitement des écarts \; aucun remboursement automatique n\'en découle\.

## Gérez les répétitions et les annulations {#g-rez-les-r-p-titions-et-les-annulations}

La livraison peut répéter des messages\. Enregistrez l\'événement vérifié dans un stockage durable et utilisez ` event_id ` comme clé unique\. La répétition d\'un événement déjà accepté doit recevoir une réponse positive sans déclencher une nouvelle remise du produit\.

Le seul identifiant de transaction ne permet pas d\'écarter tous les messages suivants \: un transfert passe par plusieurs états\. Pour la combinaison de ` chain `\, ` tx_id `\, ` index `\, ` address ` et de l\'identité de l\'actif\, enregistrez la dernière ` payment.revision ` appliquée\. N\'appliquez une transition que si sa revision est supérieure à celle déjà traitée\.

Enregistrez de façon cohérente le changement de commande et la tâche de remise\, avec une protection contre les traitements simultanés\. La tâche doit avoir une clé de commande unique\. Juste avant son exécution\, vérifiez que la remise reste autorisée\. Si une annulation arrive\, mettez à jour l\'état et stoppez les remises non exécutées \; pour un accès déjà accordé\, prévoyez un réexamen ou une suspension\.

## Vérifiez le parcours complet {#v-rifiez-le-parcours-complet}

Avant un paiement réel\, testez l’analyse du corps\, la signature et la protection contre les doublons avec des données préparées\. Un événement de service ` webhook.test ` ne confirme aucun achat et ne remplace pas la vérification du parcours complet\.

Utilisez ensuite votre propre commande de contrôle pour vérifier tout le processus \: création de facture\, transfert\, confirmation et mise à jour de commande\. Testez séparément les répétitions\, le paiement insuffisant et l\'annulation sur des données préparées\. Renvoyez HTTP 2xx après l\'enregistrement fiable du message accepté \; si cet enregistrement est temporairement impossible\, renvoyez une erreur serveur pour permettre une nouvelle livraison\.

Dans le compte\, comparez l\'événement et le journal de livraison à l\'enregistrement de votre application\. La vérification est terminée lorsque la commande confirmée est exécutée une seule fois et qu\'un événement intermédiaire\, étranger ou répété ne déclenche pas une remise incorrecte\.

Références \: [factures de l\'API v1](<https://cryptanio.com/fr/docs.html#invoices>)\, [vérification de la signature webhook](<https://cryptanio.com/fr/docs.html#verify>)\.

Étiquettes: [Paiements](https://cryptanio.com/fr/wiki/tag/payments/), [Webhooks](https://cryptanio.com/fr/wiki/tag/webhooks/)

## Articles associés

- [Qu’est ce qu’une facture](https://cryptanio.com/fr/wiki/quest-ce-quune-facture/)
- [Qu’est ce qu’un webhook](https://cryptanio.com/fr/wiki/quest-ce-quun-webhook/)
- [Comment accepter des paiements en cryptomonnaie dans un bot Telegram](https://cryptanio.com/fr/wiki/accepter-paiements-crypto-bot-telegram/)
