The key idea
What is a webhook? It is a message that one system sends to a URL on your server the moment something happens, so your code does not have to keep asking. In payments, it tells your server that a customer paid, even if the customer closed the browser. Verify the message before you deliver anything.
Webhook meaning in simple words
A webhook is a message that one system sends to a URL on your server the moment something happens. It is a normal HTTP request, almost always a POST, with a few details about the event in its body.
The key idea is the direction. Usually your code asks another system and waits. With a webhook, the other system contacts you: you give it an address once, and it uses that address each time the event happens.
Developers in Egypt usually meet webhooks first in payments. A customer pays, and the payment service tells the seller's server right away.
Polling or webhooks: an example from daily life in Egypt
You order lunch by phone from a restaurant in Giza, then you call the shop every ten minutes: has my order left yet? Most of the time the answer is not yet. That is polling: asking again and again.
Normally you give your phone number once, and the delivery rider calls you when he arrives. That is a webhook: the other side contacts you when there is news.
| Point | Polling | Webhook |
|---|---|---|
| Who starts the contact | Your server, again and again | The other system, once per event |
| When you learn the news | At your next check, often late | Close to the moment it happens |
Polling still works when your code cannot receive requests. But when events are rare and time matters, as with payments, a webhook fits better.
The five parts of a webhook
Every webhook has the same five parts:
- Your URL. An address on your server that accepts a
POST, often called an endpoint, for examplehttps://yoursite.com/webhook.php. You register it once with the sender. - The event. What happened, as a short name such as
payment.success. - The payload. The body of the request, usually JSON, with the details.
- The headers. Extra lines sent with the request, often about the delivery itself.
- Your reply. An HTTP status code. Any code that starts with 2, written 2xx, means: I received it. Anything else, or no answer in time, means the delivery failed.
Many senders try again after a failed delivery. This is called a retry.
API call, callback page or webhook: what is the difference?
These three terms differ in who starts and where the information arrives.
| Question | API call | Callback page | Webhook |
|---|---|---|---|
| Who starts it? | Your code | The browser of the customer | The other system |
| Where does it arrive? | At the other system | On a page of your site, in that browser | At a URL on your server |
| What if the customer closes the tab? | No effect | The page never opens | It still arrives |
The callback page is the page of your site that the customer returns to after paying, with the result in its address. Both it and the webhook are only signals, because anyone can type a URL or send a POST. The proof is an API call that your own code makes to an address you trust.
A real example: the payment webhook from Gammal Tech
Gammal Tech is an Egyptian platform that gives a website ready backend services, including payments. Webhooks are optional: you set a Webhook URL in the project settings. After every successful payment, including each subscription renewal, Gammal Tech sends a POST to that URL with this JSON body:
{"event": "payment.success", "timestamp": 1748524800, "payment_token": "eyJ…"}The token is shortened here: it is a signed JSON Web Token (see what is JWT). The timestamp counts seconds since the start of 1970. Two headers come with the request, X-Gammal-Event and X-Gammal-Attempt. Your server then does four things:
- Reads
payment_tokenfrom the body. - Sends it in a
POSTto the verify endpointhttps://api.gammal.tech/sdk/pay/link/verify.phpwith the JSON body{"payment_token": "…"}. - Reads
statusin the answer:1means a real payment, verified for the first time.2means a real payment that was verified before, for example by your callback page.0means invalid. - Answers with an HTTP 2xx status, quickly.
Status 1 and status 2 both prove that the payment is real. Which one your server sees depends on who asked first: if the browser of the customer already verified the token on your callback page, your server gets 2. So a server should not wait for 1. It should look up the transaction in its own records and deliver only if it has not delivered before.
Imagine a teacher in Cairo who sells an online course. A student pays by card on his phone and closes the browser before it returns to her thank you page. Without a webhook, her site never hears about the payment, and the paid lessons stay locked. With a webhook, her server gets the message anyway, verifies the token and marks the order as paid.
A short webhook handler in PHP
This handler does the four steps in PHP. deliver_once is your own function: it looks up the transaction ID in your records, gives the customer what they bought only if that transaction was not delivered before, and saves it as delivered. Read the live guide for the full description of the verify answer.
<?php
// webhook.php: called by Gammal Tech
$body = json_decode(file_get_contents('php://input'), true);
$token = $body['payment_token'] ?? '';
// Never trust the body: verify it
$url = 'https://api.gammal.tech/sdk/pay/link/verify.php';
$context = stream_context_create(['http' => [
'method' => 'POST',
'header' => 'Content-Type: application/json',
'content' => json_encode(['payment_token' => $token]),
'ignore_errors' => true,
]]);
$reply = @file_get_contents($url, false, $context);
$answer = json_decode((string) $reply, true);
if (!is_array($answer)) { http_response_code(500); exit; } // retry later
$status = (int) ($answer['status'] ?? 0);
if ($status === 1 || $status === 2) { // a real payment
deliver_once($answer['txn'], $answer); // your own function
}
http_response_code(200);The handler replies 500, not 2xx, only when it gets no usable answer from the verify endpoint, so the delivery is tried again later. The @ stops PHP from printing a warning into the reply. Keep slow work, such as sending emails, out of the handler.
Why the handler must be safe to run twice
If your server does not answer 2xx quickly, Gammal Tech retries the delivery a few times, so the same payment can reach your handler more than once. Code that gives the same final result no matter how many times it runs is called idempotent. Here the transaction ID does the work: txn is the same on every repeat, so deliver_once finds it in your records and does nothing the second time.
A page with no server behind it has no records to check, so there you deliver only on status 1 and never on 2. On a server, your own records are the judge. A useful habit: put your order number in the desc of the payment link. The verify answer returns it as description, so your server knows which order was paid.
Security basics for any webhook
A webhook URL is a public door into your server: anyone who learns the address can send a POST to it. Three habits keep it safe:
- Use HTTPS, so nobody on the network can read or change the message.
- Never trust the body alone. A message that says a payment succeeded is not a payment.
- Verify first, act second. Many services sign each message so that you can check it. Gammal Tech puts a token in the body, and a fake token gets
0from the verify endpoint.
How to test a webhook when there is no test mode
Gammal Tech has no test mode: every payment is real money. Fees and refunds differ by payment method, and the current rules are in the live guide. Read them, then test in three steps:
- A fake request. Send a
POSTto your handler with an invented token. No payment is needed. The verify endpoint answers0, so nothing should be delivered, and your handler should still reply 2xx. A 500 reply means your server could not get an answer from the verify endpoint. - The cancelled path. Open your payment link and cancel. The callback page gets
?status=cancelledand no token, and no order should be delivered. - One small real payment. Pay a small amount yourself. Check that the webhook arrived and that the order was delivered exactly once. Then send the same body again: nothing should be delivered a second time.
Log each request in a private file while you test, and remember that step three is a real payment with the normal fees.
When you do not need a webhook at all
Many small sites need no webhook. After a card payment, the customer returns to your callback page with ?status=success and a payment_token in the address. The page sends the token to the same verify endpoint and delivers on status 1. The token is short lived, so verify it at once.
That is enough for one time payments where a missed return can be fixed with one message. Add a webhook when a missed message costs you: subscriptions, where each renewal happens with no customer in front of a browser, or a shop with many orders.
Common questions about webhooks
Is a webhook the same as an API?
No. With an API call, your code asks another system and waits. A webhook goes the other way: the other system calls your URL. They often work together: the webhook brings the news, and an API call confirms it.
Do I need a server to receive webhooks?
Yes. You need a public URL that accepts a POST and runs code, such as a PHP file on your hosting. A site made only of HTML files cannot receive one, so it should use the callback page with verification.
What happens if my server is down?
Gammal Tech retries the delivery a few times. Do not depend on retries alone: keep the verification on your callback page too. A payment token that you hold can still be checked at the verify endpoint while it is valid.
Are webhooks secure?
As secure as the code that receives them. Use HTTPS, verify the token before you act, and make the handler safe to run twice. Then a fake request does no harm.
Source and service details
Integration and pricing details are based on the Gammal Tech guide. Check the documentation before publishing or changing your project configuration.