Webhooks


A webhook is an API that providers near real-time information. PayTel uses webhooks to inform the merchants regarding changes on a Transaction payment status.

The Merchant can receive the notifications either by email or endpoint (notification assembling to the status inquiry – Checkout Status), however only a notification will be sent for each transaction.

Notifications are sent by endpoint straight to the URL configured by the Merchant. The parameterization of this endpoint needs to be done on the Gateway BackOffice.

Learn how to create a webhook on the Gateway BackOffice.

Each time that Gateway receives an update on the payment status of a transaction a notification will be sent with the new transaction payment status.

Every day a summarized email with the most recent failed notifications, is sent by the Gateway (email needs to be registered on the BackOffice).

There is no guarantee on the order of messages, especially if the time difference between the notifications is smaller than the time it takes to process them or by any communication or systems issues. Once the issues are sort out, new notifications will arrive in real time and old notifications would be resent. In case no notification is received the option “Checkout Status” should be used before rejecting any transaction.

Notifications are sent as HTTP callbacks (webhooks) to an endpoint on your server. Please ensure you have a valid SSL certificate chain. Self-signed certificates are not valid.

To receive notifications, you need a server that has:

  • An endpoint that can receive an HTTP POST.
  • An open TCP port for HTTPS traffic (443, 80) with TLSv1.2.

  • Depending on your network and security requirements, you might also need to add our network to your firewall's whitelist.

    To ensure that your server is properly accepting notifications, we require you to acknowledge every notification of any type with an HTTP 200 and a response containing:

    {

    "statusCode": "200",

    "statusMsg": "Success",

    "notificationID": "2533e456-5e36-42c8-9eea-7961902f185e"

    }

    When your server receives a notification it should:

  • Decrypt the notification
  • Store the notification in your database.
  • Acknowledge the notification with an HTTP 200 OK and the following response body: { "statusCode": "200", "statusMsg": "Success","notificationID": "2533e456-5e36-42c8-9eea-7961902f185e" }


  • Decrypt

    The content of notification is encrypted to protect data from fraud attempts. When converting human-readable string to hexadecimal format, we use UTF-8.

  • Encryption algorithm : AES
  • Block mode : GCM
  • Padding : None
  • Initialization vector : In HTTP header (X-Initialization-Vector)
  • Authentication tag : In HTTP header (X-Authentication-Tag)
  • Format of body: Base64 Format of Initialization Vector: Base64

    Secret:

    O0Bur9uhZkS54NkwFhVyeutED6DhLbOQUBDt3i3W/C4=

    X-Authentication-Tag:

    Ytw9bzOS1pXqizAKMGXVQ==

    Content-Type:

    text/plain

    X-Initialization-Vector:

    Ldo3OyWNgRchSF3C

    Body before decryption:

    WgErmJOV6wg3BuRkrgZLUUnh57BYzhIzvBFdpadHRsc43UcjtZEevRGDIDu3YxocXMXe8O+xQpMRxwTJPv766IaNqUiUEjAIj

    ZSMEYCZ0pBursUYB+9nB4eqNUiAS2MJ9sR+Cj2iBf6G6KXLfp9K6dK7c0UED5XrJwbovY8X8pMyxktFTEaflp0e76ZywsCQvt

    qEtqNz9uYEyqmAANbsBwbwyWpkCC8H1kZN2fV3CYetW1CTPmWdPp3C18Yfh826NN4XlKu1VmUmea70PyjmRKSsjPXpfrRX8ud

    elVIK2WTFtnRxD4x588d1nlGY5D5DQmJ8KYZzfvjTmDXGAPiRIEGuXp8h6rBQXS8P/m1llBtboGgQv4MmW3zvq0G6KFlYIcM=

    Body after decryption:

    {

    "returnStatus": {

    "statusMsg": "Success",

    "statusCode": "000"

    },

    "paymentStatus": "Success",

    "paymentMethod": "CARD",

    "transactionID": "WebhookTest",

    "amount": {

    "currency": "EUR",

    "value": 10.0

    },

    "merchant": {

    "terminalId": 1000000

    },

    "paymentType": "PURS",

    "notificationID": "f153c248-e7be-4c12-8d88-6c9f1f3b83e4"

    }




    Examples

    Below there are 4 examples of how to decrypt the webhook notification

    using System;

    using System.Security.Cryptography;

    using System.Text;


    public static class Program {

    public static void Main() {

    byte[] secret = System.Convert.FromBase64String("6fNDiYU0T0/evFpmfycNai/AqF24i+rT0OmuVw0/sGQ=");


    byte[] ciphertext = System.Convert.FromBase64String("9bIjURJIcwoKvQr+ifOTH3HbMX+IqmsRqHuG/I1GfbSX89JE5DcWh/p8QROC5pRAuYZ7"+

    "ln7RSkHXJdZpVz1LFQ2859WsetvHHui7qYmfxATOO1j0AQuPdAD3FeRH0kR4s/v3c2nV8"+

    "1DnUXFCnQER/+VWrYdbu5vn8gm+diSE6CHvkK+ODy0ebVi5O6VBnWVjgBUG33VwWiAyIl"+

    "7Ik435V55WnZgynH3GfbVYoGwZ5UhYtn3yw2yruiLAKu6VTBvnh/ZJP21cHCJSF6NPSd+8"+

    "1gzWFU/+ECm3cf3uBbCkmKmL7HxRhRxhG0lMtX6ELZOXuw3eDJ1BTu+sSMkV/5Xk+5XX48"+

    "XmP6CGZ7KmP7Q3Fw1kZmhn0unFyv0Gw8PjT1Ohny/HMgNl16I=");


    byte[] nonce = System.Convert.FromBase64String("RYjpCMtUmK54T6Lk");


    byte[] tag = System.Convert.FromBase64String("FUajWHmZjP4A5qaa1G0kxw==");


    using (var aes = new AesGcm(secret))


    {

    var plaintextBytes = new byte[ciphertext.Length];

    aes.Decrypt(nonce, ciphertext, tag, plaintextBytes);


    string decrypt = Encoding.UTF8.GetString(plaintextBytes);


    Console.WriteLine(decrypt);


    }

    }


    }

    import java.security.Security;

    import java.util.Base64;


    import javax.crypto.Cipher;

    import javax.crypto.spec.IvParameterSpec;

    import javax.crypto.spec.SecretKeySpec;


    import com.google.common.base.Charsets;


    import org.apache.commons.lang3.ArrayUtils;

    import org.bouncycastle.jce.provider.BouncyCastleProvider;


    // For Java and JVM-based languages, you might need to install unrestricted policy file for JVM,

    // which is provided by Sun. Please refer BouncyCastle FAQ if you get

    // java.lang.SecurityException: Unsupported keysize or algorithm parameters or

    // java.security.InvalidKeyException: Illegal key size.


    // If you cannot install unrestricted policy file for JVM because of some reason, you can try with reflection: See here.


    public class Test {


    public static void main(String[] args) {


    try {

    Security.addProvider(new BouncyCastleProvider());


    // Data from configuration

    String keyFromConfiguration = "6fNDiYU0T0/evFpmfycNai/AqF24i+rT0OmuVw0/sGQ=";


    // Data from server

    String ivFromHttpHeader = "RYjpCMtUmK54T6Lk";

    String authTagFromHttpHeader = "FUajWHmZjP4A5qaa1G0kxw==";

    String httpBody = "9bIjURJIcwoKvQr+ifOTH3HbMX+IqmsRqHuG/I1GfbSX89JE5DcWh/p8QROC5pRAuYZ7"

    +"ln7RSkHXJdZpVz1LFQ2859WsetvHHui7qYmfxATOO1j0AQuPdAD3FeRH0kR4s/v3c2nV8"

    +"1DnUXFCnQER/+VWrYdbu5vn8gm+diSE6CHvkK+ODy0ebVi5O6VBnWVjgBUG33VwWiAyIl"

    +"7Ik435V55WnZgynH3GfbVYoGwZ5UhYtn3yw2yruiLAKu6VTBvnh/ZJP21cHCJSF6NPSd+8"

    +"1gzWFU/+ECm3cf3uBbCkmKmL7HxRhRxhG0lMtX6ELZOXuw3eDJ1BTu+sSMkV/5Xk+5XX48"

    +"XmP6CGZ7KmP7Q3Fw1kZmhn0unFyv0Gw8PjT1Ohny/HMgNl16I=";


    // Convert data to process

    byte[] key = Base64.getDecoder().decode(keyFromConfiguration);

    byte[] iv = Base64.getDecoder().decode(ivFromHttpHeader);

    byte[] authTag = Base64.getDecoder().decode(authTagFromHttpHeader);

    byte[] encryptedText = Base64.getDecoder().decode(httpBody);


    // Unlike other programming language, We have to append auth tag at the end of

    // encrypted text in Java

    byte[] cipherText = ArrayUtils.addAll(encryptedText, authTag);


    // Prepare decryption

    SecretKeySpec keySpec = new SecretKeySpec(key, 0, 32, "AES");

    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

    cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));


    // Decrypt

    byte[] bytes = cipher.doFinal(cipherText);

    System.out.println(new String(bytes, Charsets.UTF_8));


    } catch (Exception e) {

    e.printStackTrace();

    }

    }


    }

    function sodium_decrypt( $webhookSecret, $iv_from_http_header, $http_body , $auth_tag_from_http_header ){

    $key = mb_convert_encoding($webhookSecret, "UTF-8", "BASE64");

    $iv = mb_convert_encoding($iv_from_http_header, "UTF-8", "BASE64");

    $cipher_text = mb_convert_encoding($http_body, "UTF-8", "BASE64") . mb_convert_encoding($auth_tag_from_http_header, "UTF-8", "BASE64");


    $result = sodium_crypto_aead_aes256gcm_decrypt($cipher_text, "", $iv, $key);


    return $result;


    }


    $webhookSecret = "6fNDiYU0T0/evFpmfycNai/AqF24i+rT0OmuVw0/sGQ=";

    $iv_from_http_header = "RYjpCMtUmK54T6Lk";

    $auth_tag_from_http_header = "FUajWHmZjP4A5qaa1G0kxw==";

    $http_body = "9bIjURJIcwoKvQr+ifOTH3HbMX+IqmsRqHuG/I1GfbSX89JE5DcWh/p8QROC5pRAuYZ7" .

    "ln7RSkHXJdZpVz1LFQ2859WsetvHHui7qYmfxATOO1j0AQuPdAD3FeRH0kR4s/v3c2nV8" .

    "1DnUXFCnQER/+VWrYdbu5vn8gm+diSE6CHvkK+ODy0ebVi5O6VBnWVjgBUG33VwWiAyIl" .

    "7Ik435V55WnZgynH3GfbVYoGwZ5UhYtn3yw2yruiLAKu6VTBvnh/ZJP21cHCJSF6NPSd+8" .

    "1gzWFU/+ECm3cf3uBbCkmKmL7HxRhRxhG0lMtX6ELZOXuw3eDJ1BTu+sSMkV/5Xk+5XX48" .

    "XmP6CGZ7KmP7Q3Fw1kZmhn0unFyv0Gw8PjT1Ohny/HMgNl16I=";


    // Decrypt message

    $result = sodium_decrypt($webhookSecret, $iv_from_http_header, $http_body , $auth_tag_from_http_header);


    print($result);

    import base64

    from Cryptodome.Cipher import AES


    def decrypt_AES_GCM(encryptedMsg, authTag, secretKey, iv):

    iv = base64.b64decode(iv)

    encryptedMsg = base64.b64decode(encryptedMsg)

    secretKey = base64.b64decode(secretKey)

    authTag = base64.b64decode(authTag)

    aesCipher = AES.new(secretKey, AES.MODE_GCM, iv)

    plaintext = aesCipher.decrypt_and_verify(encryptedMsg, authTag)

    return plaintext


    example = {

    "encoded" : "9bIjURJIcwoKvQr+ifOTH3HbMX+IqmsRqHuG/I1GfbSX89JE5DcWh/p8QROC5pRAuYZ7" \

    "ln7RSkHXJdZpVz1LFQ2859WsetvHHui7qYmfxATOO1j0AQuPdAD3FeRH0kR4s/v3c2nV8" \

    "1DnUXFCnQER/+VWrYdbu5vn8gm+diSE6CHvkK+ODy0ebVi5O6VBnWVjgBUG33VwWiAyIl" \

    "7Ik435V55WnZgynH3GfbVYoGwZ5UhYtn3yw2yruiLAKu6VTBvnh/ZJP21cHCJSF6NPSd+8" \

    "1gzWFU/+ECm3cf3uBbCkmKmL7HxRhRxhG0lMtX6ELZOXuw3eDJ1BTu+sSMkV/5Xk+5XX48"

    "XmP6CGZ7KmP7Q3Fw1kZmhn0unFyv0Gw8PjT1Ohny/HMgNl16I=",

    "iv" : "RYjpCMtUmK54T6Lk",

    "tag" : "FUajWHmZjP4A5qaa1G0kxw==",

    "secret" : "6fNDiYU0T0/evFpmfycNai/AqF24i+rT0OmuVw0/sGQ="

    }


    result = decrypt_AES_GCM(example['encoded'], example['tag'], example['secret'], example['iv'])

    print(result)

    SPG Payment Form

    Available Payment Methods?

    Card

    The gateway offers integration with major international card processing schemes such as Visa and Mastercard.

    Pay by Link

    Pay by link is a reliable, convenient and secure way to get paid by consumers and businesses alike. Merchants handling telephone transactions can benefit from this solution, while this payment channel opens up a whole host of opportunities for businesses of all sizes, across sectors. It allows customers to make payments by sending them a web payment link, meaning card details do not have to be shared over the phone or text message.

    BLIK

    BLIK is the most popular mobile payment system in Poland that allows users to make instant payments using only the user's standard mobile banking app. BLIK payments are convenient and safe – you enter the BLIK code and confirm the transaction with your PIN in the banking app. You do not have to log in to online banking, enter SMS passwords or provide your payment card details.

    Credentials


    How to get your credentials ?


    Below you can find instructions on how to get your credentials.


    TerminalId

    Provided by Onboarding on your credential kit

    X-IBM-Client-Id

    Provided by Onboarding on your credential kit

    Bearer / Access Token

    Provided by Onboarding on your credential kit

    Payment type

    For single message transactions - PURS

    For two-message transactions - AUTH

    Payment Method

    Selected when you sign the contract with your Acquiring entity

    TerminalId

    Obtained on SIBS BackOffice, or provided by Onboarding team

    X-IBM-Client-Id

    Obtained on SIBS BackOffice, or provided by Onboarding team

    Bearer / Access Token

    Obtained on SIBS BackOffice

    Payment type

    For single message transactions - PURS

    For two-message transactions - AUTH

    Payment Method

    Selected when you sign the contract with your Acquiring entity