Create the checkout session, attach the buyer's reference and verify signed webhooks from your Node, Bun or Deno server. It is the same package as the browser SDK, on the `esaalat/server` entry.
npm install esaalatimport { createEsaalatServer } from "esaalat/server";
// Reads ESAALAT_CLIENT_ID, ESAALAT_CLIENT_SECRET and ESAALAT_WEBHOOK_SECRET.
const esaalat = createEsaalatServer();
// Your server fixes the amount; the browser only ever receives the token.
const session = await esaalat.sessions.create({
orderId: "order-12345",
amount: 5000, // whole riyals, as a number or a string
});
// session.session_token → open the checkout from your page
// session.portal_url → or redirect the buyer straight to it// Keep the raw body: a parsed-and-restringified object will not match.
app.post(
"/esaalat/webhooks",
express.raw({ type: "application/json" }),
async (req, res) => {
try {
const { payload } = await esaalat.webhooks.verify(
req.body,
req.header("X-Esaalat-Signature") ?? ""
);
if (payload.event === "payment.verified") {
releaseOrder(payload.order_id); // idempotent on intent_id
}
res.sendStatus(200);
} catch {
res.sendStatus(400); // anything but 2xx schedules a retry
}
}
);exp a minute early, so an integration does not start returning 401s at the hour boundary.429 or a timeout cannot create the same order twice.429, 5xx and network failures, honouring Retry-After.| Argument | Environment variable | Default |
|---|---|---|
clientId | ESAALAT_CLIENT_ID | required |
clientSecret | ESAALAT_CLIENT_SECRET | required |
webhookSecret | ESAALAT_WEBHOOK_SECRET | needed by webhooks.verify |
baseUrl | ESAALAT_BASE_URL | https://api.esaalat.com/api/v1 |
timeoutMs | — | 20000 |
maxRetries | — | 2 |
window exists, so a bundler cannot ship it to a page.esaalat for the browser, esaalat/server for this.