Create the checkout session, attach the buyer's reference and verify signed webhooks from plain PHP, Laravel or Symfony. PHP 8.1+, curl or streams, no dependencies.
composer require esaalat/esaalat-phpuse Esaalat\Client;
// Reads ESAALAT_CLIENT_ID, ESAALAT_CLIENT_SECRET and ESAALAT_WEBHOOK_SECRET.
$esaalat = Client::fromEnv();
$session = $esaalat->createSession([
'orderId' => 'order-12345',
'amount' => 5000, // whole riyals
]);
// $session['session_token'] → open the checkout from your page
// $session['portal_url'] → or redirect the buyer straight to ittry {
$event = $esaalat->verifyWebhook(
file_get_contents('php://input'), // the RAW body
$_SERVER['HTTP_X_ESAALAT_SIGNATURE'] ?? ''
);
} catch (\Esaalat\EsaalatException $error) {
http_response_code(400); // anything but 2xx retries
exit;
}
if ($event['event'] === 'payment.verified') {
releaseOrder($event['order_id']); // idempotent on intent_id
}
http_response_code(200);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 verifyWebhook |
$baseUrl | ESAALAT_BASE_URL | https://api.esaalat.com/api/v1 |
$timeout | — | 20 seconds |
$maxRetries | — | 2 |
Client::fromEnv() reads the process environment and $_ENV/$_SERVER, so a .env file loaded by Laravel or Symfony is picked up.ext-curl is suggested, not required: without it the client falls back to PHP streams.Client::formatAmount(5000.5) refuses before the request is sent.