Webhooks API
Webhooks allow you to receive real-time notifications about events in your Notch Pay account. Instead of polling the API for updates, webhooks push data to your server when events occur.
How Webhooks Work
Create a Webhook Endpoint
First, create an endpoint on your server that can receive HTTP POST requests. This endpoint will process webhook events from Notch Pay.
Register the Endpoint
Register your endpoint URL with Notch Pay through the API or dashboard. You can specify which events you want to receive.
Receive Events
When an event occurs (like a payment with status “complete”), Notch Pay sends a POST request to your endpoint with event data.
Process the Event
Your endpoint processes the event data and takes appropriate action (e.g., fulfilling an order after a payment).
Respond with 200 OK
Your endpoint should respond with a 200 OK status code to acknowledge receipt of the webhook.
Webhook Event Object
{
"id" : "evt_123456789" ,
"type" : "payment.complete" ,
"created_at" : "2023-01-01T12:00:00Z" ,
"data" : {
"id" : "pay_123456789" ,
"reference" : "order_123" ,
"amount" : 5000 ,
"currency" : "XAF" ,
"status" : "complete" ,
"customer" : "cus_123456789" ,
"created_at" : "2023-01-01T12:00:00Z" ,
"completed_at" : "2023-01-01T12:05:00Z"
}
}
Webhook Event Properties
Unique identifier for the event
Type of event (e.g., payment.complete, transfer.failed)
Timestamp when the event was created
The resource that triggered the event (e.g., a payment or transfer object)
API Endpoints
List Webhooks Retrieve a list of all webhook endpoints for your account. Query Parameters Number of items per page (default: 30, max: 100)
{
"code" : 200 ,
"status" : "OK" ,
"message" : "Webhooks retrieved" ,
"data" : [
{
"id" : "wh_123456789" ,
"url" : "https://example.com/webhooks" ,
"events" : [ "payment.complete" , "payment.failed" ],
"active" : true ,
"created_at" : "2023-01-01T12:00:00Z"
},
// More webhooks...
]
}
Create a Webhook Create a new webhook endpoint. Request Parameters The URL where webhook events will be sent
Array of event types to subscribe to
Description of the webhook
Whether the webhook is active (default: true)
Example Request {
"url" : "https://example.com/webhooks" ,
"events" : [ "payment.complete" , "payment.failed" ],
"description" : "Payment notifications for my e-commerce site"
}
{
"code" : 201 ,
"status" : "Created" ,
"message" : "Webhook created" ,
"data" : {
"id" : "wh_123456789" ,
"url" : "https://example.com/webhooks" ,
"events" : [ "payment.complete" , "payment.failed" ],
"description" : "Payment notifications for my e-commerce site" ,
"active" : true ,
"created_at" : "2023-01-01T12:00:00Z"
}
}
Retrieve a Webhook Retrieve details of a specific webhook. Path Parameters ID of the webhook to retrieve
{
"code" : 200 ,
"status" : "OK" ,
"message" : "Webhook retrieved" ,
"data" : {
"id" : "wh_123456789" ,
"url" : "https://example.com/webhooks" ,
"events" : [ "payment.complete" , "payment.failed" ],
"description" : "Payment notifications for my e-commerce site" ,
"active" : true ,
"created_at" : "2023-01-01T12:00:00Z"
}
}
Update a Webhook Update an existing webhook. Path Parameters ID of the webhook to update
Request Parameters The URL where webhook events will be sent
Array of event types to subscribe to
Description of the webhook
Whether the webhook is active
Example Request {
"events" : [ "payment.complete" , "payment.failed" , "transfer.complete" ],
"active" : true
}
{
"code" : 200 ,
"status" : "OK" ,
"message" : "Webhook updated" ,
"data" : {
"id" : "wh_123456789" ,
"url" : "https://example.com/webhooks" ,
"events" : [ "payment.complete" , "payment.failed" , "transfer.complete" ],
"description" : "Payment notifications for my e-commerce site" ,
"active" : true ,
"created_at" : "2023-01-01T12:00:00Z" ,
"updated_at" : "2023-01-02T12:00:00Z"
}
}
Delete a Webhook Delete a webhook endpoint. Path Parameters ID of the webhook to delete
{
"code" : 200 ,
"status" : "OK" ,
"message" : "Webhook deleted"
}
Event Types
Payment Events Triggered when a payment is created
Triggered when a payment starts processing
Triggered when a payment status changes to “complete”
Triggered when a payment fails
Triggered when a payment is canceled
Triggered when a payment expires
Transfer Events Triggered when a transfer is created
Triggered when a transfer starts processing
Triggered when a transfer status changes to “complete”
Triggered when a transfer fails
Customer Events Triggered when a customer is created
Triggered when a customer is updated
Beneficiary Events Triggered when a beneficiary is created
Triggered when a beneficiary is updated
Triggered when a beneficiary is deleted
Webhook Security
Always verify webhook signatures to ensure they’re coming from Notch Pay and not from an attacker.
Retrieve Your Webhook Secret
Each webhook has a secret that you can find in your dashboard or when creating a webhook via the API.
Extract the Signature
When Notch Pay sends a webhook, it includes a X-Notch-Signature header with a signature.
Verify the Signature
const crypto = require ( 'crypto' );
function verifyWebhookSignature ( payload , signature , secret ) {
const hmac = crypto . createHmac ( 'sha256' , secret );
const calculatedSignature = hmac . update ( payload ). digest ( 'hex' );
return crypto . timingSafeEqual (
Buffer . from ( calculatedSignature , 'hex' ),
Buffer . from ( signature , 'hex' )
);
}
// In your webhook handler
app . post ( '/webhooks' , express . raw ({ type: 'application/json' }), ( req , res ) => {
const signature = req . headers [ 'x-notch-signature' ];
const payload = req . body . toString ();
if ( ! verifyWebhookSignature ( payload , signature , 'your_webhook_secret' )) {
return res . status ( 400 ). send ( 'Invalid signature' );
}
const event = JSON . parse ( payload );
// Process the event
res . status ( 200 ). send ( 'Webhook received' );
});
<? php
function verifyWebhookSignature ( $payload , $signature , $secret ) {
$calculatedSignature = hash_hmac ( 'sha256' , $payload , $secret );
return hash_equals ( $calculatedSignature , $signature );
}
// In your webhook handler
$payload = file_get_contents ( 'php://input' );
$signature = $_SERVER [ 'HTTP_X_NOTCH_SIGNATURE' ];
if ( ! verifyWebhookSignature ( $payload , $signature , 'your_webhook_secret' )) {
http_response_code ( 400 );
echo 'Invalid signature' ;
exit ;
}
$event = json_decode ( $payload , true );
// Process the event
http_response_code ( 200 );
echo 'Webhook received' ;
import hmac
import hashlib
def verify_webhook_signature ( payload , signature , secret ):
calculated_signature = hmac.new(
secret.encode( 'utf-8' ),
payload.encode( 'utf-8' ),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(calculated_signature, signature)
# In your webhook handler (using Flask)
@app.route ( '/webhooks' , methods = [ 'POST' ])
def handle_webhook ():
payload = request.data.decode( 'utf-8' )
signature = request.headers.get( 'X-Notch-Signature' )
if not verify_webhook_signature(payload, signature, 'your_webhook_secret' ):
return 'Invalid signature' , 400
event = json.loads(payload)
# Process the event
return 'Webhook received' , 200
Best Practices
Respond Quickly Respond to webhooks with a 200 status code as quickly as possible, even before processing the event. This prevents Notch Pay from retrying the webhook. Process the event asynchronously if it requires time-consuming operations.
Handle Retries Notch Pay will retry failed webhook deliveries several times with increasing delays. Make your webhook handler idempotent to avoid processing the same event multiple times. Store the event ID and check if you’ve already processed it before taking action.
Monitor Webhook Health Regularly check your webhook logs in the Notch Pay dashboard to ensure they’re being delivered correctly. Set up monitoring for your webhook endpoint to detect and alert on failures.
Use HTTPS Always use HTTPS for your webhook endpoints to ensure the data is encrypted in transit. Use a valid SSL certificate from a trusted certificate authority.
Testing Webhooks
Using the Dashboard
Using a Webhook Debugger
Local Development
Go to Webhooks Section
Navigate to the Webhooks section in your Notch Pay dashboard.
Select a Webhook
Select the webhook you want to test.
Click 'Send Test Event'
Click the “Send Test Event” button and select the event type you want to test.
Check Your Logs
Check your server logs to confirm that the test event was received and processed correctly.
Set Up a Webhook Debugger
Create a Test Webhook
Create a new webhook in your Notch Pay account using the temporary URL.
Trigger Events
Trigger events (like creating a test payment) to see the webhook payloads in real-time.
Analyze the Payload
Analyze the webhook payload to understand the data structure and implement your handler accordingly.
Remember to delete test webhooks when you’re done to avoid sending sensitive data to third-party services.
Use a Tunnel Service
Use a service like ngrok or Localtunnel to expose your local server to the internet. # Example with ngrok
ngrok http 3000
Create a Webhook
Create a webhook in your Notch Pay account using the tunnel URL. https://abc123.ngrok.io/webhooks
Implement Your Handler
Implement your webhook handler in your local development environment.
Trigger Events
Trigger events in your Notch Pay test account to test your handler.
Troubleshooting
Webhook Not Receiving Events
Check Webhook Status
Verify that the webhook is active in your Notch Pay dashboard.
Check Event Subscriptions
Make sure you’re subscribed to the event types you expect to receive.
Check Server Logs
Check your server logs for any errors or failed webhook deliveries.
Verify Endpoint Accessibility
Ensure your endpoint is publicly accessible and responds with a 200 status code.
Check Secret Key
Verify that you’re using the correct webhook secret.
Check Raw Payload
Ensure you’re verifying the signature against the raw request body, not the parsed JSON.
Check for Modifications
Make sure the payload isn’t being modified before verification (e.g., by a proxy or middleware).
Implement Idempotency
Store processed event IDs and check for duplicates before processing. // Example with a database
async function handleWebhook ( event ) {
// Check if we've already processed this event
const existingEvent = await db . events . findOne ({ id: event . id });
if ( existingEvent ) {
console . log ( `Event ${ event . id } already processed, skipping` );
return ;
}
// Process the event
await processEvent ( event );
// Store the event ID to prevent duplicate processing
await db . events . insertOne ({ id: event . id , processed_at: new Date () });
}
Use Database Constraints
Use unique constraints in your database to prevent duplicate event processing.