Paiements crypto pour les backends sur mesure
Un parcours indépendant de la pile technique pour intégrer Qvard à votre propre serveur : le modèle de commande, la création du paiement, le webhook, le rapprochement et une liste de contrôle de mise en production, avec des exemples en Python.

Si votre boutique, votre service ou votre plateforme tourne sur votre propre backend, vous n'avez pas besoin d'un plugin : Qvard demande un appel qui crée un paiement et un point de terminaison qui reçoit le résultat. Ce guide déroule l'intégration d'une façon qui convient à n'importe quel langage et framework, avec des exemples en Python, et couvre ce qui la rend fiable : le modèle de commande, l'idempotence, le rapprochement et une courte liste de contrôle de mise en production.
Ce dont vous avez besoin
- Un compte Qvard avec un projet, une clé API et la callback URL de votre webhook enregistrée dans le projet.
- Un point de terminaison HTTPS public sur votre serveur pour le webhook.
- Une table de commandes (ou son équivalent) dans laquelle un paiement peut être associé à une commande par votre propre identifiant.
Il n'y a pas encore de SDK ni de sandbox : l'intégration est du HTTP simple et les tests se font avec de petits montants réels. La référence des formats de requête est API de paiement crypto pour les développeurs ; ce guide montre comment ils s'assemblent.
Un modèle de commande minimal
Vos propres données déterminent la propreté de l'intégration. Un ensemble minimal de champs pour une commande :
id: votre numéro de commande ou de facture. C'est l'idque vous envoyez à Qvard et la clé qui revient dans le webhook.amount_usd: le montant en dollars américains, l'unité dans laquelle le paiement est créé.state: par exempleawaiting_payment,paid,cancelled. Marquez payé uniquement depuis le webhook.paid_atet, si vous voulez, l'iddu paiement Qvard issu de la réponse pour le support.
Évitez de mettre toute la logique de paiement dans la classe de commande. Un petit module qui crée le paiement et un point de terminaison qui applique le webhook gardent le code testable.
Créer le paiement
Quand le client choisit de payer, créez le paiement depuis votre backend avec un seul POST vers https://api.qvard.com/api/v1/payment. Envoyez la clé dans l'en-tête Authorization: Bearer et un JSON avec votre numéro de commande comme id (une chaîne), le montant comme price en dollars américains et, en option, un paymentSystemId pour ouvrir la page de paiement avec un actif et un réseau sélectionnés.
La réponse est 201 avec redirectUrl, l'adresse de la page de paiement hébergée. Redirigez le client vers elle, ou placez le lien dans un e-mail. Des règles qui valent la peine d'être codées dans le client :
- Délai et nouvelle tentative. Utilisez un délai et réessayez sur un délai dépassé ou un
5xx. Envoyer le mêmeidpour le même moyen de paiement renvoie le paiement existant ; une nouvelle tentative ne peut donc pas en créer un second. - Ne réessayez pas un
400(« Error validation », avec un message par champ) ni un401(mauvaise clé) sans rien changer. - Montants en dollars. Si votre boutique fixe ses prix dans une autre devise, convertissez avant l'appel.
import os
import requests
QVARD_URL = "https://api.qvard.com/api/v1/payment"
def create_payment(order_id: str, amount_usd: float) -> str:
"""Create a Qvard payment for an order and return the hosted payment page URL."""
response = requests.post(
QVARD_URL,
headers={"Authorization": "Bearer " + os.environ["QVARD_API_KEY"]},
json={"id": order_id, "price": amount_usd}, # id as a string, price in US dollars
timeout=15,
)
if response.status_code == 201:
return response.json()["redirectUrl"]
# 400 = "Error validation" with a per-field message, 401 = wrong API key
raise RuntimeError(f"Qvard {response.status_code}: {response.text}")Recevoir le webhook
Lorsqu'un paiement est terminé, Qvard envoie un POST à votre callback URL avec l'id et le status, signé dans l'en-tête X-Qvard-Signature. Votre point de terminaison doit :
- Vérifier la signature : HMAC-SHA256 avec votre clé secrète sur l'horodatage et le corps brut, comparée en temps constant ; sinon répondre
400. - N'agir que sur le statut
completed. - Mettre à jour la commande de façon conditionnelle, « payée si elle attend encore le paiement », pour que les répétitions ne fassent rien.
- Mettre le travail lent en file et répondre exactement HTTP 200.
Qvard réessaie toute autre réponse, un délai dépassé ou une redirection, cinq tentatives en environ six minutes ; les livraisons répétées sont donc normales. Les détails des nouvelles tentatives sont dans stratégie de nouvelles tentatives des webhooks dans les systèmes de paiement, et les règles de sécurité dans comment sécuriser les webhooks de paiement crypto.
import os
from fastapi import FastAPI, Request, Response
from qvard import Qvard
app = FastAPI()
qvard = Qvard(os.environ["QVARD_API_KEY"])
@app.post("/webhooks/qvard")
async def qvard_webhook(request: Request):
try:
body = qvard.webhooks.construct_event(await request.body(), request.headers.get("X-Qvard-Signature"), os.environ["QVARD_API_KEY"])
except ValueError:
return Response(status_code=401)
if body.get("status") == "completed":
# Only changes an order that is not paid yet, so repeated deliveries are harmless.
changed = await db.execute(
"UPDATE orders SET state = 'paid', paid_at = now() WHERE id = :id AND state = 'awaiting_payment'",
{"id": body.get("id")},
)
if changed:
await queue.enqueue("fulfil_order", body["id"])
return Response(status_code=200)Paiements partiels, échecs et vue du client
Décidez ce que chaque statut de paiement signifie pour une commande :
wait: en attente de paiement.partial: moins que le montant complet est arrivé ; aucun webhook de fin n'est envoyé. Gardez la commande ouverte, dites au client combien il manque, ou annulez et remboursez.completed: le montant complet est arrivé ; marquez la commande comme payée.rejected: proposez un autre moyen de payer.
Les URL de retour après paiement ne sont pas encore disponibles ; le client revient donc de lui-même : donnez-lui une page de commande qui affiche le statut réel et se rafraîchit pendant l'attente. Un remboursement est un transfert que vous envoyez vous-même, car un paiement crypto ne peut pas être annulé.
Rapprochement et liste de contrôle de mise en production
Ajoutez un contrôle quotidien qui compare les commandes que vous considérez payées ou non avec les paiements et le solde de votre espace Qvard ; un paiement affiché comme terminé alors que votre commande est impayée est une notification manquée, que vous pouvez renvoyer depuis l'espace client.
Avant la mise en production :
- la clé API est dans une variable d'environnement ou un gestionnaire de secrets, jamais dans le dépôt ;
- la callback URL est enregistrée, publique, en HTTPS et joignable depuis l'extérieur de votre réseau ;
- le point de terminaison répond exactement 200, rapidement, et est sûr s'il s'exécute deux fois ;
- vous avez payé un petit montant réel et vu la commande passer à payée ;
- vous avez fait échouer le point de terminaison une fois et vu Qvard réessayer sans double exécution ;
- vous avez retiré un petit montant vers votre portefeuille. Le retrait ne coûte que les frais de réseau ; le retrait vers un compte bancaire en monnaie fiat n'est pas disponible.
Les conditions commerciales sont sur la page des tarifs, et une facture de démonstration montre ce que voit votre client.
FAQ
Existe-t-il un SDK pour mon langage ?
Non. L'intégration est du HTTP simple : un POST pour créer un paiement et un webhook pour recevoir le résultat. Les exemples de ce guide montrent le schéma en Python, et il se transpose à tout langage.
Comment associer un paiement à une commande ?
Envoyez votre propre numéro de commande comme id à la création du paiement. Le même id revient dans le webhook.
Y a-t-il un sandbox pour tester ?
Pas encore. Utilisez de petits montants réels et une facture de démonstration pour voir la page de paiement.
Et si mon serveur est arrêté quand un paiement se termine ?
Qvard réessaie cinq fois en environ six minutes. Ensuite, renvoyez la notification du paiement depuis l'espace client ; un gestionnaire idempotent rend cela sûr.
