Webhooks
Webhooks are used to notify your platform about events on finmid side. finmid sends these notifications to an endpoint hosted in your environment configured to receive and process them.
What is a webhook?
A webhook is an asynchronous notification in the shape of an HTTP request. Webhooks are a well known practice used by many services. We use webhooks to notify your platform asynchronously about events on finmid side (e.g. a buyer has been activated). This allows you to react to certain events and avoids the need for polling.
How to start receiving webhooks?
To start receiving webhooks, an endpoint has to be configured on both your and finmid ends. You need to provide a URL to which webhooks will be sent. This must be an HTTPS URL that is under your control or trusted by you. Additionally, we need certain security parameters from you to protect your endpoint. There are two ways to configure a webhook endpoint on finmid's end.
Option A: Setting up an endpoint via customer support
Please contact [email protected] and provide the following data points:
url
β URL of your webhook endpoint (only HTTPS URLs)basic_auth_username
β HTTP basic authentication username, for webhook authenticationbasic_auth_password
β HTTP basic authentication password, for webhook authenticationsignature_secret
β HMAC signature secret, for verifying the webhook payload
Option B: Setting up an endpoint via API
You can also use our API to create a new webhook endpoint. This requires an API key. Please contact our customer support if you don't have an API at hand.
curl --request POST \ --url https://api.finmid.com/api/v2/b2b-payments/webhook-endpoints \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --header 'X-API-KEY: your_apikey' \ --data ' { "url": "https://example.com/your-endpoint.php", "auth_username": "user123", "auth_password": "p455w0rd", "signature_secret": "s3cr3t" } '
You can configure multiple webhook endpoints: each of them will be receiving webhook notifications.
Protecting you webhook endpoint
Webhooks will be sent to your endpoint over the public internet. Therefore we only support HTTPS endpoint URLs. Custom self-signed certificates are not supported. We provide two important security features which will help you to protect your endpoint:
- Webhooks are using HTTP basic authentication
- The webhook payload is digitally signed using HMAC-SHA-256 algorithm
It is strongly recommended to utilize the above security features to protect your endpoint.
Webhook authentication
Webhooks utilize the HTTP basic authentication method. This method has very good support and is easy to implement while providing a sufficient level of protection.
This is just one example how easy the implementation is for an Express.js based app:
const app = require("express")();
const basicAuth = require("express-basic-auth");
app.use(
basicAuth({
users: { admin: "supersecret" },
})
);
Payload verification
All webhooks provide a X-Payload-Signature
header. It contains a HMAC-SHA-256
signature of the payload which was generated using the signature secret of the webhook endpoint. Please note that the header value is encoded as Base64.
You can check the signature like so:
- Read the signature from HTTP header
X-Payload-Signature
- Base64-decode the signature value (if necessary)
- Generate your signature from the webhook payload using
signature_secret
- Compare signature from webhook header with your generated signature. The webhook is verified if signatures match.
Pseudo code example of the above steps:
var signature = request.header("X-Payload-Signature");
var payload = request.body();
// Assume generatedSignature is a Base64-encoded signature value already
var generatedSignature = HMAC.createSha256(payload, env.SIGNATURE_SECRET);
if (signature === generatedSignature) {
// Webhook verified
}
Additional security tips
Your webhook endpoint already has quite good protection if you have implemented the above security features. However, you might consider the following additional tips:
- Choose a random, hard to guess webhook endpoint URL. This will make it hard for attackers to identify your webhook endpoint.
- Don't expose sensitive error information with your webhook endpoint. Make sure to log errors internally and only return
HTTP 404 Not Found
in case of an error. Even if an attacker identifies your webhook endpoint, they might get distracted by the not found error and stop their efforts.
Webhook Delivery Guarantees
In the event of an error when posting webhooks to your endpoint, finmid will attempt to deliver your webhooks for up to one day, with exponential backoff.
Webhook Payload Structure
This is a breakdown as to how finmidβs webhooks are structured, at a global level, and event-specific level.
General Webhook Payload
The webhook payload might contain multiple events. This allows us to reduce HTTP traffic.
Our webhooks all follow the same general payload structure, which can be found below.
Fields labelled as event specific are detailed in the next section.
{
"webhook_id": "e8cce989-0c48-4036-9bf2-1f0206398743",
"timestamp": "2022-02-10T13:48:48.892708722Z",
"events": [
{
"event_id": "ebda8288-8889-45e8-b672-0741e3553754",
"timestamp": "2022-02-10T13:42:35.282908722Z",
"type": // Event Specific,
"data": {
// Event Specific
}
},
{
"event_id": "4907a0f7-b53f-4fe7-90bd-bded64a55707",
// ...
}
]
}
Generic Fields
Field | Type | Description |
---|---|---|
webhook_id | string | |
timestamp | string | Corresponds to when the webhook was created. |
events | list of event objects |
Event Object Fields
Field | Type | Description |
---|---|---|
event_id | string | |
timestamp | string | Corresponds to when the event occured, and should be used as the source of truth to reconstruct event timelines. |
type | string | Type of the event |
data | object | Event-related data |
Types of Events & Payloads
This is a list of all the types of events we currently send. Do be aware that new events may be added.
Events are distinguished by the βtypeβ field in the payload, and additional data is provided in the βdataβ field.
Webhooks related to buyer / seller status change and payment request repayment can be tested via the simulation endpoints.
Buyer / Seller Status Changes
buyer.status_changed
This notification is sent when the status of a buyer has been asynchronously changed from one status to another.
The new_status field will correspond to the data returned when sending a GET request to our Get Buyer endpoint.
{
"event_id": "ebda8288-8889-45e8-b672-0741e3553754",
"timestamp": "2022-02-10T13:42:35.282908722Z",
"type": "buyer.status_changed",
"data": {
"buyer_id": "your-buyer-id",
"old_status": "PENDING",
"new_status": "ACTIVE"
}
}
Data Fields:
Field | Type | Description |
---|---|---|
buyer_id | string | ID of the buyer. |
old_status | string | Previous buyer status. Can be: PENDING, ACTIVE, INACTIVE, REJECTED, SUSPENDED. |
new_status | string | Current buyer status. Can be: PENDING, ACTIVE, INACTIVE, REJECTED, SUSPENDED. |
seller.status_changed
This notification is sent when the status of a seller has been asynchronously changed from one status to another.
The new_status field will correspond to the data returned when sending a GET request to our Get Seller endpoint.
{
"event_id": "ebda8288-8889-45e8-b672-0741e3553754",
"timestamp": "2022-02-10T13:42:35.282908722Z",
"type": "seller.status_changed",
"data": {
"seller_id": "your-seller-id",
"old_status": "PENDING",
"new_status": "ACTIVE"
}
}
Data Fields:
Field | Type | Description |
---|---|---|
seller_id | string | ID of the seller. |
old_status | string | Previous seller status. Can be: PENDING, ACTIVE, INACTIVE, REJECTED, SUSPENDED. |
new_status | string | Current seller status. Can be: PENDING, ACTIVE, INACTIVE, REJECTED, SUSPENDED. |
Payment Request - Repayment Events
payment_request.repayment.repaid
This notification is sent when a payment request has been fully repaid by the buyer, at which point the Payment Request can be considered as CLOSED.
{
"event_id": "ebda8288-8889-45e8-b672-0741e3553754",
"timestamp": "2022-02-10T13:42:35.282908722Z",
"type": "payment_request.repayment.repaid",
"data": {
"payment_request_id": "abc-abc"
}
}
Data Fields:
Field | Type | Description |
---|---|---|
payment_request_id | string | ID of the payment request |
payment_request.repayment.reminder
This webhook is sent whenever a Payment Request in PENDING_REPAYMENT status is either near, or past the repayment deadline. This will be sent regardless of whether the reminder email is sent to the buyer or not.
{
"event_id": "ebda8288-8889-45e8-b672-0741e3553754",
"timestamp": "2023-02-01T13:42:35.282908722Z",
"type": "payment_request.repayment.reminder",
"data": {
"payment_request_id": "abc-abc",
"due_date": "2023-02-04",
"days_from_due_date": "-3"
}
}
Data Fields:
Field | Type | Description |
---|---|---|
payment_request_id | string | ID of the payment request |
due_date | string | Payment request due date, in YYYY-MM-DD format. |
days_from_due_date | integer | Difference in calendar days from current date to repayment due date. |
Updated about 1 month ago