config-webhook
Configure Webhook Subscription
Before receiving webhooks, you need to register your webhook URL.
Endpoint
POST {{EnvironmentUrl}}/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 "{{EnvironmentUrl}}/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 "{{EnvironmentUrl}}/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 {{EnvironmentUrl}}/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 "{{EnvironmentUrl}}/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 {{EnvironmentUrl}}/apps/api/webhooks/regenerate-secret
Headers:
Authorization: Bearer {access_token}
Example Request (cURL)
curl -X POST "{{EnvironmentUrl}}/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": []
}