Merchant Webhooks has just been released! Pulling event via API method stay another good alternative (plugin integration is using pulling for now) but using webhooks especially for merchants with custom API integration that want instantness of information could be the solution.
Nevertheless, webhooks method is more complex than pulling as it requires security skills such authorizing flows between our backends and your systems. Also, asynchronous flows could be more tricky to manage in case of network or system failure. so we recommend to well study your need before starting. The reading of this guide would help you in your integration.
How to subscribe to Merchant webhooks
There 2 ways to do it:
- through your merchant portal app
- though APIs
1-Subscribe through merchant portal app
Connect your merchant portal app and start the Webhooks configuration on the dedicated tab of your personal information page.
Add a new webhook endpoint
You can add a new webhook endpoint by clicking "start" or "add a new endpoint".
Configure your endpoint with security fields
Mandatory fields *
Endpoint URL *: name of URL endpoint that will be callback when a event subscribed occurs.
Authentication method *: choose among the authentication method available [None | Basic auth | API key]
Fields to complete according authentication method:
None
Basic auth
API key
Secret *
Identifier/login *
Secret (API key) *
Signature key
Password *
Signature key
Signature key
Identifier/login and password fields are required for basic auth only.
Secret is required for authentication None or API key. Secret is a string shared between you and scalexpert.
Signature key is optional field for signing events with header X-BAAS-SIGNATURE. This add more security if needed.
Configure your endpoint to listen events
Manage webhooks endpoints
Once webhooks added, you can manage your webhooks endpoints at the webhooks tab. Manage webhooks allow you to change the configuration, activate/deactivate events.
You can activate/deactivate webhooks at any time for all events subscribed or events by events.
Pay attention activation of weebhook is not immediate (only at first time) because we would need to declare your webhook url endpoint as authorized outbound flow (a delay of 6 open days would be necessary).
Also for security reasons, you may need to register IP address of our systems into your network configuration to authorized in-bounds flows. In case of, contact us to obtain range of IP adresses to configure.
Test your webhooks configuration
We have added a special event type "HELLO_WORLD" to allow you testing your webhooks url configuration.
Retrieve events
2-Subscribe through merchant-webhooks API
The logic is the same as described in above chapters.
At minimum one "evenTypeCode" is required in the body of the request. The following values are possible:
"ANY" if you want to listen all event types depending on your solutions subscription
{a dedicated "evenTypeCode"} to listen all events from a dedicated event type code
"HELLO_WORD" special value to test your configuration only
Complete your configuration with "url" and security attributes:
"url": enter your webhook url endpoint (required if configuration active)
Security attributes (required if configuration active):
"authMethod ": choose among the authentication method available [None | Basic auth | API key]
Fields to complete according authentication method:
Mandatory fields *
None
Basic auth
API key
secret *
authLogin *
secret (API key) *
keyForSignature
authPassword *
keyForSignature
keyForSignature
Field "authScope" is depreciated. Please do not use it.
Optional attributes:
"emailForAlerts": enter an email address to receive alerts when an event is not successfully delivered to your webhook
"activeEventCodes": list of "eventsCodes" you want to listen. by default all eventsCodes from the mentioned "eventTypeCode" are listened
Activate your configuration
You can update your configuration many times till attribute "activate": false is mentioned.
"active": true make your configuration active.
Pay attention activation of weebhook is not immediate (only at first time) because we would need to declare your webhook url endpoint as authorized outbound flow (a delay of 6 open days would be necessary).
Also for security reasons, you may need to register IP address of our systems into your network configuration to authorized in-bounds flows. In case of, contact us to obtain range of IP adresses to configure.
Example PUT /webhooks using webhook.site as receiver
{
"eventTypeCode": "HELLO_WORLD",
"active": true,
"url": "https://webhook.site/<put here your generated token>",
"authMethod": "NONE",
"secret": "mySuperSecretOrMyAPIKey",
"emailForAlerts": "<put here your generated token>@emailhook.site"
}
Test your configuration
You can Verify your configuration with API GET /webhooksTest your webhook endpoint with API POST /events/tests/_trigger to trigger an "HELLO_WORLD" event (see below example).
This will trigger an event "HELLO_WORLD" to the dedicated configuration. No parameter are required. Event received is factice and will be structured as normal event but with "HELLO_WORD" payload data.
Example "HELLO_WORLD" event
{
"timestamp": "2024-12-13T15:20:26.391Z",
"id": "03e14f55-845c-470e-bfec-eef18c76b111",
"correlationId": "675c50b9110d5a53694b00eec5d27f3f",
"eventTypeCode": "HELLO_WORLD",
"data": {
"eventTypeCode": "HELLO_WORLD",
"helloWorldMessage": "Hello World ! This event was generated at 2024-12-13T15:20:26.391Z"
},
"status": "OK",
"timestampOfLastDeliveryAttempt": "2024-12-13T15:20:26.620Z",
"httpStatusCodeOfLastDeliveryAttempt": 200
}
Consume webhooks events
Once your configuration is tested and activated you will received automatically events on your webhook url. Each event would be structured as:
Once processed by your server and response is 200 OK or 201 CREATED, event is considered as consumed and will not be resent.
The response body of your webhook MUST be empty.
If the response <> 200 OK and 201 CREATED, then event is considered as not consumed and a replay operation will occur according the replay mechanism (every 10 minutes during 5 days). Each replay operation will be counted.
Retrieve webhooks events
Status meaning:
OK: Event with status 200 OK or 201 CREATED
ERROR : event with status <> 200 OK and 201 CREATED
INACTIVE: event with configuration inactive
NO_CONFIG: no configuration found for this event
(KILLED): event killed by scalexpert support team for internal reason
Even if a configuration is inactive or isn't found, you can still retrieve events through API GET /events. This functionality enables you to pull events on demand, allowing for more flexible data management.
A list of events with their status will be returned:
In the webhook configuration, If you enter a "keyForSignature" value then for each event sent thse headers "X-BAAS-SIGNATURE" and "X-BAAS-SIGNATURE-TIMESTAMP" will be provided. "X-BAAS-SIGNATURE" header value will be computed using HMAC-SHA256 algorithm and combining the payload string of event and the "keyForSignature" value. This will ensure that payload of event are correct and not falsified by malevolent actor.
Thus, you would need to verify the header "X-BAAS-SIGNATURE" before parsing the event payload.
Header "X-BAAS-SIGNATURE-TIMESTAMP" is intended to check if event received is "freshly" sent and not a older one.
Java - sample of function to verify the X-BAAS-SIGNATURE (1/2)
**
* Example of a REST/JSON webhook endpoint with validation of the signature contained in the HTTP header X-BAAS-SIGNATURE.
*
* @param webhookEventAsString: Body of the http request as String. The body must be a JSON object with a structure compatible with the event format described in the developer portal.
* @param httpRequest: The HttpServletRequest automatically injected by Spring (used to get headers)
* @return the response must be empty, the only important point is that you must return the status code 200-OK or 201-CREATED to acknowledge that the event has been taken into account/processed
* (if the returned status code is NOT 200-OK or 201-CREATED, the event will be retriggered/redelivered in few minutes)
*/
@PostMapping(value = "/webhooks/smart-credit-subscription", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Void> snippet_webhookForSmartCreditSubscription(@RequestBody String webhookEventAsString, HttpServletRequest httpRequest) {
// Get the key to use to check the signature of the body of the http request. The key is defined by the merchant during the declaration of the webhook endpoint in the merchant portal.
String keyForSignature = "123456";
// Check if the signature of the request body (with keyForSignature) and the signature in the HTTP header X-BAAS-SIGNATURE match
boolean isSignatureValid = snippet_isSignatureValid(webhookEventAsString, keyForSignature, httpRequest);
if (isSignatureValid) {
// If the signature is valid...
// parse the webhookEventAsString to some POJO class WebhookEvent ...
// process the webhookEvent...
// return the status code 200-OK or 201-CREATED
return ResponseEntity.ok().build(); // The response body must be empty, only the returned HTTP status code is important
} else {
// If the signature is NOT valid...
// return the status code 400-BAD-REQUEST (could be also 403-FORBIDDEN...)
return ResponseEntity.badRequest().build(); // The response body must be empty, only the returned HTTP status code is important
}
}
Java - sample of function to verify the X-BAAS-SIGNATURE (2/2)
/**
* This operation is used to check that the received event (on a HTTP REST/JSON webhook endpoint) has been triggered
* by a trusted source (like BaaS/ScaleExpert) and has not been altered/modified by an intermediary component (man-in-the-middle attack).
* @param webhookEventForMerchantAsString: Body of the http request as String. The body must be a JSON object with a structure compatible with the event format described in the developer portal.
* @param keyForSignature: Key to use to check the signature of the body of the http request. The key is defined by the merchant during the declaration of the webhook endpoint in the merchant portal.
* @param httpRequest:ย ย ย ย The HttpServletRequest automatically injected by Spring (used to get headers)
* @return true if the signature of the body (with keyForSignature) and the signature in the HTTP header X-BAAS-SIGNATURE match.
*/
public boolean snippet_isSignatureValid(String webhookEventForMerchantAsString, String keyForSignature, HttpServletRequest httpRequest) {
ย ย ย String HTTP_HEADER_FOR_BAAS_SIGNATURE = "X-BAAS-SIGNATURE";
ย ย ย String HTTP_HEADER_FOR_BAAS_SIGNATURE_TIMESTAMP = "X-BAAS-SIGNATURE-TIMESTAMP";
ย ย ย // If there is no Signature header, then there is no signature to check, so we consider it is ok to continue
ย ย ย String valueOfSignatureHttpHeader = httpRequest.getHeader(HTTP_HEADER_FOR_BAAS_SIGNATURE);
ย ย ย if (valueOfSignatureHttpHeader == null || valueOfSignatureHttpHeader.isEmpty()) {
ย ย ย ย ย ย ย return true;
ย ย ย }
ย ย ย String timestampInHttpHeaderAsString = httpRequest.getHeader(HTTP_HEADER_FOR_BAAS_SIGNATURE_TIMESTAMP);
ย ย ย if (timestampInHttpHeaderAsString == null || timestampInHttpHeaderAsString.isEmpty()) {
ย ย ย ย ย ย ย throw new RuntimeException("Timestamp is missing in HTTP Header " + HTTP_HEADER_FOR_BAAS_SIGNATURE_TIMESTAMP);
ย ย ย }
ย ย ย String hmacInHtpHeaderAsString = httpRequest.getHeader(HTTP_HEADER_FOR_BAAS_SIGNATURE);ย ย ย // never null nor empty because of the test at the very beginning of this operation
ย ย ย // Check that the timestamp is close to now (less than 5 minutes)
ย ย ย boolean isTimestampCloseToNow;
ย ย ย DateTimeFormatter fmt = DateTimeFormatter.ISO_INSTANT.withZone(ZoneOffset.UTC);
ย ย ย Instant timestampInHttpHeader = null;
ย ย ย try {
ย ย ย ย ย ย ย timestampInHttpHeader = Instant.from(fmt.parse(timestampInHttpHeaderAsString));
ย ย ย } catch (Exception ex) {
ย ย ย ย ย ย ย String errMsg = "Error while parsing timestamp " + timestampInHttpHeaderAsString;
ย ย ย ย ย ย ย telemetry.error(errMsg, ex);
ย ย ย ย ย ย ย throw new RuntimeException(errMsg, ex);
ย ย ย }
ย ย ย isTimestampCloseToNow = Instant.now().minusSeconds(MAX_AGE_IN_SECONDS_FOR_SIGNATURE_TIMESTAMP).isBefore(timestampInHttpHeader);
ย ย ย // Check that the hmac is ok
ย ย ย String payloadToSign = timestampInHttpHeaderAsString + "." + webhookEventForMerchantAsString;
ย ย ย String hmacOfTheBody = snippet_hmacSHA256(payloadToSign, keyForSignature);
ย ย ย boolean areHmacEqual = hmacOfTheBody.equalsIgnoreCase(hmacInHtpHeaderAsString);
ย ย ย // The signature is ok if the hmac SHA 256 is ok and the timestamp is ok
ย ย ย return areHmacEqual && isTimestampCloseToNow;
}
Java - Utility operation to generate a hmac of some String using SHA256.
/**
* Utility operation to generate a hmac of some String using SHA256.
* @param clearText: String in clear text for which we want to calculate the hmac.
* @param key: Key to use to calculate the hmac.
* @return the hmac of the string using SHA256.
*/
private String snippet_hmacSHA256(String clearText, String key) {
ย ย ย try {
ย ย ย ย ย ย ย SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "HmacSHA256");
ย ย ย ย ย ย ย Mac mac = Mac.getInstance("HmacSHA256");
ย ย ย ย ย ย ย mac.init(secretKeySpec);
ย ย ย ย ย ย ย byte[] encryptedText = mac.doFinal(clearText.getBytes());
ย ย ย ย ย ย ย return Hex.encodeHexString(encryptedText);
ย ย ย } catch (Exception ex) {
ย ย ย ย ย ย ย String errMsg = String.format("Error while calculating hmacSHA256. Error was %s", ex.getMessage());
ย ย ย ย ย ย ย throw new RuntimeException(errMsg, ex);
ย ย ย }
}
When signature is used you would have to verify the signature before consuming event. See dedicated chapter "
Chose to listen on the endpoint webhook url. Events types listed are the ones available for you that fit your solutions subscribed.
You can listen multiple events types on the same endpoint. But we recommend subscribing separated endpoints because it will be easier to parse the payload according event type structure. see more details on event types .
For now this feature is only available through API. if you want us to release it on merchant portal let us know
You can retrieve all events received and get details though .
For now this feature is only available through API. if you want us to release it on merchant portal let us know
All the features described below and more are available though .
Configure your webhook endpoints url to listen events from one code with API PUT /webhooks
Pay attention to activate your configuration ONLY if you completed parameters "url" and "security fields" otherwise it will generate an error on production. We recommend strongly to with event "HELLO_WORLD" before activating.
"Data" structure is depending on related "eventTypeCode". see more details on .
At any moment, you can retrieve events sent with filtered by status [OK| ERROR|INACTIVE |NO_CONFIG] or a specific "eventTypeCode" or "eventCode" for a period.