Security & best practices

Follow these practices to keep your API key and integration secure.

API key security

  • Server-side only — Call the CardPlusPay API from your backend (e.g. PHP, Node, Python). Never embed the API key in frontend JavaScript, mobile app binaries, or public repositories.
  • Store in environment or secrets — Use environment variables (e.g. CARDPLUSPAY_API_KEY) or a secrets manager. Do not hardcode the key or commit it to version control.
  • Rotate if compromised — If the key may have been exposed, generate a new key in API Keys in the CardPlusPay Dashboard and update your application; disable or delete the old key.
  • Limit access — Restrict who can view or copy API keys in your team. Use separate keys per environment (e.g. production vs test) if you use multiple keys.

HTTPS and requests

  • Always use HTTPS — All requests must go to https://api.cardpluspay.com. Do not send the API key or sensitive data over plain HTTP.
  • Validate responses — Check success in the response envelope before trusting data. Handle errors and timeouts in your code.
  • Idempotency — For POST /orders, send a unique, stable Idempotency-Key per purchase attempt (UUID recommended). Retries with the same key and body replay the original response. Use a new key only when starting a genuinely new purchase.
  • Purchase request timeout — Set your HTTP client timeout to at least 90 seconds for POST /orders (180 seconds recommended for some SKUs). If the connection drops before you receive a response, retry with the same idempotency key rather than creating a duplicate order.
  • Pending orders — HTTP 200 with status: pending means fulfillment is in progress. Poll GET /orders/{id} if you do not use webhooks; do not refund based on elapsed time alone.

Webhook signatures

  • HTTPS only — Your receiver URL must use HTTPS. CardPlusPay POSTs to that URL; it is not an inbound CardPlusPay API path.
  • Signing secret — Shown once when you first save a webhook URL or rotate the secret. Verify X-Webhook-Signature as HMAC-SHA256 of the raw JSON body. Reject timestamps older than 5 minutes (X-Webhook-Timestamp).
  • Idempotency — Treat id / X-Webhook-Id as unique; retries reuse the same id.
  • No codes in webhooks — Fetch codes with GET /orders/{id} after order.completed. See order webhooks.

Rate limits

Each API key is limited to a number of requests per 60-second window. Dashboard-generated keys typically allow 100 requests per minute. CardPlusPay may raise the cap for a key. All /v1 routes on that key share the same budget (catalog, purchase, poll, challenge). If request signing is on, a 401 from a missing or invalid challenge/signature does not use that budget; GET /v1/challenge does.

Successful responses include X-RateLimit-Limit and X-RateLimit-Remaining. When the cap is exceeded, the API returns HTTP 429 with the standard envelope:

{
  "success": false,
  "message": "Too many requests. Retry later.",
  "data": {
    "retry_after": 12
  }
}

Headers include Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. Wait at least retry_after seconds (or honour Retry-After) before retrying; use backoff or a queue if you send bursts. Polling GET /orders/{id} counts toward the same limit as POST /orders.

Data handling

  • Gift card codes are sensitive. Transmit them only over HTTPS and store them securely. Do not log full codes.
  • Buyer account fields (fields on direct top-up orders) are personal data. CardPlusPay does not echo them in API responses — store them securely in your application and comply with your privacy policy.
  • When passing customer email or order data to the API, ensure you comply with your privacy policy and applicable laws.

← SDK · Integration Guide · Troubleshooting →