Webhooks
ESSENTIALWebhooks allow your server to receive real-time notifications about payment events. When an event occurs—like a successful GCash payment—PaynPlus sends an HTTP POST request with a JSON payload to your configured endpoint.
Why are webhooks essential?
Client-side redirects (e.g., return_url) are not 100% reliable. A customer might close their browser or lose internet connection after a successful payment but before the redirect happens. Webhooks ensure your system stays in sync even if the user drops off.
Webhook Event Types
| Event Object | Description |
|---|---|
| payment.success | Sent when a payment is successfully authorized and captured. |
| payment.failed | Sent when a payment attempt fails due to insufficient funds or user cancellation. |
| refund.processed | Sent when a refund has been successfully completed. |
Security: Verifying Signatures
To ensure that a webhook request was actually sent by PaynPlus and not a malicious third party, every request includes a digital signature in the X-PaynPlus-Signature header.
- 1
Get the raw request body (JSON string) and the X-PaynPlus-Signature header.
- 2
Concatenate your Webhook Secret Key with the raw body.
- 3
Generate an HMAC-SHA256 hash and compare it with the signature in the header.
Implementation Example
app.post('/webhook', (req, res) => {
// 1. Get signature from header
const signature = req.headers['x-paynplus-signature'];
const secret = 'whsec_your_webhook_secret';
// 2. Compute HMAC-SHA256 hash
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(req.body))
.digest('hex');
if (signature === expectedSignature) {
// Signature valid! Process the order
console.log('Payment successful:', req.body.data.id);
res.sendStatus(200);
} else {
res.sendStatus(400);
}
});
Retry Policy
If your server does not return a 200 OK response, PaynPlus will attempt to redeliver the webhook for up to 24 hours with exponential backoff (e.g., after 5m, 15m, 1h, 4h, etc.) to ensure your data consistency.