Concepts

Key ideas you need before integrating: base URL, item codes, price vs face value (charged vs denomination), the response envelope, and the difference between checking stock and checking order status.

Base URL and versioning

All API requests go to:

https://api.cardpluspay.com/v1

The path /v1 is the API version. Append the endpoint path to this base (e.g. https://api.cardpluspay.com/v1/balance, https://api.cardpluspay.com/v1/catalog). Future versions may use /v2; the current production version is v1.

Item codes

Products in the catalog are identified by an item code (sometimes referred to as item_id in query parameters). For example: GOOGLE-5USD-KOXx.

  • Use the same identifier when calling the catalog, checking stock, and creating orders.
  • Get item codes from GET /catalog or GET /catalog?item_id=...; store the code in your database or config when you link your products to CardPlusPay.

Price vs face value (list, charged, denomination)

Three kinds of amounts appear in catalog and order responses. Use them correctly:

  • Face value (denomination) — What the customer receives (e.g. “$5 Google Play card” or “60 UC”). In the catalog use face_value (preferred) or value (alias). In order responses use denomination per code. For display only — not for billing.
  • List price — Pre-offer wholesale/list price per unit. In the catalog this is price.
  • Charged amount — What is debited per unit after an active offer. In the catalog this is charged_price (equals price when no offer). In stock check and orders use price / charged_amount / amount for financial logic.

Always re-check at checkout: cache charged_price from the catalog for display, but call POST /orders/check for the authoritative per-unit charge and stock before purchase.

Charged amount can be less than face value (e.g. promotional pricing). Never use face value for balance or checkout totals.

Product type vs category

category is a merchandising shelf (e.g. “Google Play SA”). Path GET /catalog/{category} filters by category.

product_type is the product vertical (e.g. app_store, game_card, direct_top_up). Filter with ?product_type= on GET /catalog. See the display products guide for the full slug list.

Response envelope

Every API response uses a common structure:

{
  "success": true | false,
  "message": "Human-readable message",
  "data": { ... }
}
  • success — Whether the request succeeded. Always check this before trusting data.
  • message — Short description (e.g. "Balance retrieved successfully" or an error message).
  • data — The payload. Structure depends on the endpoint (e.g. balance, products, order_id, codes).

On failure, success is false and message (and optionally data) describes the error. HTTP 429 (too many requests) uses this envelope with data.retry_after — see rate limits.

Purchase and delivery

Gift card and digital product purchases are fulfilled through the CardPlusPay API. Your integration uses CardPlusPay endpoints only.

  • Code delivery — Products such as game_card, gift_card, and app_store return redeemable codes in the POST /orders response when fulfillment completes in the same request.
  • Direct top-up — Products with product_type = direct_top_up credit a buyer account. They require a fields object (from catalog required_fields), return empty codes, and include a delivery object. See direct top-up orders.
  • Async fulfillment — Some orders return HTTP 200 with status: pending (and empty codes for code products). This means CardPlusPay is still processing — not a failure. Poll GET /orders/{id} until completed or failed. Your API wallet stays debited while an order is pending.
  • Availability — available_quantity on POST /orders/check reflects how many units CardPlusPay can fulfill right now. It may change between check and purchase; always re-validate at checkout.
  • Order lookup — Use GET /orders/{id} after create when status is pending, after a connection timeout, or for support — see order status & polling.
  • Webhooks vs poll — You may optionally set an HTTPS webhook URL on your API key to receive signed POSTs when an order is created or becomes terminal. Polling is not replaced: if you do not use webhooks, you must poll GET /orders/{id} while status is pending. Webhooks never include gift card codes. See order webhooks.

Stock check vs order status

Do not confuse these two operations:

Goal Endpoint Use when
Check if a product is available (stock) POST /orders/check Before add-to-cart or checkout. Send item_id (item code) and quantity. Response tells you if that quantity is in stock.
Get status of an existing order GET /orders/{id} After creating an order. Send the order_id from the create response. Use this to show status and retrieve gift card codes.

Use POST /orders/check for “Is this product in stock?” and GET /orders/{id} for “What’s the status of order #123?”

Quantity limits

CardPlusPay exposes per-SKU quantity limits so you can cap cart and checkout without guessing:

  • Catalog (GET /catalog) — Each product includes min_quantity and max_quantity for listings and sync. Boolean in_stock is the last record that at least one unit could be fulfilled; it is not live stock. Use POST /orders/check for the quantity you are about to sell.
  • Stock check (POST /orders/check) — Success data adds available_quantity (units purchasable now, or null if unknown), plus quantity_contract_version (currently 1).
  • Purchase (POST /orders) — Quantity must be within the SKU min/max and current stock. Failures use data.state of invalid_quantity or out_of_stock.

Purchasable cap (client-side):

purchasable_now = min(max_quantity, available_quantity ?? max_quantity, 100)

Also apply your wallet: floor(balance / price) from GET /balance. See the check availability guide for a full example response.

Typical purchase flow

A common end-to-end flow for buying a gift card looks like this:

  1. Check balance — Optional but recommended. Call GET /balance to ensure you have enough funds for the planned purchase.
  2. Get product info — Use GET /catalog (or GET /catalog?item_id=...) for item_code, charged_price, face value, quantity bounds, and product_type. First load: paginate a full listed GET with sort_by=item_code. Ongoing: updated_since delta (products upsert, removed delete). Cache charged_price; always POST /orders/check before checkout. See display products.
  3. Check stock and price — Before checkout, call POST /orders/check with item_id and quantity. Use data.price (authoritative charge), min_quantity, max_quantity, and available_quantity; confirm state is "in_stock".
  4. Create the order — After payment is authorized, call POST /orders with item_code and quantity (and a stable Idempotency-Key in production). For direct_top_up SKUs, include fields — see direct top-up orders. On success, use order_id, amount, and either codes (code products) or delivery.status (top-up).
  5. Poll if pending — When status is pending, call GET /orders/{id} on an interval until completed or failed unless you handle the same transitions via webhooks. Do not treat a client timeout as failure — retry POST /orders with the same idempotency key first. If you do not use webhooks, polling is required.
  6. Confirm if needed (optional) — Call GET /orders/{id} if you did not receive a response and are reconciling with the same idempotency key, or for support.

← Integration Guide · Authentication →