Overview

Infosys Equinox App Marketplace allows apps to register for asynchronous webhooks (for any of the events on the platform), and apps can register for pre-defined scopes (microservices and associated roles) to call back Infosys Equinox Microservices APIs.

Asynchronous Webhooks

Webhooks notify applications when specific events occur on Infosys Equinox Microservices. Apps can register for webhooks to get notified about the events (async) occurring on the platform.

Step 1: Webhook Configuration

Here is the sample webhook configuration:

URL App hosted endpoint
Webhook Type ASYNC
Priority Normal or High
Events Refer to the list of all the events on the platform available across microservices.

Note: Apps can be configured for multiple webhooks. There is no restriction on the number of webhooks that can be configured for an app.

 "webhooksConfig": [
                {
                    "url": "https://sampleurl.com/callback",
                    "webhookType": "ASYNC",
                    "priority": "Normal",
                    "events": [
                        "catalogservice/product/create"
                    ]
                }

Step 2:  Validate Signature

Once the app has registered for async webhooks, it will be notified when events occur on the platform. To view the event payload, the app has to perform signature verification. Below are the steps to fetch the app secret stored for the app and use it to generate the signature for the payload received:

  1. Fetch the app secret for the app ID, which is included in the payload.
  2. Generate the signature using the calculateHMACSHA256 function. It takes the event payload and app secret as parameters.
  3. The verifySignature function validates the signature received in the payload header against the generated signature.
  4. If there is a mismatch, it returns an authentication failure response.

Functions included – CommonUtils

  1. verifySignature(event, signatureReceived, apiSecret)
  2. calculateHMACSHA256(event, secretKey) 

App Callback

Step 1: Configure for the App Scopes

Identify the callbacks (that are required from the app) to be updated to Equinox APIs and configure them as app scopes in the app for generating callback access tokens. Provide the link to the list of app scopes on the platform.

Step 2: Get Access Token for Callback

For getting the auth token via Authorize Lambda:

  • Things Third-Party App should know:
    1. Authorize Lambda Endpoint
    2. App ID
    3. App Version
    4. Store ID ( on which App is installed)
    5. Base URL of the App
    6. State and Nonce (optional)
    7. expiryTime (should be 5 seconds after the current time)
  • Request to Authorize Lambda should have the following:
    Header: signature (requestBody encoded with the app secret)
    Request Body:
{
    "storeId": "11",
    "appId": "653906ec170e703d543dbf3f",
    "appVersion": "8.16.0",
    "baseUrl": "https: //8jr9vvcqgi.execute-api.us-east-1.amazonaws.com",
    "expireTime": 1699260660745,
    "nonce": "nonce",
    "state": "state"
}
  • Required Step: to get the app secret, copy the app secret manually or subscribe for webhooks:
{
    "entity": "app",
    "url": "domain/appSecretEventListener",
    "webhookType": "ASYNC",
    "events": [
        "appmarketplaceservices/generateapikeyandsecret/create"
    ]
}
  1. To get the store ID when the app is installed in the store, subscribe to webhooks:
{
    "entity": "app",
    "url": "doamin/appInstallationListener",
    "webhookType": "ASYNC",
    "events": [
        "appmarketplaceservices/apps/updateinstallation"
    ]
}
  1. Call Authorize Lambda with:
    Header: signature (requestBody encoded with the app secret)
    Request Body:
{
    "storeId": "",
    "appId": "",
    "appVersion": "",
    "baseUrl": "",
    "expireTime":"",
    "nonce": "",
    "state": ""
}

Reference

The following diagram depicts the asynchronous webhooks workflow of App Marketplace: Note: For the list of events and its payload of all microservices, see Events and its Payload.

Webhooks Payload Verification

A signature is a response generated when the payload body is encoded with the App Secret Key.

Prerequisite

Serverless apps should store their app secret, generated when the app is created in the Infosys Equinox App Marketplace.

Action

  1. Receive the signature: Check if you received a signature in the header section or not. The non-availability of the signature means the app secret is not present for that app.

  2. Extract signature and data: If the signature is present when the receiver receives the webhook data, extract both the data from the body and the received signature from the headers.

  3. Calculate the signature: Use the calculate HMACSHA256 function to calculate the signature based on the received data and the app secret key.

  4. Compare the Signature: Compare the received signature with the calculated signature. If they match, the data is not tampered with, and the sender is verified.

The following table describes the sample payload and code snippet:

FieldDescription
payload {
version: '2.0',
routeKey: 'POST /target',
rawPath: '/target',
rawQueryString: '',
headers: {
accept: '*/*',
'accept-encoding': 'gzip, deflate, br',
'content-length': '2898',
'content-type': 'application/json',
host: '8jr9vvcqgi.execute-api.us-east-1.amazonaws.com',
signature: '76a82c4751c7716d6a8c56c3d4013a43d9923e1a8fdc42883f7b48d6ddaf5776',
'user-agent': 'node-fetch',
'x-amzn-trace-id': 'Root=1-651bcf81-66dd86aa0d0202845330e51a',
'x-forwarded-for': '54.226.21.201',
'x-forwarded-port': '443',
'x-forwarded-proto': 'https'
},
requestContext: {
accountId: '277827593990',
apiId: '8jr9vvcqgi',
domainName: '8jr9vvcqgi.execute-api.us-east-1.amazonaws.com',
domainPrefix: '8jr9vvcqgi',
http: {
method: 'POST',
path: '/target',
protocol: 'HTTP/1.1',
sourceIp: '54.226.21.201',
userAgent: 'node-fetch'
},
requestId: 'MN1cVgmhoAMEMSA=',
routeKey: 'POST /target',



stage: '$default',
time: '03/Oct/2023:08:23:29 +0000',
timeEpoch: 1696321409814
},
body: '{"webhookDetails":{"appId":"651bce9d5d4dea0a83931083","appVersion":"1.0.0","storeId":"11","serviceCollectionId":"11","serviceName":"appmarketplace","subscriptionId":"651bcf085d4dea0a83931089","webhookId":"appmarketplaceservices.apps.updateinstallation","webhookName":"appmarketplaceservices/apps/updateinstallation","webhookType":"ASYNC","webhookUrl":"https://8jr9vvcqgi.execute-api.us-east-1.amazonaws.com/target","webhookTestUrl":"https://8jr9vvcqgi.execute-api.us-east-1.amazonaws.com/target","priority":"25","active":true,"createdOn":"2023-10-03T08:21:28.508","createdBy":0,"updatedOn":"2023-10-03T08:21:28.508","updatedBy":0},"event":{"version":"0","id":"72ebee61-6d96-7906-01c9-49f4d9c9411e","detail-type":"appmarketplaceservices/apps/updateinstallation","source":"appmarketplaceservices","account":"277827593990","time":"2023-10-03T08:23:28Z","region":"us-east-1","resources":[],"detail":{"id":"651bce9d5d4dea0a83931083","timestamp":1696321408767,"payload":{"patchResponseList":[{"createdBy":0,"createdOn":[2023,10,3,8,21,28,303000000],"updatedBy":0,"updatedOn":[2023,10,3,8,23,28,762325000],"id":"651bcf085d4dea0a83931085","appId":"651bce9d5d4dea0a83931083","appVersion":"1.0.0","merchantId":"1234","storeId":"11","collectionId":null,"clientId":"tarb86dd127749a","visibilityType":"PUBLIC","storeName":"T-Giant B2C Store","status":"ACTIVATED","category":"string","appName":"targetTest10","installationComplete":true,"installationStatus":"FULL","active":true,"appDisplayType":"DESKTOP","appConfigData":{"uiConfig":null,"feed":null,"installationConfig":null,"collectionConfig":[{"serviceName":"auth","serviceCollectionId":"10"},{"serviceName":"catalog","serviceCollectionId":"10"},{"serviceName":"inventory","serviceCollectionId":"10"},{"serviceName":"pricing","serviceCollectionId":"10"},{"serviceName":"order","serviceCollectionId":"1538405371250445"}]},"reason":null,"businessId":"3","businessEmail":null,"merchantEmailId":null,"businessType":"B2BANDB2C","processType":null,"locale":"en_US","storeLogo":"null"}]},"eventType":"appmarketplaceservices/apps/updateinstallation","userId":"45","actOnBehalfOfUser":0,"roles":["ROLE_SUPER_ADMIN"]}},"appConfigData":{"collectionConfig":[{"serviceName":"auth","serviceCollectionId":"10"},{"serviceName":"catalog","serviceCollectionId":"10"},{"serviceName":"inventory","serviceCollectionId":"10"},{"serviceName":"pricing","serviceCollectionId":"10"},{"serviceName":"order","serviceCollectionId":"1538405371250445"}],"uiConfig":null,"feed":null,"installationConfig":null},"businessId":"3","clientId":"tarb86dd127749a","authToken":"eyJhbGciOiJIUzUxMiJ9.eyJjcmVhdGVkIjoxNjk2MzIxNDA5NzYxLCJhY3Rpbmd1c2VybmFtZSI6IjIwMjMiLCJleHAiOjE2OTY0MDc4MDksImhhc2giOiJhYzRmMWE3NmRkNWJhMGVlMTViOTg4ZTVjZWEzNmM0MiIsImF1dGhvcml0aWVzIjoiW3t9XSIsInVzZXJuYW1lIjoidGFyYjg2ZGQxMjc3NDlhIn0.HcC_M7_DJkHDX3k2L365Cw9dQGeokg2pgKkl1ot-bCxPWE86JnSKP7yqs4ALXcd_dlN1M0nNdCH5jL5J9Ttwdg"}',
isBase64Encoded: false
}
code snippetimport CryptoJS from 'crypto-js';



let signatureReceived = event.headers.signature;

let bodyReceived = event.body;

let apiSecret = getValuesFromParameterStore;



var signature = calculateHMACSHA256(bodyReceived, apiSecret)

if (signatureReceived == signature) {

console.log("Authentication Successfull")

} else {

console.log("Authentication Failed")

return {

statusCode: 400,

body: "Authentication Failed"

};

}



function calculateHMACSHA256(event, secretKey) {

var hmac = CryptoJS.HmacSHA256(event, secretKey);

return hmac.toString(CryptoJS.enc.Hex);

}
     

Appendix

List of Events in All Microservices

Refer to the list of all the events on the platform available across microservices:

 

Revision History | top
     

      Revision History
      2024-05-20 | JP – Created the page and added the content.