Merchant API guide
Integrate prepaid digital goods through CardV's versioned REST API. The selected environment's OpenAPI reference defines the complete field schemas.
https://b2b.cardv.net Live · https://sandbox.cardv.net Sandbox · /api/v1
Integrate in sandbox first. Keys, wallets, SKUs, orders and supplier access are separate. A Live order may debit real funds and trigger supplier procurement.
Live API reference ↗ · Sandbox API reference ↗ · Live OpenAPI schema ↗ · Merchant Portal ↗
Get started
- Apply, verify email, await approval, then sign in to the merchant Portal. The immutable Merchant ID has format
M########; it is not your company name or login email. - Select Live or Sandbox in the Portal header, then open Integrations → API keys. Creation returns metadata; use Reveal and the emailed verification code to obtain and save the raw key.
- Call
GET /api/v1/accountandGET /api/v1/balanceon the selected host. - Browse SKUs, request a fresh quote and submit an order with a unique merchant reference.
- Poll order detail or verify webhooks until final fulfilment.
Authentication & HMAC signing
Every server-to-server request needs X-Merchant-Id and X-Api-Key. API-key authenticated POST, PUT, PATCH and DELETE requests also need X-Timestamp, X-Nonce and X-Signature. Timestamp is Unix seconds within five minutes; nonce is single-use in that window.
canonical = "\n".join([
METHOD.upper(),
PATH_WITH_QUERY,
TIMESTAMP,
NONCE,
sha256(EXACT_RAW_BODY).hexdigest()
])
signature = hmac_sha256(API_KEY, canonical).hexdigest().lower()Hash the exact raw body bytes you send, include the query string but not the host in the canonical path, and use the API key as the HMAC-SHA256 secret. Use lowercase hexadecimal. A retry needs a fresh timestamp and nonce. Swagger does not generate write-request HMAC headers.
A configured IP allowlist also applies. Never log API keys or full delivery payloads.
Catalog & pricing
| Endpoint | Purpose / 用途 |
|---|---|
GET /api/v1/account | Merchant account / 商户账号 |
GET /api/v1/balance | Wallet balance / 钱包余额 |
GET /api/v1/products | Product catalog / 商品目录 |
GET /api/v1/skus | Orderable SKUs / 可下单规格 |
GET /api/v1/skus/{sku_id}/quote | Current quote / 当前报价 |
POST /api/v1/orders | Create order / 创建订单 |
GET /api/v1/orders/{order_id} | Order detail / 订单详情 |
GET /api/v1/orders/by-external-id/{external_order_id} | Lookup by merchant reference / 按商户订单号查询 |
Only order an SKU with availability=available. Quote a fixed denomination with quantity; a range denomination also needs amount in the SKU's face_currency between min_face_value and max_face_value. Send the returned merchant_price as expected_unit_price; do not derive it from face value or catalog previews.
GET /api/v1/skus/S000001/quote?quantity=1
GET /api/v1/skus/S000002/quote?quantity=1&amount=25.00Orders, idempotency & delivery
POST /api/v1/orders accepts external_order_id and items. Each item needs sku_id and quantity; send expected_unit_price from a fresh quote. Include amount for a range SKU or inputs matching required_input_schema for direct charge.
{
"external_order_id": "YOUR-ORDER-10001",
"items": [{
"sku_id": "S000001",
"quantity": 1,
"expected_unit_price": "9.2500"
}]
}The example SKU and price are illustrative. Obtain actual values from the selected environment. external_order_id is unique per merchant, 1–120 characters. A new order returns 201; an equivalent replay returns 200 with idempotent_replay=true. Changed content under the same reference is rejected.
Store public order.order_id (O-...), not the legacy numeric id. HTTP acceptance is not delivery completion. Query by order ID or external_order_id. States include accepted, processing, succeeded, partially_succeeded, failed, cancelled and refunded. Fulfilment failure does not itself prove a wallet refund: reconcile transactions. API-key order detail may contain card secrets; disable caching and sensitive response logs.
Mobile recharge is a separate flow
Do not use /api/v1/orders for mobile recharge. Discover GET /api/v1/recharge/countries and GET /api/v1/recharge/operators?country=..., then POST /api/v1/recharge/quote with country, operator_key, amount and optional subtype. Use its quote_token, recipient account, amount and a unique external_order_id with POST /api/v1/recharge/orders. Query GET /api/v1/recharge/orders/{order_id}.
Webhooks
A Portal owner registers an HTTPS endpoint in the selected environment. Creation/reset returns signing_secret once; list/detail do not reveal it. CardV sends X-CardV-Event, X-CardV-Delivery, X-CardV-Timestamp and X-CardV-Signature: t=TIMESTAMP,v1=HEX.
expected = hmac_sha256(
WEBHOOK_SECRET,
X_CARDV_TIMESTAMP + "." + EXACT_RAW_BODY
).hexdigest().lower()Compare in constant time, reject stale timestamps and persist before returning 2xx. Deduplicate by delivery ID and by business order/state for replays. Webhooks are notifications, not full card-secret payloads; retrieve delivery via authenticated order detail.
Errors & safe retries
| HTTP | Action |
|---|---|
| 400 | Validation, price or input error; correct the request. |
| 403 + CardV JSON | Check credentials, signature, access and source IP. |
| 403 + Cloudflare 1010 | Edge blocked the request before CardV; rotating keys will not help. |
| 404 | Resource unavailable to this merchant. |
| 429 | Respect Retry-After. |
| 5xx / timeout | Query by external_order_id first; if unresolved, retry the same content/reference with fresh timestamp and nonce. |
Environments & current limitation
Sign in once at the Live merchant Portal and switch workspace in its header; direct sandbox password login is disabled. Each environment has its own OpenAPI, keys, wallet, SKUs and orders. Provisioned sandbox merchants receive 1,000 USD noncash test credit and a limited catalog; real funding is unavailable.
Cloudflare may currently block some non-browser clients with HTTP 403 / error 1010 before a request reaches CardV. A working health check or Swagger page does not prove your authenticated client works. Verify the actual client and never use Live for test orders as a workaround.
Coordinate any low-value Live test with your CardV contact. Never send API keys, webhook secrets or card deliveries by email.