Payment Links webhooks
Overview
Webhooks are HTTP callbacks that deliver notification messages for events. Peach Payments uses webhooks to inform merchant systems when certain events occur. This way, your servers are always up to date with transaction information.
After configuring and verifying a webhook, Peach Payments delivers POST notifications to the specified URL for the following events:
| Webhook event | Description |
|---|---|
initiated | Triggered when you create a payment link. |
opened | Triggered when a customer opens the payment link. |
processing | Triggered when a customer attempts to pay. |
completed | Triggered when a customer completes the payment. |
cancelled | Triggered when you cancel the payment link. |
expired | Triggered when the payment link expires. |
- Peach Payments cannot guarantee the order of webhooks. For example, if your customer initiates a transaction and Peach Payments sends a pending webhook but you have a system issue that causes the webhook to go into exponential backoff, Peach Payments might send the next webhook before the pending webhook gets retried.
- Payment Links uses Checkout to accept payments. Peach Payments therefore sends you webhooks for Payment Links and Checkout updates. To only receive Payment Links webhooks, contact support.
- Payment Links sends all webhooks from a known range of IP addresses.
For bulk payment links, Peach Payments sends webhooks for the underlying payment links and for the following batch events:
| Webhook event | Description |
|---|---|
initiated | Triggered when you create the batch. |
processing | Triggered when processing starts on the bulk file. |
completed | Triggered when Payment Links completes processing of the bulk file. |
error | Triggered if Payment Links cannot process the bulk file. |
expired | Triggered if you do not upload a file in three hours. |
Flow
You can configure webhooks in two ways:
- Add a webhook URL in the Peach Payments Dashboard.
- Enter a webhook URL in the
notificationUrlparameter of theoptionsobject of the generate link request or in thenotificationUrlparameter of the generate bulk link request. ThenotificationUrlwebhook overrides the webhook configured in the Dashboard.
Predefined events, as described above, trigger the webhook.
Webhook flow.
- The merchant receives a webhook with a result code indicating the updated checkout status.
- The merchant returns a 200 status code response acknowledging the webhook.
The merchant can then trigger a query to pull transaction or batch details and statuses.
Webhook security
Peach Payments does not currently sign bulk payment link webhooks.
You can secure your webhook endpoints using HMAC SHA256 signing. When you enable webhook signing, Peach Payments signs each webhook payload so that you can verify that a request genuinely comes from Peach Payments and that no one has tampered with it in transit.
Enable webhook signing
Only certain roles can enable webhook signing.
You can enable webhook signing from the Peach Payments Dashboard:
- Log in to your Peach Payments Dashboard and configure a webhook.
- Under Webhook security, click the Enable webhook signing toggle to the on position.
- In the Enable webhook signing window, copy the shared secret key that Peach Payments uses to sign your webhooks, then click Enable.
You can view and copy the secret key again at any time under Webhook security. You need this key to verify webhook signatures.
After you enable webhook signing, you can:
- Disable webhook signing. Click the Enable webhook signing toggle to the off position, then click Disable. Peach Payments stops signing your webhook payloads.
- Regenerate the secret key. Under Webhook security, click Regenerate secret key. Update your integration with the new key, because Peach Payments signs webhooks with the new key immediately.
If you disable and later re-enable signing without regenerating the key, Peach Payments uses your existing configured secret key.
Verify webhook signatures
After you enable webhook signing, each webhook request includes x-webhook-signature-algorithm, x-webhook-timestamp, x-webhook-id, and x-webhook-signature headers. You must verify the authenticity of the request by recalculating the signature using your secret key and comparing it to the value that Peach Payments sends.
When you recalculate the signature:
- Use the raw request body exactly as you receive it. Checkout sends the body as form-urlencoded data, whereas Payment Links sends it as JSON.
- Ensure that the URL matches the webhook URL that you configured on the Peach Payments Dashboard.
Peach Payments includes the webhook ID in the signature message to:
- Prevent replay attacks by tying the signature to a specific webhook instance.
- Enable correlation between merchant logs and Peach Payments logs for troubleshooting.
- Allow merchants to verify the integrity of the webhook ID for idempotency checking.
The following code snippet shows how to verify the HMAC signature in Node (remember to replace placeholder values with your own values):
const crypto = require('crypto');
const timestamp = get('request.header.x-webhook-timestamp');
const webhookId = get('request.header.x-webhook-id');
const receivedSignature = get('request.header.x-webhook-signature');
const url = get('request.url'); // Must match your configured webhook URL
const payload = get('request.content'); // Raw body: form-urlencoded for Checkout, JSON for Payment Links
const secret = 'some-secret'; // Must match Peach Payments secret
const message = `${timestamp}.${webhookId}.${url}.${payload}`
const calculatedSignature = crypto
.createHmac('sha256', secret)
.update(message)
.digest('hex');
const isValid = calculatedSignature === receivedSignature;
if (isValid) {
console.log('✅ HMAC Signature Valid');
console.log(`Timestamp: ${timestamp} (${new Date(timestamp * 1000).toISOString()})`);
console.log(`Webhook ID: ${webhookId}`);
console.log(`Signature: ${calculatedSignature.substring(0, 16)}...${calculatedSignature.substring(48)}`);
} else {
console.log('❌ HMAC Signature Invalid');
console.log(`Expected: ${calculatedSignature}`);
console.log(`Received: ${receivedSignature}`);
console.log(`Message: ${message.substring(0, 100)}...`);
}
set('hmac_valid', isValid);
set('hmac_signature', calculatedSignature);Webhook retry mechanism
Peach Payments expects a 200 HTTP for successful webhook delivery and a non-200 HTTP status code for failures.
For an unsuccessful response, Peach Payments retries the webhook for 30 days or until a successful acknowledgement (200 HTTP) occurs.
Exponential backoff retry
Below is the exponential backoff logic.
The interval between retries is:
- 2 minutes
- 4 minutes
- 8 minutes
- 15 minutes
- 30 minutes
- 1 hour, every day until 30 days have passed since the first attempt
Webhook events
Peach Payments sends the following webhooks for payment links.
| Event | Parameter | Type | Example |
|---|---|---|---|
| Initiated | paymentId | String | 00d886d6-4754-4bcc-b88f-74a53d5220e5 |
status | String | initiated | |
url | String (URL) | https://l.ppay.io/4c1a48e6dc3b7ebd | |
| Opened | paymentId | String | 00d886d6-4754-4bcc-b88f-74a53d5220e5 |
status | String | opened | |
| Processing | paymentId | String | 00d886d6-4754-4bcc-b88f-74a53d5220e5 |
status | String | processing | |
| Completed | paymentId | String | 00d886d6-4754-4bcc-b88f-74a53d5220e5 |
status | String | completed | |
paymentBrand | String enum [VISA, MASTER, DINERS, AMEX, MASTERPASS, MOBICRED, MPESA, 1FORYOU, APLUS, PAYPAL, ZEROPAY, PAYFLEX, BLINKBYEMTEL, CAPITECPAY, MCBJUICE, PEACHEFT] | VISA | |
registrationId* | String (optional) | 8ac7a4a188e7cf090188f7112dbe389a | |
| Cancelled | paymentId | String | 00d886d6-4754-4bcc-b88f-74a53d5220e5 |
status | String | cancelled | |
| Expired | paymentId | String | 00d886d6-4754-4bcc-b88f-74a53d5220e5 |
status | String | expired |
* Card registration token, if tokeniseCard was true on the initial request and paymentBrand is a card type.
{
"status": "initiated",
"url": "https://l.ppay.io/4c1a48e6dc3b7ebd",
"paymentId": "00d886d6-4754-4bcc-b88f-74a53d5220e5"
}{
"status": "opened",
"paymentId": "00d886d6-4754-4bcc-b88f-74a53d5220e5"
}{
"status": "processing",
"paymentId": "00d886d6-4754-4bcc-b88f-74a53d5220e5"
}{
"status": "completed",
"paymentId": "00d886d6-4754-4bcc-b88f-74a53d5220e5",
"paymentBrand": "VISA",
"registrationId": "8ac7a4a188e7cf090188f7112dbe389a",
}{
"status": "cancelled",
"paymentId": "00d886d6-4754-4bcc-b88f-74a53d5220e5"
}{
"status": "expired",
"paymentId": "00d886d6-4754-4bcc-b88f-74a53d5220e5"
}Updated 2 days ago