Webhooks Notification
Set up webhook notifications for payment events — real-time order status updates delivered to your server.
Webhooks Notification
Version: 3.0.0
Automate your Online integration with web-hook data and eliminate manual order management effort in the process.
Enabling Webhooks
In your PayOrc dashboard, configure the following:
- Enable/Disable — Toggle webhook notifications on or off.
- URL — The fully qualified (secure) URL on your server to which web-hook data will be posted.
- notification-secret — Your secret will be posted in the header for additional security.
Your webhook endpoint should respond with a 200 OK status code to acknowledge receipt. If the webhook delivery fails, PayOrc may retry the delivery.
Webhook Event Types
The following event types trigger webhook notifications:
| Event Type | Description |
|---|---|
| AUTH | Transaction authorized — funds reserved but not yet captured |
| AUTH_REVERSAL | Card tokenization (Add Card) — authorization voided; m_payment_token returned |
| AWAIT_3DS | Waiting for 3D Secure authentication from the customer |
| CAPTURE | Funds have been captured and debited from the customer's account |
| PARTIALLY_CAPTURED | A portion of the authorized amount has been captured |
| REFUND | Full refund processed — funds returned to the customer |
| PARTIALLY_REFUNDED | A portion of the captured amount has been refunded |
| VOID | Transaction voided — the authorization has been cancelled |
| FAILED | Transaction failed due to decline, insufficient funds, or other error |
| EXIT | Order timeout — the order was pending for 30+ minutes without completion |
| INVOICE_CREATED | Invoice created for SADAD bank transfer payments |
| CANCEL | Order cancelled by the merchant or system |
| TIMEOUT | Transaction timeout — the processing window expired |
Processing Logic
Use the action and status fields to determine how to update your order:
SUCCESS ORDER:
if(action==AUTH and status==SUCCESS)
{
//update order as AUTHORISED and ready to capture
}
if(action==CAPTURE and status==SUCCESS)
{
//update order as CAPTURED
}FAILED ORDER:
if(action==AUTH and status==FAILED)
{
//update order as FAILED
}
if(action==CAPTURE and status==FAILED)
{
//update order as FAILED
}AWAIT_3DS:
if(action==AWAIT_3DS and status==SUCCESS)
{
//update order as AWAIT_3DS
}REFUND SUCCESS:
if(action==REFUND and status==SUCCESS)
{
//update order as REFUNDED
}REFUND FAILED:
if(action==REFUND and status==FAILED)
{
//send mail to customer
}AUTH REVERSAL (Add Card) SUCCESS:
if(action==AUTH_REVERSAL and status==SUCCESS)
{
// store m_payment_token for Pay by Token (CAUTH)
}parameters and custom_data are arrays of single-key objects (alpha…epsilon). Values are echoed from order create when provided; otherwise each value is typically an empty string "".
Webhook Payload Structure
Every webhook notification contains the following JSON payload:
{
"action": "AUTH",
"status": "SUCCESS",
"order_status": "AUTHORISED",
"m_order_id": "your-order-123",
"p_order_id": "1000010108",
"p_request_id": "1000013993",
"psp_ref_id": "8902217",
"transaction_id": "1000010658",
"is_live": false,
"terminal_label": "paymob-terminal-01",
"remark": "",
"reason": "",
"currency": "AED",
"amount": "280.00",
"channel": "ORD API",
"channel_id": "PL-001",
"shipping_fee": "30.00",
"convenience_fee": "50.00",
"customer_details": {
"name": "John Doe",
"email": "[email protected]",
"code": "91",
"mobile": "9876543210",
"m_customer_id": "CUST-001"
},
"billing_address": {
"address_line_1": "123 Main Street",
"address_line_2": "",
"city": "Springfield",
"state": "",
"country": "US",
"pincode": ""
},
"shipping_address": {
"address_line_1": "456 Ship Ave",
"address_line_2": "",
"city": "Springfield",
"state": "",
"country": "US",
"pincode": ""
},
"ip_country": "AE",
"psp": "PAYMOB",
"payment_method": "DEBIT CARD",
"m_payment_token": "ZXh...",
"transaction_time": "16-02-2026 12:57:58",
"payment_method_data": {
"scheme": "VISA",
"card_country": "POLAND",
"card_type": "DEBIT",
"mask_card_number": "4111****1111",
"expiry_month": "12",
"expiry_year": "2045"
},
"apm_name": "",
"apm_identifier": "",
"sub_merchant_identifier": "0000001181",
"parameters": [
{ "alpha": "" },
{ "beta": "" },
{ "gamma": "" },
{ "delta": "" },
{ "epsilon": "" }
],
"custom_data": [
{ "alpha": "" },
{ "beta": "" },
{ "gamma": "" },
{ "delta": "" },
{ "epsilon": "" }
]
}For Add Card (AUTH REVERSAL), the same payload shape is used with action: "AUTH_REVERSAL" and a non-empty m_payment_token when tokenization succeeds.
Payload Fields Reference
| Field | Type | Description |
|---|---|---|
| action | String | Transaction action that triggered the webhook. Examples: AUTH, AUTH_REVERSAL, AWAIT_3DS, CAPTURE, REFUND, VOID, EXIT, INVOICE_CREATED, CANCEL, TIMEOUT, FAILED, SALE (channel-dependent). |
| status | String | Overall transaction execution status. Will be "SUCCESS" for all cases (AUTHORISED, AWAIT_3DS, CAPTURED, REFUNDED, VOID, EXIT, INVOICE_CREATED, TIMEOUT, CANCELLED, PARTIALLY_CAPTURED, PARTIALLY_REFUNDED) OR "FAILED" only when the transaction failed. Even statuses like EXIT, TIMEOUT, CANCELLED, AWAIT_3DS, and INVOICE_CREATED will have status "SUCCESS". Only when order_status is "FAILED" will status be "FAILED". |
| order_status | String | Detailed order status from the system. Provides the specific state of the order. Possible values: AUTHORISED, AWAIT_3DS, CAPTURED, REFUNDED, PARTIALLY_CAPTURED, PARTIALLY_REFUNDED, VOID, FAILED, CANCELLED, TIMEOUT, EXIT, INVOICE_CREATED. This is the detailed status that shows exactly what happened with the order. |
| m_order_id | String | Order ID provided by the merchant. |
| p_order_id | String | Order ID generated by the PayOrc platform. |
| p_request_id | String | Unique PayOrc ID for the transaction event. |
| psp_ref_id | String | Reference ID received from the PSP. |
| transaction_id | String | Unique PayOrc transaction identifier for follow-up actions. |
| is_live | Boolean | Indicates whether the transaction was processed in live mode. |
| terminal_label | String | Terminal label or identifier used for processing the transaction. |
| currency | String | Transaction currency (ISO 4217). |
| amount | String | Transaction amount (e.g. "1.00"). |
| shipping_fee | String | Shipping charges applied to the transaction. |
| convenience_fee | String | Convenience or service fee applied to the transaction. |
| channel | String | Payment initiation channel. |
| channel_id | String | Unique identifier of the channel (e.g., payment link ID). |
| customer_details | Object | Customer information provided during order creation. |
| customer_details.name | String | Customer full name. |
| customer_details.email | String | Customer email address. |
| customer_details.code | String | Country calling code of the customer. |
| customer_details.mobile | String | Customer mobile number. |
| customer_details.m_customer_id | String | Merchant-side customer identifier. |
| billing_address | Object | Billing address of the customer. |
| billing_address.address_line_1 | String | Billing address line 1. |
| billing_address.address_line_2 | String | Billing address line 2. |
| billing_address.city | String | Billing city. |
| billing_address.state | String | Billing state / province. |
| billing_address.country | String | Billing country code. |
| billing_address.pincode | String | Billing postal / ZIP code. |
| shipping_address | Object | Shipping address of the customer (same keys as billing_address). |
| ip_country | String | Country detected from the customer's IP address. |
| remark | String | Additional remarks related to the transaction. |
| reason | String | Failure or informational reason code. |
| psp | String | Payment service provider name. |
| payment_method | String | Payment method used by the customer. |
| m_payment_token | String | Payment token generated for the transaction (important for AUTH REVERSAL / Add Card). |
| transaction_time | String | Date and time when the transaction was processed. |
| payment_method_data | Object | Card or payment instrument details. |
| payment_method_data.scheme | String | Card scheme (e.g. VISA). |
| payment_method_data.card_country | String | Card issuing country. |
| payment_method_data.card_type | String | Card type (e.g. DEBIT, CREDIT). |
| payment_method_data.mask_card_number | String | Masked card number. |
| payment_method_data.expiry_month | String | Card expiry month (MM). |
| payment_method_data.expiry_year | String | Card expiry year (YYYY). |
| apm_name | String | Name of the alternative payment method. |
| apm_identifier | String | Identifier for the alternative payment method. |
| sub_merchant_identifier | String | Sub-merchant identifier mapped with the PSP. |
| parameters | Array | Merchant metadata echoed from order create — array of { "alpha" } … { "epsilon" } objects. |
| custom_data | Array | Custom data echoed from order create — same array shape as parameters. |
Code Examples: Webhook Handler
const express = require('express');
const app = express();
app.post('/payorc/webhook', express.json(), (req, res) => {
const { action, status, order_status, m_order_id, p_order_id, transaction_id } = req.body;
console.log(`Webhook: ${action} | ${status} | Order: ${m_order_id}`);
// Process based on action and status
switch (action) {
case 'AUTH':
if (status === 'SUCCESS') {
// Update order as AUTHORISED and ready to capture
updateOrderStatus(m_order_id, 'AUTHORISED');
} else {
// Authorization failed
updateOrderStatus(m_order_id, 'FAILED');
}
break;
case 'AUTH_REVERSAL':
if (status === 'SUCCESS') {
// Store m_payment_token for Pay by Token (CAUTH)
savePaymentToken(m_order_id, req.body.m_payment_token, req.body.customer_details?.m_customer_id);
} else {
updateOrderStatus(m_order_id, 'FAILED');
}
break;
case 'CAPTURE':
if (status === 'SUCCESS') {
// Order captured — fulfill
updateOrderStatus(m_order_id, 'CAPTURED');
} else {
updateOrderStatus(m_order_id, 'FAILED');
}
break;
case 'AWAIT_3DS':
// Customer is completing 3D Secure authentication
updateOrderStatus(m_order_id, 'AWAIT_3DS');
break;
case 'REFUND':
if (status === 'SUCCESS') {
updateOrderStatus(m_order_id, 'REFUNDED');
} else {
// Refund failed — notify customer
sendRefundFailedEmail(m_order_id);
}
break;
case 'VOID':
updateOrderStatus(m_order_id, 'VOID');
break;
case 'FAILED':
updateOrderStatus(m_order_id, 'FAILED');
break;
case 'EXIT':
case 'TIMEOUT':
case 'CANCEL':
updateOrderStatus(m_order_id, order_status);
break;
default:
console.log(`Unhandled action: ${action}`);
}
// Always respond 200 OK quickly
res.status(200).send('OK');
});
app.listen(3000, () => console.log('Webhook server running on port 3000'));Best Practices
Idempotency: Always use the p_order_id or transaction_id from the webhook payload to check if you've already processed this event. Webhooks may be delivered more than once due to retries — your handler must be idempotent.
1. Process Async, Respond Fast
Your webhook endpoint should return a 200 OK within 5 seconds. Queue heavier processing (database updates, email notifications, inventory changes) to a background worker.
2. Implement Idempotency
Store processed webhook IDs (using p_request_id or transaction_id) to prevent duplicate processing.
3. Log Everything
Maintain a detailed log of every webhook received, including the full payload, timestamp, and processing result. This is invaluable for debugging and reconciliation.
4. Handle All Event Types
Even if you only expect certain events (e.g., AUTH and CAPTURE), always handle all event types gracefully. Unknown events should be logged but not cause errors.
5. Use HTTPS Only
Webhook URLs must use HTTPS. PayOrc will not deliver webhooks to HTTP endpoints to protect sensitive payment data in transit.