Explanation
Webhooks breakdown
Because of the asynchronous nature of the direct debit rails, a lot of events happen after Palomma has sent back an HTTP response to any of the above endpoints. Therefore, a Payment Request might initially come back as "pending"
and change to "approved"
some time after. Although a polling strategy where a merchant continuously polls the GET /paymentRequests/{paymentRequestId}
for all payment request objects that are pending would work, Palomma also sends important updates (known as webhooks) as POST requests to a URL of the merchant’s choice.
Technical Overview
The endpoint the merchant selects must be configured to receive HTTPS POST requests with a JSON body (HTTP is acceptable for the sandbox environment), and must reply with a 200 status within 5 seconds to be marked as successfully delivered by Palomma. In case of an unsuccessful delivery (where Palomma doesn’t receive a 200 response back), Palomma might try to redeliver the request (though retries aren’t guaranteed).
Authentication
Every webhook Palomma sends is signed to verify the integrity of the data being sent in the webhook. This signature is generated by computing an HMAC-SHA-256 of the encoded body of the POST request with an integrityKey
that is assigned to a merchant when they enable webhooks. We strongly recommend verifying the signature to make sure it was sent by Palomma before trusting the content of the webhook.
The recommended flow is the following:
- Retrieve the request headers
X-Encoded-Data
andX-Signature
. X-Signature
is an HMAC with the SHA256 hash function ofX-Encoded-Data
. Compute an HMAC-SHA-256 ofX-Encoded-Data
with theintegrityKey
assigned to you, and compare it toX-Signature
. If the computed signature andX-Signature
are not equal, the signature is invalid.X-Encoded-Data
is the base64 encoding of the webhook’s payload (the body of the POST request). Decode it, parse it into a JSON object, and verify that the resulting JSON object is equal to the body of the webhook request. If this check fails, the signature is invalid.
If the above checks passed, you can be confident that the data was sent by Palomma and that it wasn’t tampered with, as long as the integrityKey
hasn’t been compromised, since only Palomma and the merchant have access to the integrityKey
and it’s implausible to generate the HMAC-SHA-256 of a message, secret key pair without the secret key. You should only proceed to processing the request if the signature is deemed valid. Otherwise, return an error and ignore the request.
Example
Here’s an example of the steps described above using Node.js.
Duplicate Requests
In cases where a request is retried, the same webhookId and timestamp will be sent. We recommend that the handling of requests be idempotent, so that if you see a webhookId
again after having successfully processed it, you ignore subsequent identical requests. You might want to cache requests processed webhookId
s for a few days (eg. 2 days). As a request comes in, we recommend that you:
- Verify that the
timestamp
is no older than 2 days. If it is, ignore the request. - Verify that you have not processed the
webhookId
in the past two days. If you have, ignore the request.
Request Structure
Headers
Property | Description |
---|---|
X-Encoded-Data | Base64 encoding of the serialized webhook’s payload. Since the webhook is sent as an HTTP POST request, the payload is represented at the body of the request. In verifying that the request’s signature is valid, you want to first verify that X-Encoded-Data does in fact equal the webhook’s payload when decoded and parsed. |
X-Signature | Signature of the webhook’s payload used to verify the data’s integrity. Generated by computing an HMAC with the SHA256 hash function of X-Encoded-Data with the integrityKey for the merchant. To verify that the request payload was written by someone with access to the integrityKey, you should compute an HMAC-SHA-256 of X-Encoded-Data with the integrityKey and verify that the computed HMAC equals X-Signature. |
Body
Property | Type | Description |
---|---|---|
webhookId | string (UUID) | Unique ID for webhook notification. Should only be repeated if the notification fails to deliver, in which case more attempts to deliver the notification might be made. |
timestamp | string | ISO string for when the webhook notification was created. |
eventType | string | Either ‘payment-request.update’ or ‘payment-method.update’. This specifies which event is being updated, in relation to the endpoint where it was created (POST /paymentMethod, POST /paymentRequest). |
paymentMethod | Payment Method | Payment Method as it looks after the update that triggered the webhook notification, either one of payment method or payment request is present in the body. |
paymentRequest | Payment Request | Payment Request as it looks after the update that triggered the webhook notification, either one of payment method or payment request is present in the body. |