Webhooks
With webhooks your system can subscribe to updates from Walley. By registrating one or many URLs at Walley, we can call it when we need to update you with new information.
Use Webhooksβ
Currently the webhook functionality is in preview mode and may change before general availability
Step 1. Expose an HTTPS endpointβ
To receive webhooks you are required to have an HTTPS endpoint.
Endpoint requirements
- Must listen to HTTP POST method
- Must be able to receive JSON body
- Must support HTTPS with TSL 1.2 or 1.3
Endpoint recommendations
- Basic authentication with username or password
- Support for HTTP2
- Validating the Walley-Signature header with HMAC key
Step 2. Accept webhooksβ
When you receive a webhook request you are required to respond with a http status code in the 2xx series.
Step 3. Configure webhooks in Walley Merchant Hubβ
To view webhook management in Merchant Hub, click here: Merchant Hub - Webhooks
Step 4. Test and enable for Productionβ
Not available right now.
Structureβ
You will receive a request body formated in JSON which will contain a type property with the name of the event, the timestamp when the event occured and a payload that can be diffrent for each event.
{
"type": string,
"timestamp": date,
"payload": object
}
Eventsβ
Events are grouped into these categories:
Retriesβ
Please note the webhooks will attempt to notify the configured webhook URI multiple times with increasing backoff time until a HTTP status code 2XX is received in the response. Our retry mechanism is built in a way that we guarantee at-least-once-delivery and the webhook URI endpoint must therefore support being called multiple times without side effects.
The webhook is sent instantly and if the endpoint does not answer with a 2XX status a retry will be made after:
- 5 seconds
- 10 seconds
- 3 minutes
- 1 hour
- 4 hours
- 8 hours
- 16 hours
- 24 hours
If the webhook endpoint did not answer with a 2XX status after the last retry (after about 80 hours), we will handle this manually and contact you before retrying again.
Headersβ
These headers will be included in the webhook requests
Key | Description |
---|---|
User-Agent | Standard http header the value would be something like, Walleybot 0.1 (+https://dev.walleypay.com) |
Walley-CorrelationId | The id of the request that triggered the webhook request, if you have issues with any request please provide this value in the support ticket. |
Walley-Timestamp | UNIX timestamp when this event occured, this is used for HMAC validation see below. |
Walley-Signature | Computed signature from Walley, this is used for HMAC validation see below. |
Walley-Live | True or False , this is used to know if the request to the webhook is made from Walleys production or a test triggered from the MerchantHub. True indicates production data. |
Verifying the request with HMACβ
When you set up a webhook, you'll get a HMAC key for it. You can use this key to check that requests you retrieve originates from Walley by using the follow method:
- Retrive the request header
Walley-Timestamp
- Concatenate the version number, the timestamp, and the body of the request to form a basestring. Use a semicolon as the delimiter between the three elements. The version number right now is always
v0
. Exampelv0:{Timestamp};{Body}
- Hash the string described above with the HMAC SHA256 implementation of you systems platform, using the HMAC secret for the webhook.
- Compare the computed signature with the request header
Walley-Signature
Examplesβ
- C#
- Node.js
- PHP
static string ComputeHash(string secret, string timestamp, string payload)
{
byte[] bytes = Encoding.UTF8.GetBytes(secret);
HMACSHA256 hmac = new HMACSHA256(bytes);
bytes = Encoding.UTF8.GetBytes($"v0;{timestamp};{payload}");
return Convert.ToHexString(hmac.ComputeHash(bytes)).ToLower();
}
static bool IsHashValid(string secret, string timestamp, string payload, string verify)
{
string hash = ComputeHash(secret, timestamp, payload);
ReadOnlySpan<byte> hashBytes = Convert.FromHexString(hash);
ReadOnlySpan<byte> verifyBytes = Convert.FromHexString(verify);
return CryptographicOperations.FixedTimeEquals(hashBytes, verifyBytes);
}
const computeHash = (secret, timestamp, payload) => {
const hmac = crypto.createHmac('sha256', secret);
hmac.write(`v0;${timestamp};${payload}`);
hmac.end();
return hmac.read().toString('hex');
};
const isHashValid = (secret, timestamp, payload, verify) => {
return crypto.timingSafeEqual(Buffer.from(verify, 'hex'), Buffer.from(computeHash(secret, timestamp, payload), 'hex'));
};
function computeHash($secret, $timestamp, $payload) {
$bytes = utf8_encode($secret);
$hmac = hash_hmac('sha256', "v0;$timestamp;$payload", $bytes, true);
return bin2hex($hmac);
}
function isHashValid($secret, $timestamp, $payload, $verify) {
$hash = computeHash($secret, $timestamp, $payload);
$hashBytes = hex2bin($hash);
$verifyBytes = hex2bin($verify);
return hash_equals($hashBytes, $verifyBytes);
}