Webhooks

Webhooks call a certain website that you register within Áskell when certain events happen. Events that can be registered before:

State identifier

Description

subscription.*

All events that are connected to subscriptions

subscription.created

Subscription created

subscription.changed

Subscription changed

subscription.renewed

Subscription renewed

customer.*

All events that are connected to customers

customer.created

Customer created

customer.changed

Customer changed

payment.*

All events that are connected to individual payments

payment.created

Individual payment created

payment.changed

Individual payment changed (f.ex. transaction is successful or non successful)

payment.retry

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.

  • python
  • ruby
  • php
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"
        }
    ]
}