Receiving payment notifications with webhooks

Listen for payment notifications across the payment's lifecycle to automatically trigger reactions.

Webhooks are used to listen to payment notifications across a payment's lifecycle.

How receiving payment notifications with webhooks works

1. Supported scenarios

ScenarioNotes
Pay with Setel code scan
(Not supported)
Webhook is not supported.
Pay with Setel onlineWebhook can be set upon creation of the checkout session.

🚧

Considerations for webhooks

  1. Due to network issues, there might be cases where the webhook requests might not be received by integrators. A retry query using the retrieve a checkout session API is recommended for this situation.

  2. Multiple webhook requests for a single order might be sent. Please ensure there is an implementation to verify which request is the latest.

2. Subscribing to a webhook

You will need to define the webhook URL when creating a checkout session. This URL will be used to receive updates on the payment sessions.

2.1 Webhook request payload

"headers": {
    "Content-Type": "application/json",
    "signature": "random string"
    }
{
    'id': 'test-id',
     'createdAt': '2021-06-09T10:09:37.927Z',
     'updatedAt': '2021-06-09T10:09:37.927Z',
     'apiKey': 'test-x-api-key',
     'paymentIntentId': 'test-payment-intent-id',
     'paymentIntentStatus': 'succeeded',
     'amount': '10',
      'referenceId': 'test-reference-id',
}

3. Signature

A signature is used to verify the identity of the request/data sent. This is needed to prevent information from being stolen or received from the wrong entity.

3.1 How to generate a signature

  • Step 1: Get the secretHash from x-api-secret.
  • Step 2: Calculate data by concatenating fields in above order.
  • Step 3: Get signature by hmac('sha256', secretHash)(data).

3.2 Sample code in NodeJS

const { createHmac, createHash } = require('crypto');

const secret = 'test-x-api-secret';
const secretHash = createHash('sha256').update(secret).digest('hex');
console.log(secretHash);
// secretHash: 874d70d91c89cfa556797c1754ea186e060a189a0cc398b000f94630c1b1674e

const body = {
    'id': 'test-id',
    'createdAt': '2021-06-09T10:09:37.927Z',
    'updatedAt': '2021-06-09T10:09:37.927Z',
    'apiKey': 'test-x-api-key',
    'paymentIntentId': 'test-payment-intent-id',
    'paymentIntentStatus': 'succeeded',
    'amount': '10',
    'referenceId': 'test-reference-id',
};
const fields = [
    'id',
    'createdAt',
    'updatedAt',
    'apiKey',
    'paymentIntentId',
    'paymentIntentStatus',
    'amount',
    'referenceId',
];

let data = '';
fields.forEach(field => data += body[field] || '');
console.log(data);
// data: test-id2021-06-09T10:09:37.927Z2021-06-09T10:09:37.927Ztest-x-api-keytest-payment-intent-idsucceeded10test-reference-id

const hmac = createHmac('sha256', secretHash).update(data).digest('hex');

console.log(hmac);
// hmac: 77b928780f10a0d2339d93be7319eda4dda4472d5a9fdf7bcc53768a2a61faf0

3.3 Sample input and output

3.3.1 Input

NameValue
x-api-secrettest-x-api-secret
body{
'id': 'test-id',
'createdAt': '2021-06-09T10:09:37.927Z',
'updatedAt': '2021-06-09T10:09:37.927Z',
'apiKey': 'test-x-api-key',
'paymentIntentId': 'test-payment-intent-id',
'paymentIntentStatus': 'succeeded',
'amount': '10',
'referenceId': 'test-reference-id',
}

3.3.2 Output

77b928780f10a0d2339d93be7319eda4dda4472d5a9fdf7bcc53768a2a61faf0