Skip to content
Cipay
Esc
navigateopen⌘Jpreview
On this page

Checkout

Preview totals, create gateway-hosted checkout, and confirm status safely.

Cipay calculates the quote and creates a hosted gateway page. Your application never collects or sends card numbers or CVV.

Locator and origin

Storefront locator

Selects the Cipay environment, merchant, storefront, and published catalog. Copy it from Settings → Developer → Storefront integration.

Application origin

Identifies the website starting checkout. Cipay compares it with the storefront’s approved origins.

Integration What you provide What Cipay receives
Direct Server SDK locator and the trusted request origin Both explicit public routing values
createCipayBuyerHandler Configure locator once The handler derives origin from the incoming same-origin request

Preview a quote

checkout.preview returns calculated quote data before a checkout session exists. It does not return UI or a hosted payment URL.

// Ask Cipay for authoritative totals before rendering the order summary.
const quote = await cipay.checkout.preview({
  locator: process.env.CIPAY_STOREFRONT_LOCATOR!,
  origin: "https://shop.example.test",
  offerId,
  discountCode: "SANDBOX10",
});

if (!quote) throw new Error("This offer is not available");

console.log({
  subtotalHalalas: quote.subtotalHalalas,
  discountHalalas: quote.discountHalalas,
  totalHalalas: quote.totalHalalas,
  currency: quote.currency,
});
Field Meaning
subtotalHalalas Price before discount.
discountHalalas Discount Cipay accepted for this quote.
totalHalalas Amount the gateway should collect.
currency SAR for the current public checkout contract.

Always display Cipay’s quote. Browser calculations are presentational and cannot set the charged amount.

Buyer and Customer are different

buyer

Contact data for this checkout attempt. It pre-fills hosted checkout and identifies the receipt recipient.

Customer

A stored merchant record with an ID and lifecycle, mapped on your server to an authenticated application user.

Load signed-in buyer contact fields from your verified session or stored customer mapping. A browser-supplied email never proves ownership of a Customer.

Create hosted checkout

Collect consent

Set recurringConsentAccepted: true only after the buyer accepts recurring terms.

Create once

Use one idempotency key for this checkout attempt, including network retries.

Store capability

Save the returned capability in a short-lived server session.

Redirect

Redirect only to the returned checkoutUrl.
import { createIdempotencyKey } from "@cipay/client-sdk/api";

const checkout = await cipay.checkout.create(
  {
    locator: process.env.CIPAY_STOREFRONT_LOCATOR!,
    origin: "https://shop.example.test",
    offerId,
    buyer: {
      email: "buyer@example.test",
      name: "Sandbox Buyer",
      locale: "en",
    },
    recurringConsentAccepted: true,
  },
  { idempotencyKey: createIdempotencyKey("checkout") },
);

if (!checkout?.checkoutUrl) throw new Error("Checkout is unavailable");

// Keep status access on the server; never put capability in a URL or log.
await secureCheckoutSession.save({
  capability: checkout.capability,
  expiresAt: checkout.status.expiresAt,
});

return Response.redirect(checkout.checkoutUrl, 303);

Retrieve checkout status

Capability

An opaque, short-lived secret scoped to one checkout’s status. It comes from checkout.capability. It is not a merchant API key, but anyone holding it can read that checkout state.

Store it in Never store it in
Encrypted server session or short-lived server cache URL, browser storage, analytics, logs, or customer-visible errors
import { CheckoutState } from "@cipay/client-sdk/api";

// Read the capability saved immediately before the hosted redirect.
const { capability } = await secureCheckoutSession.read();

const status = await cipay.checkout.retrieveStatus({
  origin: "https://shop.example.test",
  capability,
});

switch (status.state) {
  case CheckoutState.Completed:
    return { message: "Payment confirmed" };
  case CheckoutState.Failed:
    return { message: "Payment failed" };
  case CheckoutState.Expired:
    return { message: "Checkout expired" };
  case CheckoutState.Recoverable:
    return { message: "Payment needs another attempt" };
  case CheckoutState.ReadyForCheckout:
  case CheckoutState.HostedCheckoutOpen:
  default:
    return { message: "Payment is still processing" };
}

CheckoutState is generated from the checkout contract, so server and React code can branch without repeating state strings. See the generated status response for nextAction, expiry, and related identifiers.

The buyer handler exposes the same operation at POST /api/cipay/v1/checkout/status. useCheckoutCreate returns the capability, and the handler derives the origin from the request.