Merchant Developer Documentation
Integrate Wasal with your store in minutes. Create Vodafone Cash and InstaPay payment requests and receive signed webhooks the second a transfer is confirmed.
Overview & Base URL
All Merchant API requests are made over HTTPS using JSON. When creating a payment, pass the amount in Egyptian Pounds (EGP). In responses and webhook payloads, amounts are returned in piasters (100 piasters = 1 EGP).
API Base URL
Authentication
Generate an API key from your Merchant Dashboard once your account is approved. Pass the key in the x-api-key header on every request. Keep your API keys secret on your server and never expose them in client-side code.
x-api-key: wsl_live_9f8e7d6c5b4a3f2e1d0c.../v1/paymentsCreate a Payment Request
Creates a new payment request tied to your store's order reference and returns a hosted checkout link (payUrl) where your customer can see your wallet details and upload their receipt.
| Field | Type | Required | Description |
|---|---|---|---|
| amount | number | Yes | Amount in EGP (e.g. 750 = 750.00 EGP, stored as 75000 piasters) |
| method | "vodafone_cash" | "instapay" | Yes | Transfer method selected by the customer |
| reference | string (1..64) | Yes | Unique order or invoice reference in your system |
| description | string | Optional | Optional description shown for your records |
| expiresInMin | number (5..10080) | Optional | Expiration window in minutes (default: 1440 = 24h) |
curl -X POST https://api.wasal-pay.com/v1/payments \
-H "x-api-key: wsl_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"amount": 750,
"method": "vodafone_cash",
"reference": "order-1042",
"description": "Order #1042 - Cairo Store",
"expiresInMin": 1440
}'{
"id": "b7e2f8a1-91d4-4c68-9f14-2c18a3918b22",
"merchantId": "63f1e9c2-4411-4090-81c4-0b3c12345678",
"publicToken": "a8f92e1b90c34d128e77f123456789ab",
"reference": "order-1042",
"amount": 75000,
"method": "vodafone_cash",
"status": "pending",
"expiresAt": "2026-11-04T12:00:00.000Z",
"payUrl": "http://localhost:3001/pay/a8f92e1b90c34d128e77f123456789ab"
}Drop-in checkout button
Paste this on your site. Clicking the button creates a payment and sends the customer to the Wasal checkout. Keep the API key on your server in production. The button below is only for trying the same script.
Paste into your page
<script src="https://your-wasal-host/wasal.js" data-api="https://api.wasal-pay.com"></script>
<button
data-wasal-pay
data-key="YOUR_API_KEY"
data-amount="750"
data-method="vodafone_cash"
data-reference="order-1042"
>
Pay 750 EGP with Wasal
</button>Prompt for your developer or coding agent
Add Wasal checkout to this site. Load wasal.js from the Wasal site and set data-api="https://api.wasal-pay.com". Add a button with data-wasal-pay, data-key set to the merchant API key, data-amount in EGP, data-method of vodafone_cash or instapay, and a unique data-reference. On click, POST /v1/payments and redirect the customer to payUrl. Do not build a second checkout page.Try the same button
/v1/payments & /v1/payments/:idList & Fetch Payments
Retrieve a list of recent payments (optionally filtered by status) or fetch a single payment by its unique ID.
# List recent payments (optional status filter)
curl -X GET "https://api.wasal-pay.com/v1/payments?status=confirmed" \
-H "x-api-key: wsl_live_your_api_key"
# Get a single payment by ID
curl -X GET "https://api.wasal-pay.com/v1/payments/b7e2f8a1-91d4-4c68-9f14-2c18a3918b22" \
-H "x-api-key: wsl_live_your_api_key"/v1/payments/:id/receiptDirect Receipt Upload
If you build a custom checkout UI instead of redirecting to the hosted payUrl, you can upload the customer's receipt image directly from your backend using multipart/form-data.
curl -X POST "https://api.wasal-pay.com/v1/payments/b7e2f8a1-91d4-4c68-9f14-2c18a3918b22/receipt" \
-H "x-api-key: wsl_live_your_api_key" \
-F "file=@/path/to/receipt-screenshot.jpg"Payment Lifecycle Statuses
Every payment transitions through the following statuses from creation to verification:
Payment created; waiting for customer receipt upload or SMS/notification match.
Receipt matched with a verified message on the merchant's Android phone.
Receipt uploaded, awaiting manual merchant review (e.g., delayed SMS or partial mismatch).
Manually rejected by the merchant.
Payment expired before completion.
Webhooks & HMAC-SHA256 Verification
Whenever a payment status changes (such as automatic or manual confirmation), Wasal sends a POST request to your configured Webhook URL signed with your Webhook Secret in X-Wasal-Signature and the Unix timestamp in X-Wasal-Timestamp.
POST https://yourstore.com/api/webhooks/wasal
Content-Type: application/json
X-Wasal-Signature: 84e7f902d1c48b20e3a591c22f8901d23b4567c89a0123b456789c0123456789
X-Wasal-Timestamp: 1793777520
{
"event": "payment.confirmed",
"paymentId": "b7e2f8a1-91d4-4c68-9f14-2c18a3918b22",
"reference": "order-1042",
"amount": 75000,
"method": "vodafone_cash",
"status": "confirmed",
"confirmedBy": "auto"
}import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyWasalWebhook(
rawBody: string,
signatureHeader: string,
timestampHeader: string,
webhookSecret: string,
): boolean {
const expected = createHmac("sha256", webhookSecret)
.update(`${timestampHeader}.${rawBody}`)
.digest("hex");
const a = Buffer.from(expected, "utf8");
const b = Buffer.from(signatureHeader, "utf8");
return a.length === b.length && timingSafeEqual(a, b);
}