Website gateway integration
Site Gateway is the website checkout contract used by the EXZIX WooCommerce plugin and other server-side website integrations. Your server creates a Payment Intent, redirects the customer to the returned payment_url, and finalizes the order from a signed return or verified webhook.
When to use this API
Use Site Gateway when a website checkout needs a hosted EXZIX payment page. POS and QR integrations use their own existing product flows; they are not changed by this contract. Keep the site access code and webhook secret on the server only.
Create a Payment Intent
Send the site access code as a Bearer credential. The site_id must belong to that credential and the currency must be supported by the site. The registered site Return URL and Webhook URL are used by EXZIX; request fields do not override those server-side site settings.
curl -X POST https://api.exzix.com/api/v1/site-payment-intents \
-H 'Authorization: Bearer exzix_site_live_YOUR_SITE_ACCESS_CODE' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Idempotency-Key: wc_123_attempt_1' \
--data '{
"site_id": "site_YOUR_SITE_ID",
"order_id": "123",
"amount": "12.00",
"currency": "TRY",
"description": "WooCommerce order #123",
"expires_in_seconds": 900
}'Response and redirect
A successful request returns data.payment_id, data.status and data.payment_url. Redirect the customer to payment_url. Do not mark the WooCommerce order as paid when the browser leaves your site; payment authority remains server-side.
- Initial status: pending
- Hosted checkout URL: https://pay.exzix.com/pay/{payment_id}
- Payment Intent status is eventually confirmed, cancelled, or expired.
Test without real money
A Payment Intent created with an exzix_site_test_... credential is isolated to Test. Use the returned payment_url and submit one of the test outcomes below; no blockchain transaction is created. The hosted page then follows the normal Return URL and the existing Site Gateway webhook worker delivers the final result to the site.
POST https://pay.exzix.com/checkout/pi_YOUR_PAYMENT_ID/test-simulate
Content-Type: application/x-www-form-urlencoded
outcome=confirmed # or cancelled, expiredSecure return handling
When the hosted checkout reaches a final state, EXZIX redirects to the Return URL registered for the site and appends payment_id, status, and exzix_return_signature. The signature is HMAC-SHA256 over payment_id + "|" + status using the site webhook secret. Verify it locally before showing a final result; use the status endpoint as a fallback when the signature is absent or invalid.
GET https://api.exzix.com/api/v1/site-payment-intents/pi_YOUR_PAYMENT_ID
Authorization: Bearer exzix_site_live_YOUR_SITE_ACCESS_CODEStatus verification endpoint
Use the same site Bearer credential. The response returns payment_id, order_id and status. Only confirmed is a successful payment. pending and processing are not final; cancelled and expired must not complete the order.
{
"data": {
"payment_id": "pi_YOUR_PAYMENT_ID",
"order_id": "123",
"status": "confirmed"
}
}Webhook contract
The registered Webhook URL receives a server-to-server notification even if the customer closes the browser. Verify the HMAC against the exact raw JSON body before parsing, deduplicate by payment_id and status, and return HTTP 2xx after successful processing. The WooCommerce plugin endpoint is ?wc-api=exzix_gateway_webhook.
- paid — complete the matching WooCommerce order with payment_complete()
- cancelled — mark the matching order failed/cancelled and keep the cart available
- expired — mark the matching order failed/cancelled and keep the cart available
Webhook payload and signature
Site Gateway payment notifications use X-EXZIX-Signature and the raw-body HMAC-SHA256 contract below. The payment object includes payment_id, order_id and status. Never trust a payment_id from an unrelated order.
$rawBody = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_EXZIX_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $rawBody, 'YOUR_SITE_WEBHOOK_SECRET');
if (!hash_equals($expected, strtolower($received))) {
http_response_code(401);
exit;
}
$payload = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
$payment = $payload['data']['payment'] ?? [];
// Match payment_id and order_id, then handle paid/cancelled/expired once.
http_response_code(200);Retries and idempotency
Idempotency-Key is required for creation and is scoped to the site and environment. For a retry of a cancelled or expired WooCommerce attempt, use a new attempt key such as wc_123_attempt_2. Reusing the old key intentionally returns the old Payment Intent; it does not create a new payment.
Expiration and order safety
The WooCommerce setting controls expires_in_seconds and currently supports 60–86400 seconds, with a default of 900 seconds. Expiry is final for that Payment Intent. A later checkout attempt must use a new idempotency key and a new Payment Intent, while webhook handling must match both the payment ID and the order ID.