Documentation Index Fetch the complete documentation index at: https://paystack-sdk.efobi.dev/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Webhook class provides a secure, type-safe way to handle webhook events from Paystack. It automatically verifies webhook signatures, parses event payloads, and routes events to registered handlers.
Key Features
Automatic signature verification using HMAC SHA-512
Type-safe event handlers with full TypeScript support
Event-driven architecture with chainable .on() method
Platform-agnostic works with Express, Next.js, Hono, and more
Zod validation for runtime type safety
Methods
Registers a handler function for a specific webhook event. This method is chainable, allowing you to register multiple handlers at once.
paystack . webhook
. on ( "charge.success" , ( data ) => {
console . log ( `Payment received: ${ data . reference } ` );
console . log ( `Amount: ${ data . amount / 100 } ` );
console . log ( `Customer: ${ data . customer . email } ` );
})
. on ( "transfer.success" , ( data ) => {
console . log ( `Transfer completed: ${ data . reference } ` );
console . log ( `Recipient: ${ data . recipient . name } ` );
});
Parameters
The function to execute when the event is received. The function receives the event data as its parameter and can be async
Returns
Returns the Webhook instance for chaining.
process
Processes an incoming webhook request by verifying the signature, parsing the payload, and dispatching it to the appropriate handler.
import express from 'express' ;
import { Paystack } from '@efobi/paystack' ;
const app = express ();
const paystack = new Paystack ( process . env . PAYSTACK_SECRET_KEY ! );
// Register event handlers
paystack . webhook . on ( "charge.success" , ( data ) => {
console . log ( "Payment successful:" , data . reference );
});
// Webhook endpoint
app . post (
'/webhooks/paystack' ,
express . raw ({ type: 'application/json' }),
async ( req , res ) => {
try {
const signature = req . headers [ 'x-paystack-signature' ] as string ;
const rawBody = req . body . toString ( 'utf-8' );
await paystack . webhook . process ( rawBody , signature );
res . status ( 200 ). json ({ message: 'Webhook processed' });
} catch ( error ) {
console . error ( error . message );
res . status ( 400 ). json ({ message: 'Error processing webhook' });
}
}
);
Parameters
The raw, unparsed request body as a string. Important : Do not parse the JSON before passing it to this method, as the signature verification requires the raw body
The value of the x-paystack-signature header from the webhook request
Returns
Returns a Promise that resolves to the parsed webhook payload if successful.
Throws
Error("Missing 'x-paystack-signature' header") - If the signature header is missing
Error("Invalid webhook signature") - If the signature verification fails
Error("Failed to parse webhook payload") - If the payload doesn’t match the expected schema
Supported Events
The SDK supports all Paystack webhook events with full TypeScript types:
Charge Events
Triggered when a charge is successful. Handler data includes:
reference - Transaction reference
amount - Amount in kobo
customer - Customer details (email, name, etc.)
authorization - Card/payment authorization details
metadata - Custom metadata
Triggered when a dispute is created on a charge. Handler data includes:
id - Dispute ID
transaction - Full transaction details
customer - Customer details
status - Dispute status
Triggered to remind you of a pending dispute.
Triggered when a dispute is resolved.
Transfer Events
Triggered when a transfer is successful. Handler data includes:
reference - Transfer reference
amount - Transfer amount
recipient - Recipient details
status - Transfer status
Triggered when a transfer fails.
Triggered when a transfer is reversed.
Subscription Events
Triggered when a subscription is created. Handler data includes:
subscription_code - Unique subscription code
plan - Plan details
customer - Customer details
authorization - Payment authorization
Triggered when a subscription is disabled.
Triggered when a subscription will not renew.
subscription.expiring_cards
Triggered to alert about expiring cards on subscriptions. Returns an array of subscriptions with expiring cards.
Invoice Events
Triggered when an invoice is created.
Triggered when an invoice is updated.
Triggered when an invoice payment fails.
Refund Events
Triggered when a refund is pending.
Triggered when a refund is being processed.
Triggered when a refund has been processed.
Triggered when a refund fails.
Other Events
dedicatedaccount.assign.success
Triggered when a dedicated virtual account is successfully assigned to a customer.
dedicatedaccount.assign.failed
Triggered when dedicated virtual account assignment fails.
customeridentification.success
Triggered when customer identification is successful.
customeridentification.failed
Triggered when customer identification fails.
Triggered when a payment request is pending.
Triggered when a payment request is successful.
Security
Signature Verification
The SDK automatically verifies webhook signatures using HMAC SHA-512. This ensures that webhooks are genuinely from Paystack and haven’t been tampered with.
// The process method handles verification automatically
await paystack . webhook . process ( rawBody , signature );
// If this doesn't throw, the signature is valid
Best Practices
Always use the raw request body
The signature is computed against the raw request body. Parse the body to JSON only after verification fails. // ✅ Correct
const rawBody = await req . text ();
await paystack . webhook . process ( rawBody , signature );
// ❌ Wrong
const body = await req . json ();
await paystack . webhook . process ( JSON . stringify ( body ), signature );
Use environment variables for secrets
Never hardcode your Paystack secret key. const paystack = new Paystack ( process . env . PAYSTACK_SECRET_KEY ! );
Paystack may send the same webhook multiple times. Use the event’s reference/ID to prevent duplicate processing. paystack . webhook . on ( "charge.success" , async ( data ) => {
const { reference } = data ;
// Check if already processed
const exists = await db . transaction . findUnique ({ where: { reference } });
if ( exists ) return ;
// Process the payment
await db . transaction . create ({ data: { reference , /* ... */ } });
});
Process webhooks quickly or queue them for background processing. Paystack expects a 200 response within a reasonable time. app . post ( '/webhook' , async ( req , res ) => {
try {
await paystack . webhook . process ( rawBody , signature );
res . status ( 200 ). json ({ received: true });
} catch ( error ) {
res . status ( 400 ). json ({ error: error . message });
}
});
Error Handling
Handle webhook processing errors gracefully:
try {
await paystack . webhook . process ( rawBody , signature );
console . log ( 'Webhook processed successfully' );
} catch ( error ) {
if ( error . message . includes ( 'signature' )) {
console . error ( 'Invalid webhook signature - possible security issue' );
} else if ( error . message . includes ( 'parse' )) {
console . error ( 'Webhook payload validation failed' );
} else {
console . error ( 'Unexpected error:' , error );
}
// Still return 200 to Paystack to avoid retries for unrecoverable errors
}
Testing Webhooks
Using Paystack Test Mode
Use your test secret key to initialize the SDK
Make a test transaction on your staging/test environment
Paystack will send webhooks to your configured endpoint
Local Testing with Ngrok
# Install ngrok
npm install -g ngrok
# Start your local server
npm run dev
# Expose it to the internet
ngrok http 3000
# Use the ngrok URL in Paystack dashboard
# Example: https://abc123.ngrok.io/webhooks/paystack
Manual Testing
You can manually trigger webhooks using the Paystack API or dashboard for testing.
Paystack Webhook Docs Official Paystack webhook documentation
Webhook Events Complete list of webhook events and their payloads
Security Learn more about webhook security
GitHub Examples See complete webhook integration examples