Build a Gift Card Store in PHP: FazerCards API Walkthrough
PHP remains the language of the internet's long tail — shared hosting, cheap VPS stacks, legacy storefronts. Wherever a digital-goods reseller runs today, PHP is likely already running alongside them. This guide wires the FazerCards API into a PHP project from scratch: authenticate requests, pull a live gift card catalog, place real orders, and verify webhook callbacks that push codes to buyers the second a purchase completes. No Composer packages required — every call uses PHP's built-in cURL functions, keeping your dependency footprint at zero. If your store already runs on WordPress, the WooCommerce guide is the faster route; this walkthrough is for custom PHP code.
Prerequisites
Before you write the first line of PHP, make sure you have:
- A FazerCards account with an active API key. The free 5-day Gold trial gives you full API access and the real catalog from day one — no credit card required.
- PHP 8.0 or higher with the curl and json extensions enabled (both ship standard on virtually every shared host and VPS).
- A publicly reachable URL for webhook delivery. A local tunnel works fine during development.
- Your API key stored as an environment variable or in a config file outside your webroot — never hardcoded in source.
Step 1: Build a Reusable API Helper
Every FazerCards request shares two constants: the base URL https://api.fzr.cards/api/v2 and the X-Api-Key header carrying your secret. A single helper function that encodes these once keeps every feature file clean.
Write a function named fzr_request that accepts an HTTP method string, a path string, an optional array for the request body, and an optional array of extra headers — you will need it for the Idempotency-Key in Step 3. Inside, initialise a cURL handle pointed at the concatenated base URL plus path, enable CURLOPT_RETURNTRANSFER, and attach the required headers (X-Api-Key and Content-Type: application/json). On POST calls, set CURLOPT_POST, encode the body array with json_encode, and pass it to CURLOPT_POSTFIELDS. Set CURLOPT_TIMEOUT so a stalled call cannot hang a checkout, execute, check the HTTP status code before trusting the body, then decode the JSON response with json_decode and return the result as a PHP array.
That one function handles every endpoint in the steps below. All FazerCards responses are JSON; none require a separate authentication handshake or token exchange.
Step 2: Retrieve the Gift Card Catalog
The catalog is segmented by product type. For gift cards, start with GET /giftcards to pull a list of categories — each entry carries a category_id, a display name, and metadata. When a buyer selects a category (say, PlayStation or Google Play), follow up with GET /giftcards/cards and pass the chosen category_id as a query parameter to pull the available offers. Each offer contains a card_id, a price_usd, and a stock count.
Cache the category list server-side — it changes infrequently and a live round-trip per page load adds latency buyers notice. A PHP file cache or a short-TTL Redis key is enough; the offers list can be refreshed per request since prices and stock change faster. Render categories as a select menu and offers as a card grid or radio buttons.
FazerCards covers gift cards, game top-ups, game keys, and more across 10,000+ products. The same fzr_request helper works for top-up catalogs via GET /topups and game key catalogs via GET /gamekeys — swap the path, keep the code.
Step 3: Place an Order
When a buyer confirms a selection, POST to /giftcards/order with three body fields: category_id, card_id, and quantity. The response returns the order object, with its id and status. Instant products come back with status: completed and the codes already in the response body — no waiting, no polling. Non-instant products start as status: processing and complete through a webhook event.
Add an Idempotency-Key header to every order POST. Generate the key once per checkout — a UUID or a hash of the cart ID — and store it with the cart, so every retry of that checkout sends the same key. If the buyer double-submits or your server retries after a network timeout, FazerCards returns the original order rather than charging again. Keys are kept for seven days. This one header eliminates an entire class of duplicate-charge bugs without extra server state.
For top-ups and game keys the endpoint paths differ (POST /topups/order, POST /gamekeys/order), but the same helper, the same idempotency pattern, and the same four order statuses apply throughout. See the API docs for the field-by-field reference.
Step 4: Surface Codes and Handle Async Orders
For instant products, pull the codes directly from the completed order response. For processing orders, call GET /orders/ followed by the order ID to check the current status. The four possible statuses are exactly processing, completed, failed, and refund — never "pending" or "delivered". When the status reaches completed, the same endpoint returns the codes in its body.
Build a lightweight buyer-facing status page that polls the orders endpoint every few seconds in JavaScript, or skip the browser-side loop entirely and let webhooks handle delivery — which is Step 5.
Step 5: Verify Webhook Signatures Before Trusting Events
Register your webhook URL in the FazerCards dashboard. For orders, the platform sends two event types: order.created and order.status_changed. The event body is minimal — order_id, type, status, and previous_status — and carries no codes. When status is completed, fetch the codes with the orders endpoint.
Before processing any incoming event, verify its origin. FazerCards attaches an X-Webhook-Signature header formatted as sha256= followed by the hex value. Reproduce it server-side: run HMAC-SHA256 over the raw request body using your webhook secret as the key, hex-encode the result, and prepend sha256=. In PHP, read the raw body with file_get_contents('php://input') before any framework parses it, compute the digest with hash_hmac('sha256', $rawBody, $secret), and compare using hash_equals — the constant-time function that prevents timing attacks. When the signature fails, return HTTP 401 and do not process the event.
Undelivered events retry after 1, 5, and 30 minutes using the same event_id, so your handler must be idempotent: log processed event IDs and skip duplicates. The webhooks reference covers the full event schema, and the API cookbook has worked examples in curl, Node.js, Python and Go that map one-to-one onto PHP's cURL calls. For a broader look at building reliable delivery flows, see the API integration guide.
What You Have
After these five steps your PHP gift card store does everything a serious reseller's fulfilment layer needs: a cached live catalog, instant code delivery for fast products, async webhook delivery for slower ones, duplicate-order protection via idempotency keys, and HMAC-verified callbacks that reject fraudulent requests. The same helper function scales to the full catalog — swap the endpoint path and adjust the order body for each product type.
Ready to go live? The FazerCards pricing page lays out what each plan unlocks, and a free 5-day Gold trial gets you the full API, the full catalog, and instant delivery so you can validate your PHP store under real conditions before committing to a subscription.