Entwickler/Integration
Produkte und Bestellungen
Diese Anleitung führt Sie durch den gesamten Einkauf: Guthaben prüfen, Produkt suchen, Preis abfragen, bestellen und Codes abrufen.
Siehe auch: Authentifizierung · Grundregeln · Webhooks
#Konto und Guthaben
#Konto
GET/api/v1/account zeigt Ihre Firmendaten und ob die API für Sie freigeschaltet ist.
{
"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_currencyist die Währung Ihrer Wallet. Alle Preise, die Sie bezahlen, sind in dieser Währung.api_access_enabledisttrue, sobald CardV Ihr Unternehmen freigegeben hat.
#Guthaben
GET/api/v1/balance zeigt, wie viel Guthaben Sie ausgeben können.
{
"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_balancekönnen Sie jetzt ausgeben. Es istbalanceminusreserved_amount.- Eine Bestellung über
available_balancehinaus wird abgelehnt. Es wird nichts abgebucht. low_balance_thresholdist die Schwelle für die E-Mail bei niedrigem Guthaben. Sie legen sie im Portal fest.- Guthaben laden Sie im Portal auf.
#Produkte (SKUs)
Eine SKU ist ein einzelnes Produkt, das Sie kaufen können, zum Beispiel „Steam Wallet 10 USD“.
Ihre ID sieht so aus: S000456. Preisabfragen und Bestellungen beziehen sich immer auf eine SKU.
GET/api/v1/skus listet die SKUs auf, die Sie kaufen können. Beispiel:
GET /api/v1/skus?search=steam®ion=US&limit=50&offset=0Filter (alle optional):
| Filter | Beispiel | Findet |
|---|---|---|
search | steam | SKU-ID, Name oder Marke |
brand | Steam | Markenname (Groß-/Kleinschreibung egal) |
region | US | Ländercode oder Ländername |
vertical | gift_card | Produktbereich |
product_type | pin_code | Art der Lieferung |
Seitenweise Abfrage: Senden Sie limit (Standard 100, höchstens 500) und offset.
Fragen Sie mit steigendem offset weiter ab, bis offset den Wert count erreicht.
{
"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} liefert eine einzelne SKU mit denselben Feldern.
Die wichtigsten Felder:
| Feld | Bedeutung |
|---|---|
sku_id | Die ID für Preisabfrage und Bestellung. |
merchant_price | Ihr Preis pro Stück, in settlement_currency. |
availability | available oder unavailable. Bestellen Sie nur SKUs mit available. |
denomination_type | fixed oder range. Siehe feste und freie Beträge. |
face_currency | Die Währung auf der Karte. Kann von Ihrer Wallet-Währung abweichen. |
min_quantity, max_quantity | Wie viele Stück eine Bestellposition enthalten darf. |
product_type | pin_code (Sie erhalten einen Code) oder direct_charge (wir laden ein Konto direkt auf). |
required_input_schema | Angaben, die Sie für Direktaufladungen mitsenden müssen. |
brand_logo_url, image_url | Bilder, die CardV bereitstellt, oder "". |
description, redemption_instructions, terms | Texte, die Sie Ihren Kunden zeigen können. |
Tipps:
- Sie sehen nur SKUs, die aktiv und für Ihr Konto freigegeben sind. Andere SKUs liefern 404.
- Aktualisieren Sie die SKU-Liste alle 5–15 Minuten. Fragen Sie den Preis immer direkt vor der Bestellung ab.
filter_optionsnennt die Marken, Regionen und Produktbereiche, nach denen Sie filtern können.
#Feste und freie Beträge
Die meisten SKUs haben einen festen Nennwert, zum Beispiel 10 USD. Einige SKUs haben einen Bereich: Ihr Kunde wählt den Betrag selbst, zum Beispiel 5 bis 500 USD.
| Typ | Bei der Preisabfrage | Bei der Bestellung |
|---|---|---|
fixed | quantity senden | amount weglassen |
range | quantity und amount senden | amount senden |
Bei einer SKU mit Bereich muss amount zwischen min_face_value und max_face_value liegen.
Der Betrag ist in face_currency angegeben.
#Direktaufladungen
Manche Produkte laden das Konto Ihres Kunden direkt auf, zum Beispiel ein Spielkonto. Dafür braucht CardV die Kontodaten. Welche das sind, steht in der SKU:
"required_input_schema": [
{"key": "player_id", "label": "Player ID", "required": true},
{"key": "server", "label": "Server", "required": false}
]Senden Sie die Werte in den inputs der Bestellposition, jeweils unter dem passenden key:
"inputs": {"player_id": "123456789", "server": "EU"}- Ein Feld ist Pflicht, außer es steht
"required": falsedabei. - Fehlt ein Pflichtwert, wird die Bestellung mit einem
items-Fehler abgelehnt. - Diese Werte sind personenbezogene Daten Ihrer Kunden. Schützen Sie sie (siehe Sicherheit).
#Preisabfrage
Eine Preisabfrage nennt Ihnen den aktuellen Preis für eine bestimmte Menge.
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"
}- Eine Preisabfrage reserviert den Preis nicht. Preise können sich jederzeit ändern.
- Zu Ihrem Schutz senden Sie
merchant_pricebei der Bestellung alsexpected_unit_pricemit. Hat sich der Preis geändert, lehnt CardV die Bestellung ab und bucht nichts ab. - Liegen Menge oder Betrag außerhalb des erlaubten Bereichs, erhalten Sie HTTP 400 mit einem
quantity- oderamount-Fehler.
#Bestellen
POST/api/v1/orders kauft eine oder mehrere SKUs und bezahlt aus Ihrer Wallet.
Diese Anfrage muss signiert sein.
{
"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"}
}
]
}| Feld | Pflicht | Bedeutung |
|---|---|---|
external_order_id | Ja | Ihre Bestellnummer, 1–120 Zeichen. Muss eindeutig sein. |
items | Ja | Eine oder mehrere Bestellpositionen. |
items[].sku_id | Ja | Die SKU, die Sie kaufen. |
items[].quantity | Nein | Anzahl. Standard ist 1. |
items[].amount | Bei SKUs mit Bereich | Der gewünschte Nennwert. |
items[].expected_unit_price | Empfohlen | Der abgefragte merchant_price. Immer mitsenden. |
items[].inputs | Bei Direktaufladungen | Kontodaten für Direktaufladungen. |
Nimmt CardV die Bestellung an, wird der gesamte Betrag sofort von Ihrer Wallet abgebucht. Die Lieferung läuft danach im Hintergrund.
Die Antwort kommt mit HTTP 201:
{
"idempotent_replay": false,
"order": {
"order_id": "O-00001234",
"external_order_id": "SHOP-20260929-10001",
"status": "accepted",
"total_amount": "46.2000",
"...": "more fields"
}
}Speichern Sie order.order_id. Diese Antwort enthält nie Codes. Die rufen Sie später ab (Bestellung abrufen).
#Abgelehnte Bestellungen
Eine abgelehnte Bestellung liefert HTTP 400. Es wird nichts abgebucht. Der Fehlerschlüssel nennt den Grund:
| Schlüssel | Ursache | Was tun |
|---|---|---|
items | Preis geändert, SKU nicht verfügbar, ungültiger Betrag oder fehlende Angabe | Preis neu abfragen, korrigieren, erneut senden |
balance | Nicht genug Guthaben in Ihrer Wallet | Guthaben im Portal aufladen |
risk | Bestellgröße oder Tageslimit überschritten | An CardV wenden |
external_order_id | Ihre Bestellnummer gehört schon zu einer anderen Bestellung | Siehe Sicher erneut senden |
Beispiel für eine Preisänderung:
{
"items": "SKU S000456 price changed from 9.2500 to 9.4100 USD; refresh quote and confirm again."
}Ihre Kontostufe legt die Limits fest: pro Bestellung, für den Tagesumsatz und für die Zahl der Bestellungen pro Tag. Tageslimits beginnen um 00:00 UTC neu. Ihre Limits erfahren Sie bei CardV.
#Sicher erneut senden
Ihre Bestellnummer (external_order_id) schützt Sie vor doppelten Käufen.
Senden Sie dieselbe Bestellung mit derselben Bestellnummer noch einmal, bucht CardV nicht erneut ab.
Sie erhalten die Bestellung zurück, die es schon gibt.
| Sie senden | Sie erhalten |
|---|---|
| Eine neue Bestellnummer | HTTP 201. Eine neue Bestellung. Ihre Wallet wird belastet. |
| Dieselbe Bestellnummer und dieselbe Bestellung | HTTP 200 und "idempotent_replay": true. Die bestehende Bestellung. Keine Abbuchung. |
| Dieselbe Bestellnummer, aber eine andere Bestellung | HTTP 400 bei external_order_id. Es passiert nichts. |
„Dieselbe Bestellung“ heißt: dieselben Positionen in derselben Reihenfolge, jeweils mit gleicher SKU, Menge, gleichem Betrag und gleichen Angaben.
Wenn Sie expected_unit_price mitsenden, muss der Wert zum Preis der ersten Bestellung passen.
Eine wiederholte Bestellung wird vor der Guthaben- und Preisprüfung erkannt. Deshalb erhalten Sie immer die erste Bestellung zurück, auch wenn sich der Preis inzwischen geändert hat.
#Ablauf beim erneuten Senden
Wenn Sie keine eindeutige Antwort erhalten, senden Sie einfach dieselbe Bestellung noch einmal.
POST /orders mit Bestellnummer R
├─ 201 oder 200 → order_id speichern. Fertig.
├─ 400 items / balance / risk → es wurde keine Bestellung angelegt.
│ Ursache beheben und erneut senden. R darf wiederverwendet werden.
├─ 400 external_order_id → R gehört zu einer anderen Bestellung. Anhalten und prüfen.
├─ 403 Signaturfehler → neu signieren und denselben Body senden.
├─ 429 → Retry-After abwarten, neu signieren, denselben Body senden.
└─ Timeout, 5xx oder Verbindung abgebrochen
→ denselben Body mit demselben R erneut senden.
Sie erhalten 201 (der erste Versuch kam nicht an) oder 200 (er kam an).Regeln:
- Vergeben Sie nie eine neue Bestellnummer, nur weil eine Antwort verloren ging. Ist die erste Anfrage doch angekommen, kaufen Sie mit einer neuen Nummer alles doppelt.
- Jedes erneute Senden braucht einen neuen Zeitstempel, eine neue Nonce und eine neue Signatur. Der Body bleibt gleich.
#Bestellung abrufen
GET/api/v1/orders/{order_id} liefert die Bestellung, ihren Status und ihre 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_amountist der Betrag, der bei Annahme der Bestellung abgebucht wurde.items[].unit_priceist der Preis, der für diese Bestellung festgeschrieben ist.items[].deliveriesenthält die vollständigen Codes. Behandeln Sie die Antwort als geheim.invoice_urlunddelivery_file_urlsind Pfade zur Rechnung und zu den Codes als CSV. Beide funktionieren nur im Portal. Mit einem API-Key liefern sie HTTP 403.- Außerdem enthalten:
id(eine alte Nummer, nicht verwenden),events(ein Verlauf nur zur Anzeige) und der Lieferfortschritt pro Position. Diese Felder können Sie ignorieren. - Eine unbekannte Bestell-ID liefert HTTP 404.
#Bestellstatus und Codes
#Status
accepted ──► processing ──► succeeded
│
├──► partially_succeeded (einige Positionen geliefert, andere nicht)
│
└──► failed ──► refunded (Geld zurück in Ihrer Wallet)| Status | Abgeschlossen? | Was tun |
|---|---|---|
accepted | Nein | Warten. Die Wallet ist belastet, die Lieferung hat noch nicht begonnen. |
processing | Nein | Warten. Bestellen Sie nicht noch einmal. |
succeeded | Ja | Codes abrufen und an Ihren Kunden weitergeben. |
partially_succeeded | Ja | Das Gelieferte weitergeben. Der Rest wird später erstattet. |
failed | Noch nicht | Auf refunded warten. failed bedeutet noch keine Erstattung. |
refunded | Ja | Das Geld ist wieder in Ihrer Wallet. |
Wenn Sie keine Webhooks nutzen, fragen Sie so ab: nach 5 Sekunden, dann nach 10 s, 30 s, 60 s, danach alle 5 Minuten. Bleiben Sie dabei im Anfragelimit. Die meisten Bestellungen sind in Sekunden fertig. Manche brauchen eine manuelle Prüfung und können Stunden dauern.
#Codes
Jede gelieferte Einheit ist ein Objekt in 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
}- Zeigen Sie Ihrem Kunden die
display_fields: Jedes Feld hat einlabelund einenvalue. Zeigen Sie auchredeem_url,expiry_dateundinstructions, wenn sie nicht leer sind. kindistsecretbei Codes und PINs undreferencebei Angaben wie Seriennummern.delivery_typesagt, was Sie erhalten haben:code,card_pin,link,code_linkoderqr. Es können neue Typen hinzukommen. Bauen Sie die Anzeige deshalb immer ausdisplay_fieldsauf.- Bei
link-Lieferungen ist dieredeem_urlselbst der Code. Halten Sie sie geheim. - Geben Sie Ihrem Kunden nie eine Einheit mit dem
statusvoided. - Direktaufladungen haben meist keine Lieferungen.
succeededheißt dort: Das Konto wurde aufgeladen. - Kopien wie
card_numberundpin_codestehen auch als eigene Felder in der Antwort. Sie können leer sein. - Webhooks enthalten nie Codes. Rufen Sie die Bestellung ab, nachdem ein Webhook eingegangen ist.
Fragen zur Integration? Schreiben Sie an [email protected] und nennen Sie Ihre Merchant ID sowie die Bestell- oder Request-ID.