Webhook
Configure a webhook to receive notifications about orders.
MultiSafepay uses a webhook to send updates about orders and other notifications to your web server.
You can configure the webhook at website level or at order level.
How it works
The webhook is triggered when we have data to send you, or when the order status or transaction status changes, e.g. when:
- A customer completes payment
- A customer's payment is declined or fails
- A customer begins the payment process but does not finalize it
- An order has shipped
- A refund is processed
MultiSafepay uses HTTPS to send notifications securely to the webhook endpoint configured for your web server.
Our webhook uses the POST method to inform your web server when there is an update, and shares details on what has changed. This is more efficient than a poll-based GET method where your web server must continually check for updates. We do support GET as a notification_method, but we strongly recommend POST.
Note:International bank account numbers (IBANs) are sensitive data. For security reasons, we mask them by default in POST webhook notifications so that only the last 4 digits are visible, e.g. *** 1234.
To unmask them, see IBANs.
Prerequisites
You must set a webhook endpoint, which is a URL that:
- Doesn't include port numbers.
- Is publicly accessible, or has MultiSafepay on your allow list.
- Uses HTTPS - We don't accept HTTP for security reasons.
- Only contains a specific set of ASCII characters (alphanumeric, -, _, ., :, /).
For a list of MultiSafepay IP addresses, email [email protected]
Configure your webhook endpoint
You can configure the webhook endpoint at:
Sign in to your MultiSafepay dashboard .
For websites:
-
Go to Websites, and then click the relevant website.
-
Under Functionality > Webhook URL, set your webhook endpoint.
For terminal groups:
- Go to Devices > Terminals.
- Click Manage groups. Go to the relevant terminal group and click Edit.
- Enter your webhook URL. Click Save.
Note:If you configure webhook endpoints at both site and order level, the order-level
notification_urlis used by default. If a notification to the order-levelnotification_urlfails, we immediately send the notification to the site-level webhook URL configured in your MultiSafepay dashboard.If you configure only a site-level webhook URL, we recommend setting
payment_options.notification_methodtoPOSTwhen creating an order.
Example request
curl -X POST \
"https://api.multisafepay.com/v1/json/orders?api_key={your-api-key}"
-d '{
"type": "redirect",
"order_id": "my-order-id-1",
"currency": "EUR",
"amount": 1000,
"description": "product description",
"payment_options": {
"notification_url": "https://www.example.com/paymentnotification", // Can be omitted if set at site level
"notification_method": "POST" // Always include the POST method to receive status notifications
}
}'Example POST notification payload
{"amount":100,"amount_refunded":0,"completed":"2026-09-04T11:46:33","costs":[{"amount":0.6,"description":"2.9 % For Visa CreditCards Transactions (min 0.6)","transaction_id":13816765,"type":"SYSTEM"}],"created":"2026-09-04T11:46:33","currency":"EUR","custom_info":{"custom_1":null,"custom_2":null,"custom_3":null},"customer":{"address1":"Neherkade","address2":null,"city":"Gravenhage","cocnumber":null,"company_name":null,"country":"NL","country_name":"Netherlands","email":"[email protected]","first_name":"Testperson-nl","house_number":"XI","last_name":"Approved","locale":"nl_NL","phone1":"0612345678","phone2":null,"state":null,"zip_code":"2521VA"},"description":"Test Order Description","fastcheckout":"NO","financial_status":"completed","items":null,"modified":"2026-09-04T11:46:33","order_id":"apitool_144088240","payment_details":{"account_holder_name":"sada","account_id":null,"acquirer_reference_number":"24347496247000000326103","authorization_code":"007615","card_acceptor_id":"1001001","card_acceptor_location":"Brusell","card_acceptor_name":"Test; winkel Demoshop; me","card_additional_response_data":{"address_verification_result":"N","cavv_results_code":"2"},"card_authentication_details":{"flow":"non-3ds"},"card_authentication_result":{"chargeback_liability":"UNKNOWN"},"card_entry_mode":"UNKNOWN","card_expiry_date":"4210","card_funding":"C","card_product":"MCS","card_product_type":1,"card_program":"MCC","card_verification_result":"M","external_transaction_id":"624709958958","issuer_bin":"411111","issuer_country_code":"US","last4":"1111","mcc":"5995","payment_account_reference":"V0010013822055663018841627710","recurring_flow":null,"recurring_id":"998156387459303072","recurring_model":null,"response_code":"00","scheme_reference_id":"0326090411463493","type":"VISA"},"payment_methods":[{"account_holder_name":"sada","amount":100,"card_expiry_date":"4210","currency":"EUR","description":"Test Order Description","external_transaction_id":"624709958958","payment_description":"Visa","status":"completed","type":"VISA"}],"reason":"Approval and completed successfully","reason_code":"1000","related_transactions":null,"status":"completed","transaction_id":1788515193100858,"var1":null,"var2":null,"var3":null}
Success!You have configured your webhook endpoint Now you need to configure your web server to handle notifications correctly.
Handle notifications
When there is an update to your order, we notify your web server at the following URL via a POST request:{your-webhook-endpoint}?transactionid=12345×tamp=140292929
This URL is your webhook endpoint combined with two additional parameters:
transactionid: Your unique identifier for the order, previously set asorder_idin your API request.timestamp: The time the notification was triggered.
The updated order details make up the payload of the request.
1. Check the status
Check the order status in the status field. If necessary, update your backend.
Note:You can ignore notifications that:
- Don't have the
timestampparameter in the URL- Have the same order status
When using webhook notifications on POS devices, you might encounter soft declines when processing payments. For more information, see Soft declines .
Pre-transactions
If a customer initiates a payment process but does not finalize it, and no PSP ID (transaction reference number) is associated with the payment session, the corresponding order status will be updated to Canceled in your backend.
2. Validate the request
Every POST notification request includes an HMAC signature that you must use to validate its authenticity. To validate the request, you can either:
- Use the notification function from our PHP SDK. View on GitHub , or
- Create your own solution to validate HMAC signatures.
Own solution
To validate the HMAC signature of a POST notification request:
-
Base64 decode the
Authheader value.Encoded
Authheader:MTY0MTIxODg4NDowNmNiZjIyNmU3Yzg3M2VmZjk2OTIxZDdmZGUzOTk4ZWI2YmUwZGU3OTE1ZWUxYzFiNTE0OTUxMWZjYTgyZTI2YmIwYWIyZTZkMGUwYWQ5OTdjYmFiMTUxZTRiYTU2MTU0MThkOGUxMjUyODMwMTcyNjE0M2VkMTE0NjI4N2Y5Mw==Decoded value:
1641218884:06cbf226e7c873eff96921d7fde3998eb6be0de7915ee1c1b5149511fca82e26bb0ab2e6d0e0ad997cbab151e4ba5615418d8e12528301726143ed1146287f93 -
Split the decoded value at the colon (
:).This gives you:
- Timestamp:
1641218884 - HMAC signature:
06cbf226e7c873eff96921d7fde3998eb6be0de7915ee1c1b5149511fca82e26bb0ab2e6d0e0ad997cbab151e4ba5615418d8e12528301726143ed1146287f93
- Timestamp:
-
Create the value to hash by concatenating the timestamp, a colon (
:), and the exact request payload:{timestamp}:{request_payload}For example:
1641218884:{"amount":1000,"amount_refunded":0,"costs":[{"amount":0.49,"description":"0.49 For iDEAL Transactions","transaction_id":"123456789","type":"SYSTEM"}],"created":"2022-01-03T15:08:02","currency":"EUR","custom_info":{"custom_1":null,"custom_2":null,"custom_3":null},"customer":{"address1":null,"address2":null,"city":null,"country":null,"country_name":null,"email":"","first_name":null,"house_number":null,"last_name":null,"locale":"en_US","phone1":null,"phone2":"","state":null,"zip_code":null},"description":"product description","fastcheckout":"NO","financial_status":"initialized","items":null,"modified":"2022-01-03T15:08:02","order_id":"my-order-id", "payment_details":{"account_holder_name":null,"account_iban":"https://example.com","account_id":null,"external_transaction_id":"123456789","issuer_id":"3151","recurring_flow":null,"recurring_id":null,"recurring_model":null,"type":"IDEAL"},"payment_methods":[{"amount":1000,"currency":"EUR","description":"product description","external_transaction_id":"123456789","payment_description":"iDEAL","status":"initialized","type":"IDEAL"}],"reason":"","reason_code":"","related_transactions":null,"status":"initialized","transaction_id":"123456789","var1":null,"var2":null,"var3":null}Use the exact, unmodified request payload when calculating the HMAC signature.
-
Generate an HMAC SHA-512 hash of the value from step 3, using your API key as the HMAC key.
-
Compare the generated hash with the HMAC signature from step 2. If they match, the HMAC signature is valid.
Additionally, check whether the timestamp is recent and whether the request originates from a MultiSafepay IP address. For a list of MultiSafepay IP addresses, email [email protected].
Code example
The following example demonstrate how to validate the HMAC signature with Python.
Python HMAC signature validation
#!/usr/bin/python
import argparse
import base64
import hashlib
import hmac
import sys
# Parse the command-line arguments
parser = argparse.ArgumentParser()
parser.add_argument("-k", "--apikey", help="API key", required=True)
parser.add_argument("-p", "--payload", help="Payload", required=True)
parser.add_argument("-a", "--authheader", help="Auth header", required=True)
args = parser.parse_args()
# Step 1: Base64 decode the Auth header
decoded_auth = base64.b64decode(args.authheader).decode("ascii")
# Step 2: Extract the timestamp and HMAC signature
timestamp, signature = decoded_auth.split(":", 1)
# Step 3: Concatenate the timestamp, colon, and payload
concatenated_string = timestamp + ":" + args.payload
# Step 4: Generate the HMAC SHA-512 hash
hashed_value = hmac.new(
args.apikey.encode(),
concatenated_string.encode(),
hashlib.sha512
).hexdigest()
# Step 5: Compare the generated hash with the signature
if hmac.compare_digest(hashed_value, signature):
print("The notification is authentic")
sys.exit(0)
else:
print("Error: The notification is not authentic")
sys.exit(1)3. Acknowledge the notification
Acknowledge that you have successfully received a valid notification by returning an HTTP status code 200 (OK) response, where the message body meets at least one of the following conditions:
- The response body starts with, or ends with
OK. - The response body contains
MULTISAFEPAY_OK,>OK<, or"OK".
Example response
HTTP/1.1 200 OK
Content-Type: text/plain
OK Until we receive your acknowledgment, we resend the notification 3 times at 15 minute intervals, each with a new timestamp.
4. Resend failed notifications
If a notification fails or we don't receive your acknowledgment, we resend the notification 3 times at 15 minute intervals, each with a new timestamp.
If for some reason you don't receive the notification, you can retry it manually in your dashboard. To do this:
- Sign in to your MultiSafepay dashboard .
- Go to Transactions > Transaction overview, and then click the relevant transaction.
- On the Transaction details page, under Notification history, click
and check that the URL displayed is the correct webhook endpoint. - If the webhook endpoint is correct, to resend the notification, click the Resend icon.
If you still don't receive a notification, you may need to authorize MultiSafepay servers' IP addresses on your web server.
For a list of MultiSafepay IP addresses, email [email protected]
Success!You have configured your webhook endpoint and set up your web server to handle notifications.
Notifications overview
To see a filterable overview of all notifications you've received:
- Sign in to your MultiSafepay dashboard .
- Go to Transactions > Notifications.
High-volume transaction processing
In some situations, webhook notifications may be delayed temporarily. As a preventive measure, we recommend retrieving the order status directly by creating a Get order request if the final status has not been confirmed within the expected timeframe.
Webhook notifications should remain the primary mechanism for receiving payment status updates. Layer 1 should be fully implemented and thoroughly tested before introducing fallback status checks.
To handle delayed notifications, implement the following layers:
- Layer 1 (Primary)
Rely on webhook notifications as specified. Once the order is created, MultiSafepay sends POST notifications to your webhook endpoint whenever the order status changes. Ensure your webhook implementation is correctly configured and tested before proceeding with fallback mechanisms.
The webhook remains the primary real-time confirmation path.
For SmartPOS notifications, see event notifications for cloud mode.
- Layer 2 (Fallback)
If the webhook has not confirmed the transaction status after the customer successfully returns to your website, send a Get order request to retrieve the latest status.
- Layer 3 (Fallback)
If the status remains unresolved, perform a delayed backend status check using another Get order request.
A common approach is to send a Get order request when the customer returns successfully to the redirect_url.
How to set up your backend
- Send a Get order request if the notification hasn't been received within the expected timeframe. Use the original transaction's
order_id.
GET https://api.multisafepay.com/v1/json/orders/{order_id}?api_key={your-api-key}- In the response, look for the transaction status:
{
"success": true,
"data": {
"amount": 1000,
"amount_refunded": 0,
"completed": "2026-05-26T16:54:08",
...
"status": "completed", // Final order status
"transaction_id": "example-transaction-id",
...
}
}-
Use the returned
statusto update your backend when the customer returns to theredirect_url. -
If the webhook notification and the initial status check don't resolve the payment status, your backend can perform another delayed status check as a final fallback.
Make sure that your integration handles order updates correctly to prevent duplicate processing:
- Don't process the same order multiple times.
- Don't perform post-payment actions multiple times if the same payment update is received again.
- Don't send duplicate confirmation emails.
- Don't reduce stock multiple times.
Support
Email [email protected]
Updated 12 days ago