Développeurs/Intégrer
Catalogue et commandes
Ce guide décrit tout le parcours d'achat : consulter votre solde, trouver un produit, obtenir son prix, passer commande et récupérer les codes.
Voir aussi : Authentification · Conventions · Webhooks
#Compte et solde
#Compte
GET/api/v1/account affiche les informations de votre entreprise et indique si l'accès API est activé.
{
"merchant_id": "M00000001",
"name": "Acme Shop",
"legal_name": "Acme Shop Ltd",
"tier": "standard",
"billing_email": "[email protected]",
"status": "active",
"kyb_status": "approved",
"api_access_enabled": true,
"default_currency": "USD"
}default_currencyest la devise de votre portefeuille. Tous les prix que vous payez sont dans cette devise.api_access_enabledpasse àtruedès que CardV a validé votre entreprise.
#Solde
GET/api/v1/balance indique combien vous pouvez dépenser.
{
"currency": "USD",
"balance": "1520.4000",
"reserved_amount": "0.0000",
"available_balance": "1520.4000",
"low_balance_threshold": "200.0000",
"low_balance_notified_at": null,
"is_active": true
}available_balanceest le montant que vous pouvez dépenser tout de suite :balancemoinsreserved_amount.- Une commande supérieure à
available_balanceest refusée, et rien n'est débité. low_balance_thresholdest le seuil qui déclenche l'e-mail de solde bas. Vous le réglez dans le Portail.- Pour ajouter des fonds, passez par le Portail.
#Produits (SKU)
Un SKU est un produit que vous pouvez acheter, par exemple « Steam Wallet 10 USD ».
Son identifiant ressemble à S000456. C'est sur les SKU que vous demandez un prix et passez commande.
GET/api/v1/skus liste les SKU que vous pouvez acheter. Exemple :
GET /api/v1/skus?search=steam®ion=US&limit=50&offset=0Filtres (tous facultatifs) :
| Filtre | Exemple | Recherche dans |
|---|---|---|
search | steam | Identifiant de SKU, nom ou marque |
brand | Steam | Nom de la marque (majuscules ou minuscules) |
region | US | Code ou nom du pays |
vertical | gift_card | Gamme de produits |
product_type | pin_code | Mode de livraison |
Pagination : envoyez limit (100 par défaut, 500 au maximum) et offset.
Augmentez offset à chaque appel jusqu'à ce qu'il atteigne count.
{
"count": 7,
"limit": 1,
"results": [
{
"sku_id": "S000456",
"product_id": "P000123",
"name": "Steam Wallet 10 USD",
"product_name": "Steam Wallet US",
"brand": "Steam",
"region": "US",
"vertical": "gift_card",
"product_type": "pin_code",
"denomination_type": "fixed",
"denomination_value": "10.0000",
"face_currency": "USD",
"merchant_price": "9.2500",
"settlement_currency": "USD",
"availability": "available",
"min_quantity": 1,
"max_quantity": 100,
"required_input_schema": [],
"...": "more fields"
}
],
"filter_options": {"brands": [], "regions": [], "verticals": []}
}GET/api/v1/skus/{sku_id} renvoie un seul SKU, avec les mêmes champs.
Les champs les plus utiles :
| Champ | Signification |
|---|---|
sku_id | L'identifiant à utiliser pour obtenir le prix et commander. |
merchant_price | Votre prix unitaire, en settlement_currency. |
availability | available ou unavailable. Ne commandez que des SKU available. |
denomination_type | fixed ou range. Voir montants fixes et montants libres. |
face_currency | Devise inscrite sur la carte. Elle peut différer de celle de votre portefeuille. |
min_quantity, max_quantity | Nombre d'unités autorisé par ligne de commande. |
product_type | pin_code (vous recevez un code) ou direct_charge (nous rechargeons un compte). |
required_input_schema | Informations à fournir pour les recharges directes. |
brand_logo_url, image_url | Images hébergées par CardV, ou "". |
description, redemption_instructions, terms | Textes que vous pouvez montrer à vos clients. |
Conseils :
- Vous ne voyez que les SKU actifs et ouverts à votre compte. Les autres renvoient 404.
- Synchronisez la liste des SKU toutes les 5 à 15 minutes. Demandez toujours le prix juste avant de commander.
filter_optionsliste les marques, les pays et les gammes de produits sur lesquels vous pouvez filtrer.
#Montants fixes et montants libres
La plupart des SKU ont une valeur fixe, par exemple 10 USD. D'autres ont un montant libre dans une fourchette : votre client choisit le montant, par exemple entre 5 et 500 USD.
| Type | Pour obtenir le prix | Pour commander |
|---|---|---|
fixed | Envoyez quantity | N'envoyez pas amount |
range | Envoyez quantity et amount | Envoyez amount |
Pour un SKU à montant libre, amount doit être compris entre min_face_value et max_face_value.
Il est exprimé en face_currency.
#Recharges directes
Certains produits rechargent directement le compte de votre client, par exemple un compte de jeu. Pour ces produits, CardV a besoin des informations du compte. Le SKU indique lesquelles :
"required_input_schema": [
{"key": "player_id", "label": "Player ID", "required": true},
{"key": "server", "label": "Server", "required": false}
]Envoyez les valeurs dans le champ inputs de la ligne de commande, avec chaque key :
"inputs": {"player_id": "123456789", "server": "EU"}- Un champ est obligatoire, sauf s'il porte
"required": false. - S'il manque une valeur obligatoire, la commande est refusée avec une erreur
items. - Ces valeurs sont des données personnelles de votre client. Protégez-les (voir Sécurité).
#Prix actuel
Une demande de prix (quote) vous donne le prix actuel pour une quantité donnée.
GET /api/v1/skus/S000456/quote?quantity=2
GET /api/v1/skus/S000789/quote?quantity=1&amount=25.00{
"sku_id": "S000456",
"settlement_currency": "USD",
"merchant_price": "9.2500",
"quantity": 2,
"total_price": "18.5000",
"min_quantity": 1,
"max_quantity": 100,
"availability": "available"
}- Le prix obtenu n'est pas garanti. Il peut changer à tout moment.
- Pour vous protéger, renvoyez
merchant_pricedansexpected_unit_pricequand vous commandez. Si le prix a changé entre-temps, CardV refuse la commande et ne débite rien. - Une quantité ou un montant hors limites renvoie HTTP 400 avec une erreur
quantityouamount.
#Passer une commande
POST/api/v1/orders achète un ou plusieurs SKU et les paie avec votre portefeuille.
Cet appel doit être signé.
{
"external_order_id": "SHOP-20260929-10001",
"items": [
{"sku_id": "S000456", "quantity": 2, "expected_unit_price": "9.2500"},
{"sku_id": "S000789", "amount": "25.00", "expected_unit_price": "23.7500"},
{
"sku_id": "S000900",
"expected_unit_price": "4.9000",
"inputs": {"player_id": "123456789"}
}
]
}| Champ | Obligatoire | Signification |
|---|---|---|
external_order_id | Oui | Votre numéro de commande, de 1 à 120 caractères. Il doit être unique. |
items | Oui | Une ou plusieurs lignes de commande. |
items[].sku_id | Oui | Le SKU à acheter. |
items[].quantity | Non | La quantité. 1 par défaut. |
items[].amount | SKU à montant libre | La valeur faciale à acheter. |
items[].expected_unit_price | Recommandé | Le merchant_price obtenu. Envoyez-le toujours. |
items[].inputs | Recharges directes | Informations du compte pour les recharges directes. |
Dès que CardV accepte la commande, il débite immédiatement le montant total de votre portefeuille. La livraison démarre ensuite en arrière-plan.
La réponse est un HTTP 201 :
{
"idempotent_replay": false,
"order": {
"order_id": "O-00001234",
"external_order_id": "SHOP-20260929-10001",
"status": "accepted",
"total_amount": "46.2000",
"...": "more fields"
}
}Enregistrez order.order_id. Cette réponse ne contient jamais de codes : vous les récupérerez ensuite (voir Consulter une commande).
#Commandes refusées
Une commande refusée renvoie HTTP 400 et rien n'est débité. La clé d'erreur indique la raison :
| Clé | Cause | Que faire |
|---|---|---|
items | Prix modifié, SKU indisponible, montant incorrect ou information manquante | Redemandez le prix, corrigez, renvoyez |
balance | Solde insuffisant dans votre portefeuille | Ajoutez des fonds dans le Portail |
risk | Plafond par commande ou plafond quotidien dépassé | Contactez CardV |
external_order_id | Votre numéro de commande a déjà servi pour une autre commande | Voir Renvoyer sans risque |
Exemple de changement de prix :
{
"items": "SKU S000456 price changed from 9.2500 to 9.4100 USD; refresh quote and confirm again."
}Votre formule fixe les plafonds par commande, de dépense quotidienne et de nombre de commandes par jour. Les plafonds quotidiens repartent à zéro à 00:00 UTC. Demandez vos plafonds à CardV.
#Renvoyer sans risque
Votre numéro de commande (external_order_id) vous évite d'acheter deux fois.
Si vous renvoyez la même commande avec le même numéro, CardV ne vous débite pas une seconde fois.
Il vous renvoie la commande déjà enregistrée.
| Vous envoyez | Vous recevez |
|---|---|
| Un nouveau numéro de commande | HTTP 201. Une nouvelle commande. Votre portefeuille est débité. |
| Le même numéro et la même commande | HTTP 200 et "idempotent_replay": true. La commande existante. Aucun débit. |
| Le même numéro mais une commande différente | HTTP 400 sur external_order_id. Il ne se passe rien. |
« La même commande » signifie les mêmes lignes, dans le même ordre, avec les mêmes SKU, quantités, montants et inputs.
Si vous envoyez expected_unit_price, il doit être identique à celui de la première commande.
Une commande en double est détectée avant la vérification du solde et du prix. Elle renvoie donc toujours la première commande, même si le prix a changé depuis.
#Renvoyer une commande pas à pas
Si vous n'obtenez pas de réponse claire, renvoyez simplement la même commande.
POST /orders avec le numéro de commande R
├─ 201 ou 200 → enregistrez order_id. Terminé.
├─ 400 items / balance / risk → aucune commande n'a été créée.
│ Corrigez la cause et renvoyez. Vous pouvez réutiliser R.
├─ 400 external_order_id → R correspond à une autre commande. Arrêtez et vérifiez.
├─ 403 erreur de signature → signez à nouveau et envoyez le même corps.
├─ 429 → attendez Retry-After, signez à nouveau, envoyez le même corps.
└─ délai dépassé, 5xx ou connexion perdue
→ renvoyez le même corps avec le même R.
Vous recevez 201 (le premier envoi n'était pas arrivé) ou 200 (il était arrivé).Règles :
- Ne créez jamais un nouveau numéro de commande parce qu'une réponse s'est perdue. Si la première requête était bien arrivée, un nouveau numéro vous ferait tout acheter deux fois.
- Chaque renvoi demande un nouvel horodatage, un nouveau nonce et une nouvelle signature. Le corps, lui, ne change pas.
#Consulter une commande
GET/api/v1/orders/{order_id} renvoie la commande, son statut et ses codes.
{
"order_id": "O-00001234",
"external_order_id": "SHOP-20260929-10001",
"status": "succeeded",
"currency": "USD",
"total_amount": "18.5000",
"created_at": "2026-09-29T08:15:30.123456Z",
"updated_at": "2026-09-29T08:15:41.004211Z",
"items": [
{
"sku_id": "S000456",
"product_name": "Steam Wallet US",
"quantity": 2,
"unit_price": "9.2500",
"total_price": "18.5000",
"delivery_count": 2,
"deliveries": [{"...": "see Codes below"}]
}
],
"...": "more fields"
}total_amountest le montant débité au moment où la commande a été acceptée.items[].unit_priceest le prix bloqué pour cette commande.items[].deliveriescontient les codes complets. Traitez cette réponse comme confidentielle.invoice_urletdelivery_file_urlsont les chemins de la facture et du fichier CSV des codes. Ils sont réservés au Portail : avec une clé API, ils renvoient HTTP 403.- La réponse contient aussi
id(un ancien numéro, à ne pas utiliser),events(un historique destiné à l'affichage) et l'avancement de la livraison ligne par ligne. Vous pouvez les ignorer. - Un numéro de commande inconnu renvoie HTTP 404.
#Statut de la commande et codes
#Statut
accepted ──► processing ──► succeeded
│
├──► partially_succeeded (une partie des lignes livrée, le reste non)
│
└──► failed ──► refunded (argent rendu à votre portefeuille)| Statut | Terminée ? | Que faire |
|---|---|---|
accepted | Non | Attendez. Le portefeuille est débité, la livraison n'a pas commencé. |
processing | Non | Attendez. Ne repassez pas la commande. |
succeeded | Oui | Récupérez les codes et remettez-les à votre client. |
partially_succeeded | Oui | Livrez ce qui est arrivé. Le reste sera remboursé plus tard. |
failed | Pas encore | Attendez refunded. Un échec n'est pas encore un remboursement. |
refunded | Oui | L'argent est revenu dans votre portefeuille. |
Si vous n'utilisez pas les webhooks, interrogez l'API à ce rythme : après 5 secondes, puis 10 s, 30 s, 60 s, puis toutes les 5 minutes. Restez sous la limite de requêtes. La plupart des commandes se terminent en quelques secondes. Certaines demandent une vérification manuelle et peuvent prendre plusieurs heures.
#Codes
Chaque unité livrée correspond à un objet dans items[].deliveries :
{
"status": "stored",
"delivery_type": "card_pin",
"display_fields": [
{"key": "card_number", "label": "Card number", "value": "X1234", "kind": "secret"},
{"key": "pin_code", "label": "PIN", "value": "9876", "kind": "secret"}
],
"redeem_url": "",
"expiry_date": "2027-09-29",
"instructions": "Redeem at ...",
"is_masked": false
}- Montrez à votre client les
display_fields: chacun comporte unlabelet unevalue. Montrez aussiredeem_url,expiry_dateetinstructionslorsqu'ils ne sont pas vides. kindvautsecretpour les codes et les PIN, etreferencepour des éléments comme les numéros de série.delivery_typeindique ce que vous avez reçu :code,card_pin,link,code_linkouqr. De nouveaux types peuvent apparaître : construisez donc toujours l'affichage à partir dedisplay_fields.- Pour les livraisons
link, leredeem_urlest lui-même le code. Gardez-le secret. - Ne remettez jamais à votre client une unité dont le
statusestvoided. - Les produits à recharge directe n'ont généralement pas de livraison.
succeededsignifie que le compte a été rechargé. - Certaines valeurs sont aussi reprises dans des champs séparés, comme
card_numberetpin_code. Ces champs peuvent être vides. - Les webhooks ne contiennent jamais de codes. Consultez la commande après avoir reçu un webhook.
Une question sur votre intégration ? Écrivez à [email protected] en indiquant votre Merchant ID et l’identifiant de commande ou de requête.