Receiving legacy webhooks

Background
Before starting this tutorial, make sure you understand the following topics from the Quick start section:

After you have configured legacy webhooks for the event types that you want, you must be ready to receive the webhook. Optionally, for security purposes, you can verify the webhook signature before acting on the webhook.

In order to receive and use webhook responses for your site, do the following:

  1. If needed, whitelist the IP addresses that send the webhooks. You can find the list for both sandbox and production environments at https://ip-ranges.nex.io/ip-ranges.json. Note: The list is dynamic and is updated periodically. Therefore, you should re-check it periodically.

  2. Listen for Nexio's POST requests on the URL you provided in step 3 of the Configuring legacy webhooks tutorial.

    Note: For webhooks that fail or are rejected by the receiving server, the system resends the webhook after one minute, and then, if there is a second failure, after another minute, for a total of three attempts.

    The body of the request includes the eventType and a data object, with information about the event.

  3. Save the JSON as a string called body.

    var body = '
    {
      "eventType": "TRANSACTION_AUTHORIZED",
      "data": {
        "id": "2eruYW1lIjoidXNhZXBheSIZYXABCiOiIxMDAwMzkiLCJyZWZOdW1iZXIiOiZYXABCcmFuZG9tIjowLCJjdXJyZW5jeSI6InVzZCJ9",
        "shouldUpdateCard": true,
        "merchantId": "100039",
        "transactionDate": "2019-12-23T20:50:23.060Z",
        "authCode": "458399",
        "transactionStatus": "pending",
        "amount": 1.15,
        "transactionType": "sale",
        "currency": "USD",
        "gatewayResponse": {...},
        "data": {
            "amount": 1.15,
            "currency": "USD",
            "settlementCurrency": "USD",
            "customFields": {...},
            "customer": {...},
            "cart": {...},
            "lodging": {...}
        },
        "card": {...},
        "kountResponse": {...},
        "token": {...}
      }
    }';
    

    If you configured the webhook secret (step 5 of the Configuring legacy webhooks tutorial), the request will include a header, which you will use to verify the signature. Continue to step 3.

    If you did not configure a merchant secret, skip to step 4.

  4. Verify the signature by doing the following:

    a. Copy the signature and timestamp from the header.

    Nexio-signature: t=1554146049,v1=f66f6c47e7288e4922629ffe87819678b793944c60668d8695804e4a2b9f90d1
    

    b. Split the header string at the , character.

    c. Split each of the two strings at the = characters.

    d. Save the data following the t= as a variable called timestamp.

    var timestamp = 1554146049;
    

    e. Save the data following the s= as a variable called signature.

    var signature = 'f66f6c47e7288e4922629ffe87819678b793944c60668d8695804e4a2b9f90d1';
    

    f. Concatenate the timestamp and body (from step 2) with a . character in between and save it as a variable called payload.

    var payload = ${timestamp}.${body};
    

    g. Re-create the expected signature by creating an HMAC using the SHA256 hash function. Use the webhook secret as the key and the payload as the message.

    This is the expected signature.

    📘

    Note

    If you did not save the secret when you configured the legacy webhook (step 5), you can retrieve it again by sending a GET request to the View webhook secret endpoint for the merchant ID or the payout account ID.


    h. Compare the expected signature from step 2h with the signature you received from the webhook.
    (The signature from step 2e.)

    The following example uses Node.js to verify the signature:

    import crypto from 'crypto';
    
    function verifyHMACSignature(payload, signature, sharedSecret) {
      //Create an HMAC using the SHA256 hash function
      const hmac = crypto.createHmac('sha256', sharedSecret);
      hmac.update(payload);
    
      const mySig = hmac.digest().toString('hex');
    
      //Compare the expected signature with the signature you received
      return mySig.length === signature.length
        && crypto.timingSafeEqual(Buffer.from(mySig, 'hex'), Buffer.from(signature, 'hex'));
    }
    
    function verifyNexioSignature(body, signatureHeader, sharedSecret) {
      //Split the Nexio-Signature on the comma to get the timestamp field and the signature field
      const [timeStampField, signatureField] = signatureHeader.split(',');
    
      //Split each of the two strings on the equals signs and save them as variables
      const timestamp = timeStampField.split('=')[1];
      const signature = signatureField.split('=')[1];
    
      //Recreate the payload that was signed by Nexio
      const payload = `${timestamp}.${body}`;
    
      return verifyHMACSignature(payload, signature, sharedSecret);
    }
    

    i. If the signatures match, then the webhook is valid and you can continue to step 3.


  5. From the string you saved as body (step 2), use the eventType and a data object to determine what action, if any, to take as a result of this webhook. For more details about possible body content, see the Legacy webhook body examples page.