Receiving & Processing Webhooks
DigetPay sends HTTP POST notifications to your configured webhook URL when payment events occur, such as sales, captures, and refunds.
This page is the main implementation guide for receiving and processing DigetPay payment webhooks. It combines the webhook requirements, processing flow, payload structure, idempotency guidance, retry behavior, and code examples.
Webhooks are the recommended way to receive server-to-server payment updates. Implement webhook handling before moving your integration to production.
How Webhook Delivery Works
sequenceDiagram
autonumber
participant Customer as Customer
participant Gateway as Payment Gateway
participant DigetPay as DigetPay
participant Merchant as Merchant Server
participant Database as Merchant Database
Customer->>Gateway: Complete payment
Gateway->>DigetPay: Payment event
DigetPay->>DigetPay: Update transaction record
DigetPay->>Merchant: HTTP POST webhook
Merchant->>Merchant: Validate and parse payload
Merchant->>Database: Check transaction/event
Merchant->>Database: Process if not already handled
Merchant-->>DigetPay: HTTP 200 OK
Your webhook endpoint should acknowledge the notification quickly and perform heavy processing asynchronously.
Requirements
| Requirement | Detail |
|---|---|
| HTTPS | Use a publicly reachable HTTPS endpoint. |
| HTTP Method | Accept POST requests. |
| Content Type | Accept application/json requests. |
| Response | Return HTTP 200 OK quickly after receiving a valid webhook. |
| Idempotency | Handle duplicate deliveries safely. |
| Logging | Store sufficient transaction information for troubleshooting and auditing. |
| Environment | Keep staging and production webhook endpoints separate. |
Critical: Return HTTP
200 OKas soon as the webhook has been accepted. Do not keep the request open while performing slow database operations, order fulfillment, notifications, or other heavy processing.
Recommended Processing Flow
A recommended webhook handler should follow this sequence:
- Receive the HTTP
POSTrequest. - Parse the JSON payload.
- Validate that the required transaction information is present.
- Check whether the event has already been processed.
- Return HTTP
200 OKonce the webhook has been accepted. - Process the payment event asynchronously.
- Update the corresponding order or transaction.
- Store the processing result for auditing and troubleshooting.
flowchart TD
A[Receive webhook] --> B[Parse JSON]
B --> C{Valid payload?}
C -- No --> D[Reject request]
C -- Yes --> E{Already processed?}
E -- Yes --> F[Return 200 OK]
E -- No --> G[Accept webhook]
G --> H[Return 200 OK]
H --> I[Process asynchronously]
I --> J[Update order / transaction]
J --> K[Store processing result]
Idempotency and Duplicate Events
DigetPay may deliver the same event more than once. Your integration must therefore be idempotent.
Use the transaction reference and event type to determine whether an incoming notification has already been processed.
For example:
transactionId + typeBefore applying a payment update, check whether the same transaction event has already been handled.
Important: Never create a second order, fulfill an order twice, or apply the same refund twice because the same webhook was delivered more than once.
Example
If your system receives:
{
"transactionId": "2232e99b-0257-47d5-bbfd-022c8951767f",
"type": "Sale",
"status": "Approved"
}and receives the same event again, the second delivery should be recognized as already processed and should not trigger the business operation again.
Retry and Delivery Behavior
Webhook delivery should be treated as at-least-once delivery. The same event may be delivered more than once, so your endpoint must be able to safely process duplicate notifications.
DigetPay may retry a webhook delivery when the notification is not successfully acknowledged, including cases such as:
- The endpoint does not respond within the expected time.
- The connection times out.
- The endpoint returns a non-success response.
Important: Do not assume that receiving the same webhook more than once means that multiple payments occurred. Use idempotency to prevent duplicate processing.
Retry / Backoff Policy
DigetPay's merchant payment webhook documentation does not currently expose a fixed public schedule for the number of retry attempts, retry intervals, or maximum retry window.
Therefore, integrations must not depend on a specific retry count or timing.
Instead:
- Return HTTP
200 OKas soon as the webhook has been accepted. - Process the event asynchronously when possible.
- Store the transaction/event reference.
- Make processing idempotent.
- Treat every delivery as potentially duplicated.
- Use the Payment Status API when your business flow requires additional transaction confirmation.
The retry behavior described here applies to merchant payment webhooks. Do not use retry/backoff rules documented for unrelated DigetPay Developer Portal or marketplace products as the payment webhook retry policy.
Example Webhook Payload
{
"transactionId": "2232e99b-0257-47d5-bbfd-022c8951767f",
"orderId": "PAY-1781872369616",
"amount": 0.2,
"currencyCode": "682",
"status": "Approved",
"type": "Sale",
"cardScheme": "Mada",
"channel": "Payment Gateway"
}Important Fields
| Field | Description |
|---|---|
transactionId | DigetPay gateway transaction UUID. Use it when querying the transaction status or performing supported transaction operations. |
orderId | Merchant order reference associated with the transaction. |
amount | Transaction amount. |
currencyCode | Currency code associated with the transaction. |
status | Current transaction status. |
type | Transaction/event type. |
cardScheme | Card scheme associated with the transaction, when applicable. |
channel | Channel through which the transaction was processed. |
Store
transactionId: Keep the DigetPay transaction ID with your order record. It can be used to correlate webhook notifications with transaction status and supported transaction operations.
Expected Response
Your webhook endpoint should return HTTP 200 OK after successfully accepting the notification.
Example:
HTTP/1.1 200 OK
Content-Type: text/plain
OKA short response body such as OK is sufficient.
Do not perform long-running operations before returning the response. Queue the event or process it asynchronously when additional processing is required.
Code Examples
The following examples demonstrate the basic webhook receiver pattern. Adapt the persistence, idempotency, and business logic to your application.
<?php
declare(strict_types=1);
$raw = file_get_contents('php://input');
if ($raw === false) {
http_response_code(400);
exit;
}
$payload = json_decode($raw, true);
if (!is_array($payload)) {
http_response_code(400);
exit;
}
$transactionId = $payload['transactionId'] ?? null;
$orderId = $payload['orderId'] ?? null;
$status = $payload['status'] ?? null;
$type = $payload['type'] ?? null;
if (!$transactionId || !$type) {
http_response_code(400);
exit;
}
/*
* Check whether transactionId + type
* has already been processed.
*
* If already processed:
* return 200 without repeating the business operation.
*/
http_response_code(200);
echo 'OK';
/*
* Process the event asynchronously:
* - Update order
* - Store transaction status
* - Trigger fulfillment
* - Record processing result
*/import express from "express";
const router = express.Router();
router.post(
"/webhooks/digetpay",
express.raw({ type: "application/json" }),
async (req, res) => {
let payload;
try {
payload = JSON.parse(req.body.toString());
} catch {
return res.status(400).send("Invalid JSON");
}
const {
transactionId,
orderId,
status,
type
} = payload;
if (!transactionId || !type) {
return res.status(400).send("Invalid payload");
}
/*
* Check whether transactionId + type
* has already been processed.
*
* If already processed:
* return 200 without repeating the business operation.
*/
res.status(200).send("OK");
/*
* Process the event asynchronously:
* - Update order
* - Store transaction status
* - Trigger fulfillment
* - Record processing result
*/
}
);
export default router;from flask import Blueprint, request
webhooks = Blueprint("webhooks", __name__)
@webhooks.route("/webhooks/digetpay", methods=["POST"])
def digetpay():
payload = request.get_json(silent=True)
if not isinstance(payload, dict):
return "Invalid JSON", 400
transaction_id = payload.get("transactionId")
order_id = payload.get("orderId")
status = payload.get("status")
event_type = payload.get("type")
if not transaction_id or not event_type:
return "Invalid payload", 400
# Check whether transaction_id + event_type
# has already been processed.
# Return 200 after accepting the event.
# Process heavy business logic asynchronously.
return "OK", 200@RestController
@RequestMapping("/webhooks")
public class DigetPayWebhookController {
@PostMapping("/digetpay")
public ResponseEntity<String> webhook(
@RequestBody Map<String, Object> payload) {
String transactionId =
(String) payload.get("transactionId");
String orderId =
(String) payload.get("orderId");
String status =
(String) payload.get("status");
String type =
(String) payload.get("type");
if (transactionId == null || type == null) {
return ResponseEntity.badRequest()
.body("Invalid payload");
}
/*
* Check whether transactionId + type
* has already been processed.
*
* If already processed:
* return 200 without repeating the business operation.
*/
/*
* Process heavy business logic asynchronously
* after accepting the webhook.
*/
return ResponseEntity.ok("OK");
}
}S2S Callback Ingress
For S2S integrations, the gateway may also post transaction callbacks through the S2S callback flow.
Example:
POST /v1/payment/s2s/callback/{merchantId}DigetPay processes the gateway callback and forwards the relevant notification to the merchant callback URL configured for the merchant.
Configure the merchant callback URL through Webhook Configuration.
Testing on Fin
Before enabling the integration in production:
- Configure your webhook endpoint using Webhook Configuration.
- Make sure the endpoint is publicly reachable over HTTPS.
- Run a test transaction in Fin staging.
- Confirm that the webhook request reaches your server.
- Verify the payload and transaction reference.
- Return HTTP
200 OK. - Confirm that your order or transaction is updated correctly.
- Test duplicate delivery handling.
- Review your server logs for errors.
Troubleshooting
Webhook not received
Check that:
- The webhook URL is correct.
- The endpoint is publicly reachable.
- HTTPS is configured correctly.
- The webhook is registered under the correct merchant account.
- You are testing in the correct environment.
- Your firewall or WAF is not blocking the request.
Webhook is received multiple times
This is expected behavior for at-least-once delivery.
Check that your implementation uses an idempotency mechanism based on the transaction/event reference and does not repeat the business operation.
Webhook requests time out
Return 200 OK immediately after accepting the event and move slow processing to an asynchronous worker or queue.
Transaction status is unclear
Do not use the webhook alone when your business flow requires additional confirmation. Use the documented Transaction Status API to verify the transaction.
Best Practices
- Use HTTPS for all webhook endpoints.
- Return
200 OKquickly. - Process heavy operations asynchronously.
- Implement idempotency.
- Store
transactionIdwith your order or transaction record. - Log received webhook events and processing results.
- Keep staging and production endpoints separate.
- Do not assume that each webhook delivery represents a new payment.
- Use the Payment Status API when additional transaction confirmation is required.
- Keep your webhook handler available and publicly reachable.
Related Documentation
Configure and manage webhook URLs through the Merchant Portal and Portal API.
Review supported event types and webhook payload fields.
Review webhook security and validation requirements.
Learn about DigetPay API credentials and authentication.
Updated 22 days ago

