Contents
Method of Communication
When a webhook is called, it sends a POST request including a few headers and a JSON-formatted body to the endpoint URL defined by the webhook subscription. The event name is added to the base URL. For example, if your base URL is https://example.com/webhooks and the event is startSession, the full URL that is called will be https://example.com/webhooks/startSession. Learn more about subscriptions here.
It then expects a response with a few headers and also a JSON formatted body. The HTTP response code should be 200 if the request was processed successfully and 400 when an error occurs. Learn more about the possible error codes here.
It is also possible that the response contains an error. MplusKASSA will then send a new request to the same endpoint URL, including the error that was detected and the original event details.
Communication Flow
Here is a schematic representation of the communication flow for a normal request and successful response.
MplusKASSA | Webhook URL | |
---|---|---|
(1) Event occurs | Send request | (2) Process request |
(4) Process response | Send response | (3) No problems found |
(5) No problems found |
Here is a schematic representation of the communication flow for a normal request, but error response.
MplusKASSA | Webhook URL | |
---|---|---|
(1) Event occurs | Send request | (2) Process request |
(4) Process error response | Send error response | (3) Problem found |
Here is a schematic representation of the communication flow for a normal request and a successful response, but with an error found in the response.
MplusKASSA | Webhook URL | |
---|---|---|
(1) Event occurs | Send request | (2) Process request |
(4) Process response | Send response | (3) No problems found |
(5) Problem found | Send error request | (6) Process error request |
(7) Maybe log error |
Examples of Request and Response
Let’s explain further by showing some examples.
Example of normal request
Take note of the URL, the required request headers and the JSON body. The exact contents of the payload are not important, they are explained in other places.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
POST <Endpoint URL>/startPayment X-Mplus-Subscription-Id: example_subscription X-Mplus-Signature: ... Content-Type: application/json Accept: application/json { "event": { "eventCounter": 1, "eventTimestamp": "2017-01-01T12:00:00+02:00", "eventBlocking": true }, "sender": { "branchNumber": 1, "workplaceNumber": 1 }, "session": { "sessionId": "c229e3d0-72d1-11e7-8cf7-a6006ad3dba0", "relation": { "relationNumber": 1, "name": "Example Customer" }, "lines": [ { "lineId": "3587e2b8-72d3-11e7-8cf7-a6006ad3dba0", "articleNumber": 1, "text": "Example Article" "priceIncl": 3.50, "discountPercentage": 10 } ] } } |
Example of successful response:
Take note of the HTTP response code, the required response headers and the JSON body. The exact contents are not important, they are explained in other places.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
HTTP/1.1 200 OK X-Mplus-Signature: ... Content-Type: application/json { "lineChanges": [ { "lineId": "3587e2b8-72d3-11e7-8cf7-a6006ad3dba0", "externalDiscount": { "discountId": "d06059a8-72d5-11e7-8cf7-a6006ad3dba0", "discountDescription": "Example voucher discount", "discountAmount": 2 } } ] } |
Example of error response:
Take note of the HTTP response code, the required response headers and the JSON body. The exact contents are not important, they are explained in other places.
1 2 3 4 5 6 7 8 9 10 11 |
HTTP/1.1 400 BAD REQUEST X-Mplus-Signature: ... Content-Type: application/json { "error": { "code": "INVALID_SIGNATURE", "message": "Invalid signature." } } |
Example of error request
Take note of the URL, it’s the same as the original request. Also take note of the additional
error element and the
original element containing the original event.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
POST <Endpoint URL>/startPayment X-Mplus-Subscription-Id: example_subscription X-Mplus-Signature: ... Content-Type: application/json Accept: application/json { "error": { "code": "LINE_ALREADY_HAS_DISCOUNT", "message": "Line '3587e2b8-72d3-11e7-8cf7-a6006ad3dba0' already has a discount." }, "original": { "event": { "eventCounter": 1, "eventTimestamp": "2017-01-01T12:00:00+02:00", "eventBlocking": true }, "sender": { "branchNumber": 1, "workplaceNumber": 1 }, "session": { "sessionId": "c229e3d0-72d1-11e7-8cf7-a6006ad3dba0", "relation": { "relationNumber": 1, "name": "Example Customer" }, "lines": [ { "lineId": "3587e2b8-72d3-11e7-8cf7-a6006ad3dba0", "articleNumber": 1, "text": "Example Article", "priceIncl": 3.50, "discountPercentage": 10 } ] } } } |
Security measures
All communication over SSL
To prevent snooping, all webhook endpoint urls must communicate over SSL. In simple terms, any URL that is not preceded by
https:// will not be supported.
Sign with HMAC
To provide a means of verification and protection against outside manipulation, a hash-based message authentication code (HMAC) is used. Hash your payload with the
HMAC-SHA256 algorithm and encode the result in
Base64. Add this value as a header with the name
X-Mplus-Signature to your response.
Please note that the secret key displayed in the examples below is just an example key. In a production environment an unique key will be configurable (or automatically generated) for your application. So do not hard-code the key into your application.
Example PHP code
1 2 3 4 5 6 7 8 |
<?php $algo = 'sha256'; $data = 'test'; $key = 'eFc5HrxwLbONJ+EYXrbHB+a9HueYIQzotgKRLRVAfx0='; $decoded_key = base64_decode($key); $hash = hash_hmac($algo, $data, $decoded_key, true); $encoded_hash = base64_encode($hash); |
Example to verify your algorithm
To test if your implementation works, generate a hash based on:
- The message: test
- The secret key, in Base64: eFc5HrxwLbONJ+EYXrbHB+a9HueYIQzotgKRLRVAfx0=
The result should be, in Base64:
✔
EBFFIb5qPH/teEFmjtwcIj6h80cl+X1DUy62D46tnu8=
Some common mistakes
If your result is:
✘
7h2xjbN4zhk37ZDr3iw1DTXOGmJA9uRl9MjfbFVLHw0=
You haven’t Base64 decoded the secret key.
✘
10114521be6a3c7fed7841668edc1c223ea1f34725f97d43532eb60f8ead9eef
You haven’t Base64 encoded the hashed result.
✘
a4a0eb9ac2940c9ed0783c3238cb547048bd68197e0539f3c582074aa8db7e4e
You haven’t Base64 decoded the secret key.
you haven’t Base64 encoded the hashed result.
Another example
Please ensure you have first verified that your algorithm works using the example above.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
{ "event": { "eventBlocking": true, "eventCounter": 45, "eventTimestamp": "2018-04-04T12:10:40.720+02:00" }, "scanCode": { "codeType": "BARCODE", "scannedCode": "YHDAA-tech" }, "sender": { "branchNumber": 1, "instanceId": "e86f09db-e140-4d99-92aa-33757b956a97", "workplaceNumber": 1 }, "session": { "sessionId": "167e876b-22b0-4531-86f6-72c9d23ba838", "totalInclAmount": 0 } } |
Secret:
EBFFIb5qPH/teEFmjtwcIj6h80cl+X1DUy62D46tnu8=
Hash:
7H0jJ1U0Quc+m4GBo9oAhWVOuXS5pKlkjGhxnUxu+0c=
If you have a Webhook Test Suite account, you can use this page to generate more examples: https://webhooks.mpluskassa.nl/hmac