config-webhook
Configure Webhook Subscriptionβ
Before receiving webhooks, you need to register your webhook URL.
Endpointβ
POST /apps/api/webhooks
Headers:
Authorization: Bearer {access_token}
Content-Type: application/json
Request Bodyβ
{
"webhookUrl": "https://yourdomain.com/webhooks/wepay"
}
Field Descriptionsβ
| Field | Type | Required? | Description |
|---|---|---|---|
| webhookUrl | String | Yes | Your HTTPS endpoint URL to receive webhook notifications. Must be publicly accessible. Example: https://api.yoursite.com/webhooks |
Example Request (cURL)β
curl -X POST "https://api.wepay.com.sa/apps/api/webhooks" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://yourdomain.com/webhooks/wepay"
}'
Example Responseβ
{
"data": {
"id": "019bb692-28e0-7ae1-926f-59b12c3b784c",
"webhookUrl": "https://yourdomain.com/webhooks/wepay",
"secretKey": "whsec_abc123xyz789defghijklmnopqrstuvwxyz",
"isActive": true,
"consecutiveFailures": 0,
"lastSuccessAt": null,
"lastFailureAt": null,
"createdAt": "2026-01-19T12:00:00Z"
},
"message": "Webhook created successfully.",
"status": 200,
"validationErrors": []
}
Response Fieldsβ
| Field | Description |
|---|---|
| id | Your business entity ID (used as webhook subscription identifier) |
| webhookUrl | Your registered webhook endpoint |
| secretKey | Secret key for signature verification (Save this securely) |
| isActive | Whether the webhook is currently active (false if disabled due to failures) |
| consecutiveFailures | Number of consecutive delivery failures (resets to 0 on success) |
| lastSuccessAt | Timestamp of last successful delivery |
| lastFailureAt | Timestamp of last failed delivery |
| createdAt | When the business entity was created |
Important: Save the
secretKeysecurely. You'll need it to verify webhook signatures.
The secret is only shown once when creating or regenerating. Never expose your secret key in client-side code.
Verifying Webhook Signaturesβ
Verify the webhook signature before processing any webhook.
Signature Formatβ
X-WePay-Signature contains:
sha256={hex_signature}
Verification Processβ
- Extract the signature from
X-WePay-Signature. - Read the raw JSON request body.
- Compute HMAC-SHA256 using your webhook secret key.
- Compare the computed signature with the received signature using constant-time comparison.
Node.js Exampleβ
const crypto = require("crypto")
function verifyWebhookSignature(payload, signature, secretKey) {
const expectedSignature =
"sha256=" +
crypto.createHmac("sha256", secretKey).update(payload, "utf8").digest("hex")
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature),
)
}
app.post("/webhooks/wepay", (req, res) => {
const signature = req.headers["x-wepay-signature"]
const payload = JSON.stringify(req.body)
if (!verifyWebhookSignature(payload, signature, YOUR_SECRET_KEY)) {
return res.status(401).send("Invalid signature")
}
const event = req.body
switch (event.event) {
case "payment.completed":
// handle payment
break
case "contract.released":
// handle release
break
case "refund.full-initiated":
case "refund.full-succeeded":
// handle refund events
break
}
res.status(200).send("OK")
})
PHP Exampleβ
<?php
function verifyWebhookSignature($payload, $signature, $secretKey) {
$expectedSignature = 'sha256=' . hash_hmac('sha256', $payload, $secretKey);
return hash_equals($expectedSignature, $signature);
}
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEPAY_SIGNATURE'] ?? '';
if (!verifyWebhookSignature($payload, $signature, YOUR_SECRET_KEY)) {
http_response_code(401);
exit('Invalid signature');
}
$event = json_decode($payload, true);
switch ($event['event']) {
case 'payment.completed':
break;
case 'contract.released':
break;
case 'refund.full-initiated':
case 'refund.full-succeeded':
break;
}
http_response_code(200);
echo 'OK';
?>
Python Exampleβ
import hmac
import hashlib
from flask import Flask, request, abort
app = Flask(__name__)
SECRET_KEY = 'your_webhook_secret_key'
def verify_signature(payload, signature, secret):
expected = 'sha256=' + hmac.new(
secret.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
@app.route('/webhooks/wepay', methods=['POST'])
def webhook_handler():
signature = request.headers.get('X-WePay-Signature', '')
payload = request.get_data(as_text=True)
if not verify_signature(payload, signature, SECRET_KEY):
abort(401)
event = request.get_json()
if event['event'] == 'payment.completed':
pass
elif event['event'] == 'contract.released':
pass
elif event['event'] in [
'refund.full-initiated',
'refund.full-succeeded'
]:
pass
return 'OK', 200
C# Exampleβ
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
public class WebhookController : ControllerBase
{
private readonly string _secretKey = "your_webhook_secret_key";
private bool VerifyWebhookSignature(string payload, string signature)
{
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_secretKey));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
var expectedSignature = "sha256=" + Convert.ToHexString(hash).ToLowerInvariant();
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(signature),
Encoding.UTF8.GetBytes(expectedSignature)
);
}
[HttpPost("webhooks/wepay")]
public async Task<IActionResult> HandleWebhook()
{
var signature = Request.Headers["X-WePay-Signature"].ToString();
using var reader = new StreamReader(Request.Body);
var payload = await reader.ReadToEndAsync();
if (!VerifyWebhookSignature(payload, signature))
{
return Unauthorized();
}
var webhookEvent = JsonSerializer.Deserialize<WebhookEvent>(payload);
switch (webhookEvent?.Event)
{
case "payment.completed":
break;
case "contract.released":
break;
case "refund.full-initiated":
case "refund.full-succeeded":
break;
}
return Ok();
}
}
Always use constant-time comparison (timingSafeEqual, hash_equals, compare_digest, FixedTimeEquals) to prevent timing attacks.
Get Webhook Subscriptionβ
Retrieve your current webhook configuration.
Endpointβ
GET /apps/api/webhooks
Headers:
Authorization: Bearer {access_token}
Example Request (cURL)β
curl -X GET "https://api.wepay.com.sa/apps/api/webhooks" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Example Responseβ
{
"data": {
"id": "019bb692-28e0-7ae1-926f-59b12c3b784c",
"webhookUrl": "https://yourdomain.com/webhooks/wepay",
"secretKey": "whsec_abc123xyz789defghijklmnopqrstuvwxyz",
"isActive": true,
"consecutiveFailures": 0,
"lastSuccessAt": "2026-01-19T14:30:00Z",
"lastFailureAt": null,
"createdAt": "2026-01-19T12:00:00Z"
},
"message": "",
"status": 200,
"validationErrors": []
}
Update Webhook URLβ
Update your existing webhook subscription with a new URL.
Endpointβ
POST /apps/api/webhooks
Headers:
Authorization: Bearer {access_token}
Content-Type: application/json
Request Bodyβ
{
"webhookUrl": "https://newdomain.com/webhooks/wepay"
}
Example Request (cURL)β
curl -X POST "https://api.wepay.com.sa/apps/api/webhooks" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://newdomain.com/webhooks/wepay"
}'
Example Responseβ
{
"data": {
"id": "019bb692-28e0-7ae1-926f-59b12c3b784c",
"webhookUrl": "https://newdomain.com/webhooks/wepay",
"secretKey": "whsec_abc123xyz789defghijklmnopqrstuvwxyz",
"isActive": true,
"consecutiveFailures": 0,
"lastSuccessAt": "2026-01-19T14:30:00Z",
"lastFailureAt": null,
"createdAt": "2026-01-19T12:00:00Z"
},
"message": "Webhook updated successfully.",
"status": 200,
"validationErrors": []
}
Note: Updating the webhook URL also re-enables the subscription if it was disabled due to consecutive failures.
Regenerate Secret Keyβ
If your secret key is compromised, regenerate it immediately.
Endpointβ
POST /apps/api/webhooks/regenerate-secret
Headers:
Authorization: Bearer {access_token}
Example Request (cURL)β
curl -X POST "https://api.wepay.com.sa/apps/api/webhooks/regenerate-secret" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Example Responseβ
{
"data": {
"secretKey": "whsec_xYz123AbCdEfGhIjKlMnOpQrStUvWxYz456"
},
"message": "Webhook secret regenerated successfully.",
"status": 200,
"validationErrors": []
}
Important: After regenerating, update your server immediately to use the new secret key. Webhooks signed with the old key will fail verification.
Delete Webhook Subscriptionβ
Remove your webhook subscription to stop receiving notifications.
Endpointβ
DELETE /apps/api/webhooks
Example Requestβ
curl -X DELETE "{baseUrl}/apps/api/webhooks" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Example Responseβ
{
"data": true,
"message": "Webhook deleted successfully.",
"status": 200,
"validationErrors": []
}