Webhooks
Webhooks call a certain website that you register within Áskell when certain events happen. Events that can be registered before:
State identifier |
Description |
---|---|
|
All events that are connected to subscriptions |
|
Subscription created |
|
Subscription changed |
|
Subscription renewed |
|
All events that are connected to customers |
|
Customer created |
|
Customer changed |
|
All events that are connected to individual payments |
|
Individual payment created |
|
Individual payment changed (f.ex. transaction is successful or non successful) |
|
Retry pending for an individual payment |
Identification
We recommend that all webhooks are authenticated. We sign each API call with a header named Hook-HMAC
. The value is an HMAC hashed string based on the content of the web call and a secret value known only to you and us. You get this secret value when you create a new webhook. The event type is also specified in the header Hook-Event
.
Here is a Python example. The process is very similar to what Shopify uses and you can find more code examples in their documentation here.
import base64
import hmac
import hashlib
WEBHOOK_SECRET = "Your webhook secret".encode()
WEBHOOK_DIGEST_TYPE = 'sha512'
def verify(hmac_header, digest_method, secret, message):
digestmod = getattr(hashlib, digest_method)
signed = base64.b64encode(
hmac.new(secret, message, digestmod).digest(),
).strip()
return hmac.compare_digest(signed, hmac_header)
# your view function
def handle_webhook(request):
# The signature
digest = request.META.get('HTTP_HOOK_HMAC').encode()
# The name of the webhook event
event = request.META.get('HTTP_HOOK_EVENT').encode()
body = request.body
if verify(digest, WEBHOOK_DIGEST_TYPE, WEBHOOK_SECRET, body):
payload = json.loads(body)
# ... the rest of your code here```
require 'rubygems'
require 'base64'
require 'openssl'
require 'sinatra'
WEBHOOK_SECRET = 'Your webhook secret'
WEBHOOK_DIGEST_TYPE = 'sha512'
helpers do
def verify_webhook(secret, data, hmac_header)
digested = ::OpenSSL::HMAC.digest(WEBHOOK_DIGEST_TYPE, secret, data)
calculated_hmac = Base64.encode64(digested).strip
return calculated_hmac == hmac_header
end
end
post '/' do
request.body.rewind
data = request.body.read
if verify_webhook(WEBHOOK_SECRET, env["HTTP_HOOK_HMAC"])
# deserialize data' using json and process webhook
end
end
<?php
define('WEBHOOK_SECRET', 'Your webhook secret')
define('WEBHOOK_DIGEST_TYPE', 'sha512')
function verify_webhook($data, $hmac_header)
{
$calculated_hmac = base64_encode(hash_hmac(WEBHOOK_DIGEST_TYPE, $data,
WEBHOOK_SECRET, true));
return ($hmac_header == $calculated_hmac);
}
$hmac_header = $_SERVER['HTTP_HOOK_HMAC'];
$data = file_get_contents('php://input');
$verified = verify_webhook($data, $hmac_header);
?>
Data in webhooks
Subscriptions (subscription.*)
When a subscription is created, updated or renewed the following events are sent. Here is an example of data that comes with a subscription event:
{
"id": 12345,
"plan": {
"id": 12345,
"name": "Titill",
"alternative_name": "Annar titill",
"reference": "",
"interval": "month",
"interval_count": 1,
"amount": "1.0000",
"currency": "ISK",
"trial_period_days": 5,
"enabled": true,
"private": false,
"electronic_only": true,
"description": "Some description"
},
"customer": 12345,
"customer_reference": "customer-12345",
"trial_end": "2024-01-01T00:00:00Z",
"start_date": "2024-01-01T00:00:00Z",
"ended_at": null,
"reference": "reference-12345",
"active": true,
"meta": "{}",
"description": "Some subscription description",
"active_until": "2024-02-01T00:00:00Z",
"is_on_trial": false,
"token": "md5sumtokenwith32chars",
"is_failing": false,
"last_billing_log": {
"id": 12345,
"billing_log_type": "renewal",
"billing_date": "2024-01-01T00:00:00Z",
"plan_billed_up_to": "2024-02-01T00:00:00Z",
"total": null,
"transaction": {
"external_reference": null,
"data": {},
"state": "settled",
"uuid": "some-guid-with-numbers-and-letters",
"fail_code": null,
"refund_code": null,
"cancel_code": null,
"amount": "1.00",
"currency": "ISK",
"payment_method": null,
"created_at": "2024-01-01T00:00:00Z"
}
},
"delivery_address": null,
"amount": "1.0000"
}
Customer (customer.*)
When a customer is created or updated the following events are sent. Here is an example of data that comes with a customer event:
{
"id": 33482,
"first_name": "",
"last_name": "",
"delivery_name": "",
"address_1": "",
"address_2": "",
"address_3": "",
"zip_code": "",
"city": "",
"country": "",
"email": null,
"customer_reference": "0208912039",
"payment_method": [],
"address": null
}
Individual payment (payment.*)
When an individual payment is created, changed or retired the following events are sent. Here is an example of data that comes with a individual payment event:
{
"uuid": "some-guid-with-numbers-and-letters",
"amount": "1.0000",
"currency": "ISK",
"description": "Description here",
"reference": "some-reference",
"state": "settled",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z",
"transactions": [
{
"external_reference": "410009982476",
"data": {
"id": "4100000000",
"receipt": {}, // receipt contains vendor specific fields, not listed here
"status": "settled"
},
"state": "settled",
"uuid": "some-guid-with-numbers-and-letters",
"fail_code": null,
"refund_code": null,
"cancel_code": null,
"amount": "1.00",
"currency": "ISK",
"payment_method": {
"verified": true,
"canceled": false,
"valid_until": "2025-03-01T00:00:00Z",
"display_info": "XXXX-XXXX-XXXX-4324 (Visa)",
"credit_card": true
},
"created_at": "2024-01-01T00:00:00Z"
}
]
}