LogoDOCS

Webhooks

ESSENTIAL

Webhooks 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 ObjectDescription
payment.successSent when a payment is successfully authorized and captured.
payment.failedSent when a payment attempt fails due to insufficient funds or user cancellation.
refund.processedSent 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.

HMAC-SHA256 Verification
  1. 1

    Get the raw request body (JSON string) and the X-PaynPlus-Signature header.

  2. 2

    Concatenate your Webhook Secret Key with the raw body.

  3. 3

    Generate an HMAC-SHA256 hash and compare it with the signature in the header.

Implementation Example

Node.js / Express
const crypto = require('crypto');

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.