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é.

JSON
{
  "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_currency est la devise de votre portefeuille. Tous les prix que vous payez sont dans cette devise.
  • api_access_enabled passe à true dès que CardV a validé votre entreprise.

#Solde

GET/api/v1/balance indique combien vous pouvez dépenser.

JSON
{
  "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_balance est le montant que vous pouvez dépenser tout de suite : balance moins reserved_amount.
  • Une commande supérieure à available_balance est refusée, et rien n'est débité.
  • low_balance_threshold est 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 :

HTTP
GET /api/v1/skus?search=steam&region=US&limit=50&offset=0

Filtres (tous facultatifs) :

FiltreExempleRecherche dans
searchsteamIdentifiant de SKU, nom ou marque
brandSteamNom de la marque (majuscules ou minuscules)
regionUSCode ou nom du pays
verticalgift_cardGamme de produits
product_typepin_codeMode de livraison

Pagination : envoyez limit (100 par défaut, 500 au maximum) et offset. Augmentez offset à chaque appel jusqu'à ce qu'il atteigne count.

JSON
{
  "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 :

ChampSignification
sku_idL'identifiant à utiliser pour obtenir le prix et commander.
merchant_priceVotre prix unitaire, en settlement_currency.
availabilityavailable ou unavailable. Ne commandez que des SKU available.
denomination_typefixed ou range. Voir montants fixes et montants libres.
face_currencyDevise inscrite sur la carte. Elle peut différer de celle de votre portefeuille.
min_quantity, max_quantityNombre d'unités autorisé par ligne de commande.
product_typepin_code (vous recevez un code) ou direct_charge (nous rechargeons un compte).
required_input_schemaInformations à fournir pour les recharges directes.
brand_logo_url, image_urlImages hébergées par CardV, ou "".
description, redemption_instructions, termsTextes 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_options liste 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.

TypePour obtenir le prixPour commander
fixedEnvoyez quantityN'envoyez pas amount
rangeEnvoyez quantity et amountEnvoyez 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 :

JSON
"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 :

JSON
"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.

HTTP
GET /api/v1/skus/S000456/quote?quantity=2
GET /api/v1/skus/S000789/quote?quantity=1&amount=25.00
JSON
{
  "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_price dans expected_unit_price quand 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 quantity ou amount.

#Passer une commande

POST/api/v1/orders achète un ou plusieurs SKU et les paie avec votre portefeuille. Cet appel doit être signé.

JSON
{
  "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"}
    }
  ]
}
ChampObligatoireSignification
external_order_idOuiVotre numéro de commande, de 1 à 120 caractères. Il doit être unique.
itemsOuiUne ou plusieurs lignes de commande.
items[].sku_idOuiLe SKU à acheter.
items[].quantityNonLa quantité. 1 par défaut.
items[].amountSKU à montant libreLa valeur faciale à acheter.
items[].expected_unit_priceRecommandéLe merchant_price obtenu. Envoyez-le toujours.
items[].inputsRecharges directesInformations 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 :

JSON
{
  "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éCauseQue faire
itemsPrix modifié, SKU indisponible, montant incorrect ou information manquanteRedemandez le prix, corrigez, renvoyez
balanceSolde insuffisant dans votre portefeuilleAjoutez des fonds dans le Portail
riskPlafond par commande ou plafond quotidien dépasséContactez CardV
external_order_idVotre numéro de commande a déjà servi pour une autre commandeVoir Renvoyer sans risque

Exemple de changement de prix :

JSON
{
  "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 envoyezVous recevez
Un nouveau numéro de commandeHTTP 201. Une nouvelle commande. Votre portefeuille est débité.
Le même numéro et la même commandeHTTP 200 et "idempotent_replay": true. La commande existante. Aucun débit.
Le même numéro mais une commande différenteHTTP 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.

Text
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.

JSON
{
  "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_amount est le montant débité au moment où la commande a été acceptée.
  • items[].unit_price est le prix bloqué pour cette commande.
  • items[].deliveries contient les codes complets. Traitez cette réponse comme confidentielle.
  • invoice_url et delivery_file_url sont 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

Text
accepted ──► processing ──► succeeded
                  │
                  ├──► partially_succeeded   (une partie des lignes livrée, le reste non)
                  │
                  └──► failed ──► refunded   (argent rendu à votre portefeuille)
StatutTerminée ?Que faire
acceptedNonAttendez. Le portefeuille est débité, la livraison n'a pas commencé.
processingNonAttendez. Ne repassez pas la commande.
succeededOuiRécupérez les codes et remettez-les à votre client.
partially_succeededOuiLivrez ce qui est arrivé. Le reste sera remboursé plus tard.
failedPas encoreAttendez refunded. Un échec n'est pas encore un remboursement.
refundedOuiL'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 :

JSON
{
  "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 un label et une value. Montrez aussi redeem_url, expiry_date et instructions lorsqu'ils ne sont pas vides.
  • kind vaut secret pour les codes et les PIN, et reference pour des éléments comme les numéros de série.
  • delivery_type indique ce que vous avez reçu : code, card_pin, link, code_link ou qr. De nouveaux types peuvent apparaître : construisez donc toujours l'affichage à partir de display_fields.
  • Pour les livraisons link, le redeem_url est lui-même le code. Gardez-le secret.
  • Ne remettez jamais à votre client une unité dont le status est voided.
  • Les produits à recharge directe n'ont généralement pas de livraison. succeeded signifie que le compte a été rechargé.
  • Certaines valeurs sont aussi reprises dans des champs séparés, comme card_number et pin_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.