Webhooks
Receive real-time notifications about payment events
Overview
Webhooks allow you to receive real-time notifications when events occur in your Yassir Payment account. Instead of polling our API for updates, we push event data to your server as they happen.
When you create a service during onboarding, you provide a webhookUrl. Yassir will send a POST request to this URL whenever a payment event occurs.
When Webhooks Fire
Webhooks are sent after the following events:
| Event | Description |
|---|---|
| Proceed (success) | Payment completed successfully via Yassir Wallet |
| Proceed (failure) | Payment was rejected (e.g., insufficient balance) |
| Pre-authorization | Funds were successfully reserved |
| Capture | Reserved funds were captured (full or partial) |
| Release | Reserved funds were released back to the user |
| Refund | A full or partial refund was processed |
Webhook Payload
All webhook events are sent as a POST request with a JSON body in the following format:
{
"actionId": "order_12345",
"remoteStatus": "The amount was deposited successfully",
"remoteStatusCode": 2,
"contextType": "ORDER",
"orderId": "123e4567-e89b-12d3-a456-426614174000",
"additionalData": {}
}{
"actionId": "order_12345",
"remoteStatus": "The amount was deposited successfully",
"remoteStatusCode": 2,
"contextType": "ORDER",
"orderId": "123e4567-e89b-12d3-a456-426614174000",
"additionalData": {}
}Payload Fields
| Field | Type | Description |
|---|---|---|
actionId | string | Your unique identifier from the original payment intent (e.g., your order ID) |
remoteStatus | string | Human-readable status message |
remoteStatusCode | number | Numeric status code (see table below) |
contextType | string | Always "ORDER" |
orderId | string | The Yassir payment ID |
additionalData | object | Additional data (may be empty) |
Status Codes (remoteStatusCode)
| Code | Status | Description |
|---|---|---|
2 | Succeeded | Payment completed or captured successfully |
3 | Rejected | Payment was rejected by the provider |
4 | Refunded | Full refund was processed |
10 | Reversed / Released | Pre-authorized funds were released |
13 | Pre-Authorized | Funds reserved, awaiting capture or release |
16 | Partially Refunded | A partial refund was processed |
Example Payloads
// Payment succeeded
{
"actionId": "order_12345",
"remoteStatus": "The amount was deposited successfully",
"remoteStatusCode": 2,
"contextType": "ORDER",
"orderId": "123e4567-e89b-12d3-a456-426614174000",
"additionalData": {}
}// Payment succeeded
{
"actionId": "order_12345",
"remoteStatus": "The amount was deposited successfully",
"remoteStatusCode": 2,
"contextType": "ORDER",
"orderId": "123e4567-e89b-12d3-a456-426614174000",
"additionalData": {}
}// Pre-authorization completed
{
"actionId": "order_12345",
"remoteStatus": "Pre-authorized",
"remoteStatusCode": 13,
"contextType": "ORDER",
"orderId": "123e4567-e89b-12d3-a456-426614174000",
"additionalData": {}
}// Pre-authorization completed
{
"actionId": "order_12345",
"remoteStatus": "Pre-authorized",
"remoteStatusCode": 13,
"contextType": "ORDER",
"orderId": "123e4567-e89b-12d3-a456-426614174000",
"additionalData": {}
}// Partial refund processed
{
"actionId": "order_12345",
"remoteStatus": "Partially Refunded",
"remoteStatusCode": 16,
"contextType": "ORDER",
"orderId": "123e4567-e89b-12d3-a456-426614174000",
"additionalData": {}
}// Partial refund processed
{
"actionId": "order_12345",
"remoteStatus": "Partially Refunded",
"remoteStatusCode": 16,
"contextType": "ORDER",
"orderId": "123e4567-e89b-12d3-a456-426614174000",
"additionalData": {}
}Setting Up Webhooks
Configure your endpoint
Create an HTTP endpoint on your server to receive webhook events. The endpoint must be publicly accessible and use HTTPS.
Register the webhook URL
Provide your webhook URL as the webhookUrl when creating your service during merchant onboarding. All payment events for that service will be sent to this URL.
Handle events
Process the webhook event and return a 2xx response:
app.post('/webhooks/yassir', async (req, res) => {
const event = req.body;
switch (event.remoteStatusCode) {
case 2: // Succeeded
await handlePaymentSuccess(event.actionId, event.orderId);
break;
case 3: // Rejected
await handlePaymentFailure(event.actionId);
break;
case 4: // Refunded
await handleRefund(event.actionId);
break;
case 10: // Released
await handleRelease(event.actionId);
break;
case 13: // Pre-authorized
await handlePreAuth(event.actionId, event.orderId);
break;
case 16: // Partially refunded
await handlePartialRefund(event.actionId);
break;
}
res.status(200).send('OK');
});app.post('/webhooks/yassir', async (req, res) => {
const event = req.body;
switch (event.remoteStatusCode) {
case 2: // Succeeded
await handlePaymentSuccess(event.actionId, event.orderId);
break;
case 3: // Rejected
await handlePaymentFailure(event.actionId);
break;
case 4: // Refunded
await handleRefund(event.actionId);
break;
case 10: // Released
await handleRelease(event.actionId);
break;
case 13: // Pre-authorized
await handlePreAuth(event.actionId, event.orderId);
break;
case 16: // Partially refunded
await handlePartialRefund(event.actionId);
break;
}
res.status(200).send('OK');
});Best Practices
- Respond to webhooks within 30 seconds
- Process webhook data asynchronously for long operations
- Handle duplicate events using idempotency (use
actionId+remoteStatusCodeas a key) - Always use the
remoteStatusCode(not the text status) for programmatic logic