Skip to main content
Redirect customers to a complete checkout page that Lite builds, serves, and processes. When a customer is ready to pay, you send them from your site to a Lite URL. They enter their card there, lite processes the payment on its own servers, and they land back on your site with the result. Because the card details live entirely on lite’s infrastructure, they never touch your servers. You never store, transmit, or handle a raw card number, which can make you eligible for the simplest level of PCI validation; SAQ A. Use a Hosted Page if you:
  • aren’t PCI-DSS certified and want to keep it that way,
  • want the fastest integration with the least code, or
  • are happy to send customers to a lite-hosted (but brand-able) page to pay.

How it works

  1. Create a session. Your server calls POST /v1/checkout/sessions and gets back a payment_link.
  2. Redirect the customer. You send them to the payment_link. They pay on lite’s hosted page.
  3. Customer returns. lite sends cardholders back to your success or failure URL, so you can show a result right away.
  4. Confirm with a webhook. lite sends a payment.captured event to your server. This is the source of truth you fulfill from.
A checkout session is a short-lived attempt to collect payment. It resolves to Completed, Expired, or Cancelled. The return URL tells you the session resolved; the webhook tells you the money actually moved. Treat the webhook as your source of truth — see Step 4.

Before you begin

This guide assumes your one-time setup is done. You can re-check any of it from the dashboard. You’ll need:
  • A lite account in good standing.
  • Test and live API keys (sk_test_… / sk_live_…).
  • At least one channel configured, with its channel_id (unless you intend to use the default channel option). A channel sets your enabled payment methods, branding, and default redirect URLs.
  • A webhook endpoint registered, you’ll need it for fulfillment in Step 4.
Build and test everything with your test key first. Going live takes more than a live key. To process real payments, your account needs completed KYB (Know Your Business) verification and active payment acceptance. You can track both from the dashboard. Until they’re done, you will only see lite sandbox.

Step 1: Create a session

Create the session from your backend, so a customer can’t tamper with the amount or currency.
curl https://{{url}}/v1/checkout/sessions \
  -H "x-api-key: $LITE_API_KEY" \
  -H "x-correlation-id: $(uuidgen)" \
  -H "x-idempotency-key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_id": "550e8400-e29b-41d4-a716-446655440000",
    "amount": 4999,
    "currency": "SAR",
    "customer": {
      "email": "john.doe@email.com",
      "first_name": "John",
      "last_name": "Doe"
    },
    "order": { "reference": "ORD-123456" },
    "redirect_urls": {
      "success": "https://example.com/return?session_id={SESSION_ID}",
      "failure": "https://example.com/checkout"
    },
    "expiry": 1800
  }'

Required headers

HeaderRequiredPurpose
x-api-keyYesAuthenticates the request. Use your test key in test mode. Or send Authorization: Bearer <JWT> instead.
x-idempotency-keyRecommendedA UUID per logical session. Retries with the same key return the original session instead of creating a duplicate.
x-correlation-idOptionalA UUID per request, echoed in logs. Quote it in support tickets.

Key request fields

FieldRequiredNotes
customerYesCustomer details. The individual fields (email, name, phone) are each optional.
amountYesIn minor units (4999 = 49.99 SAR). The exponent varies by currency: SAR has 2 decimals, KWD has 3, JPY has 0.
currencyYesISO 4217 code.
redirect_urlsYesThe success and failure URIs the customer returns to.
orderYesOrder details as price, quantity, shipping and vat. order.reference (your order ID) is required and comes back as order_id.
channel_idNoApplies a channel’s branding, enabled payment methods, and redirect URLs.
expiryNoSession lifetime in seconds (60 to 86400). Defaults to 3600.
three_ds_requiredNoForce 3D Secure on. Otherwise it’s applied automatically per scheme and SAMA rules.

Response

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "payment_link": "https://checkout.lite.sa/ses_a1b2c3d4e5f6",
  "client_secret": "cs_a1b2c3d4e5f6",
  "status": "Pending",
  "expires_on": "2026-06-01T12:30:00.000Z",
  "order_id": "ORD-123456",
  "amount": 4999,
  "currency": "SAR"
}
A new session always starts as Pending. Hold on to the id (a UUID), you’ll use it to match the webhook, and to look the session up later if you need to. (client_secret is only for the Drop-in SDK; you can ignore it for the hosted redirect.) By default, 3D Secure is applied automatically based on the card scheme and SAMA rules; for mada and most KSA cards it’s mandatory. Set three_ds_required: true if you want to force it on every payment.

Step 2: Redirect to the hosted page

Send the customer to payment_link with an HTTP 303:
res.redirect(303, session.payment_link);
Use 303, not 302, so the browser switches to a GET for the checkout page even though your endpoint received a POST. From here the customer pays entirely on Lite’s domain — you never see their card details.

Step 3: Handle the return

After checkout, lite sends the customer back to one of your redirect_urls:
  • success: the payment went through.
  • failure: the payment failed, or the customer cancelled.
Use this page to show them something right away — an order confirmation or a retry prompt. Don’t fulfill the order here.
[!WARNING] The return URL is not authoritative. A customer can pay and then close the tab or drop offline before your page loads, so the redirect might never reach you. Always fulfill from the webhook in Step 4.
If you want to show a definitive state on this page, check the session server-side first; but still fulfill from the webhook. Retrieve the full session, or call the lightweight validity endpoint:
const session = await lite.checkout.sessions.retrieve(req.query.session_id);
// or GET /checkout/sessions/{session_id}/validity for a quick { valid, status } check
// render based on session.status, but still fulfill from the webhook

Step 4: Confirm with a webhook

Webhooks are how you reliably learn the outcome of every payment. For the Hosted Page flow, subscribe to these events:
EventWhen it fires
payment.capturedPayment succeeded and funds were captured. Fulfill here.
payment.authorization_failedThe payment was declined.
payment.refundedA refund completed.

Test and go live

Test mode accepts lite’s published test cards. At a minimum, confirm each of these outcomes:
ScenarioExpected result
Successful mada paymentSession Completed, payment.captured webhook fires
Successful Visa / Mastercard paymentSession Completed, payment.captured webhook fires
Declined cardSession not completed, payment.authorization_failed webhook fires
Before you switch to your live key, confirm that:
  • Your account has KYB verification complete and payment acceptance activated.
  • Your webhook endpoint verifies the signature and returns 200.
  • A session creates successfully and returns a payment_link.
  • The redirect reaches lite’s hosted page and back to your return URL.
  • Fulfillment fires from payment.captured, not from the return URL.
  • A declined card shows up correctly on your failure page.
For full event payloads, the signature scheme, and the retry schedule, see the webhooks reference.