Web SDK Integration Guide

This tutorial will provide a guide to developing a basic 'Hosted Session' type integration, using Network International's proprietary Web SDK technology, and is intended to provide a starting point for customers and developers that wish to accept credit and debit card payments on their website.

Before you start

You will need the following items before you begin to integrate the Web SDK for a Hosted Session integration:

  • A front-end web-site capable of consuming 3rd-party JavaScript libraries (i.e. the Web SDK)
  • A back-end service capable of making JSON-based POST requests to the N-Genius Online payments APIs
  • One API key (of type 'Hosted Session') to facilitate SDK authentication and session generation
  • A second (back-end) API key, used to complete back-end payment completion requests

Both these keys can be generated from the N-Genius Online portal by navigating to the Settings > Integrations > Service Accounts section and following the on-screen prompts.

You will also require an outlet reference (UUID). You can find this by navigating to Settings > Organization Hierarchy in the N-Genius Online portal and clicking on a relevant Outlet to retrieve the matching reference UUID.

Once you are satisfied that you have everything you need, please find below the steps required to integrate the Web SDK onto your own website for a Hosted Session integration.

1. Include the SDK in your website

We host the JavaScript SDK file for this integration type on your behalf in order to streamline your development process, and to ensure that you are always using the most up-to-date version of the SDK. To embed the SDK into your web-site/app, you will need to link it from your code using a <script> HTML tag, or similar.

The SDK URL for inclusion in your <script> tag will be:

  • https://paypage.sandbox.ngenius-payments.com/hosted-sessions/sdk.js, if you are integrating to our UAT environment (for initial/ongoing integration testing), or...
  • https://PAYPAGEDOMAIN/hosted-sessions/sdk.js, if you are integrating to the Live/Production environment

An example of this link method can be seen below in the <script> tag:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <!--SDK url with rest of your scripts-->
    <script src="https://<<paypageSandboxDomain>>/hosted-sessions/sdk.js"></script>
  </head>
  <body>
    <!--Your website code goes here-->
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <!--SDK url with rest of your scripts-->
    <script src="https://paypage-uat.ngenius-payments.com/hosted-sessions/sdk.js"></script>
  </head>
  <body>
    <!--Your website code goes here-->
  </body>
</html>

Once you include the SDK script in your code, as above, the NI object will become available in the document window. If you are familiar with jQuery, the NI object functions comparably to jQuery's $ syntax.

2. Mount the payment form on your website

Once the SDK has been included successfully in your project, you will need to call the mountCardInput method under the NI namespace, providing a DOM ID of the element you wish to mount the payment card fields onto:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <script src="https://<<paypageSandboxDomain>>/hosted-sessions/sdk.js"></script>
  </head>
  <body>
    <!--Your website code goes here-->
    <!--below div will have payment form loaded -->
    <div id="mount-id"></div>
    <!--....-->
  </body>
  <script>
    /* Method call to mount the card input on your website */
    window.NI.mountCardInput('mount-id'/* the mount id*/, {
      style, // Style configuration you can pass to customize the UI
      hostedSessionApiKey, // // Hosted Session Key which is used to generate Session ID	
      language, // language to be passed either "ar" or "en", default: "en"
      outletRef, // outlet reference from the portal
      onSuccess, // Success callback if hostedSessionApiKey validation succeeds
      onFail, // Fail callback if hostedSessionApiKey validation fails
      onChangeValidStatus: ({
    		isCVVValid,
    		isExpiryValid,
    		isNameValid,
    		isPanValid
  		}) => {
    		console.log(isCVVValid, isExpiryValid, isNameValid, isPanValid);
  		}
    });
  </script>
</html>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <script src="https://paypage-uat.ngenius-payments.com/hosted-sessions/sdk.js"></script>
  </head>
  <body>
    <!--Your website code goes here-->
    <!--below div will have payment form loaded -->
    <div id="mount-id"></div>
    <!--....-->
  </body>
  <script>
    /* Method call to mount the card input on your website */
    "use strict";
    window.NI.mountCardInput('mount-id'
    /* the mount id*/
    , {
      style: style,
      // Style configuration you can pass to customize the UI
      apiKey: apiKey,
      // API Key for WEB SDK from the portal
      outletRef: outletRef,
      // outlet reference from the portal
      onSuccess: onSuccess,
      // Success callback if apiKey validation succeeds
      onFail: onFail, // Fail callback if apiKey validation fails
      onChangeValidStatus: (function (_ref) {
  			var isCVVValid = _ref.isCVVValid,
      	isExpiryValid = _ref.isExpiryValid,
      	isNameValid = _ref.isNameValid,
      	isPanValid = _ref.isPanValid;
  			console.log(isCVVValid, isExpiryValid, isNameValid, isPanValid);
			});
    });
  </script>
</html>

This method requires a number of mandatory inputs:

  • style: a set of configurable CSS elements you can use to customize the look and feel of the payment fields when they are displayed to the user
  • hostedSessionApiKey : a dedicated, limited authority API key used by the SDK to authenticate your request, but which cannot be used to execute payments, login to the N-Genius Online portal, or perform any other API interactions beyond SDK authentication. An API key for this purpose can be generated at any time from the N-Genius Online portal (Settings > Integrations > Service Accounts (Type - Hosted Session Service Account)).
  • outletRef: a UUID representing the N-Genius Online outlet against which you wish to process a transaction
  • onSuccess: a call-back function allowing you to provide bespoke code to be executed if the authentication of the SDK with N-Genius Online is successful. Often, this is used to hide an element that could be used as a placeholder whilst the SDK is being loaded (i.e. a spinner)
  • onFail: a call-back function allowing you to provide bespoke code to be executed if the authentication of the SDK is unsuccessful.
  • onChangeValidStatus: a call-back function that is called when a field changes from valid to invalid state or vice versa. This function is called with a single argument which is an object with 4 keys as follows isCVVValid, isExpiryValid, isNameValid, isPanValid. Each key holds a value true or false based on the validity of the respective fields. You can use this call back to ascertain if all fields are valid or not before proceeding to generate a session ID.

Note that you may wish to call this method only when the full DOM has been loaded by the user's browser, in order to avoid errors arising from order of execution.

📘

DOM Readiness

Various methods exist for ensuring that, when the JavaScript contained in the <script> HTML tag is executed, the full DOM has already been loaded. One such example is the jQuery .ready() syntax, but how you choose to do this will depend on your desired user experience, and choice of programming language.

If all the above parameters are correct, the SDK will attempt to authenticate itself with the N-Genius Online identity services and, if successful, you should now see the payment form loaded on the DOM mount id provided.

A basic example of the customization available with the style element:

🚧

Token Expiry

By default, the self-authentication of the N-Genius Online SDK is only valid for five minutes.

It is important to factor this understanding in to your page design, in order to ensure that the payment fields are only displayed when your customer is actually ready to pay, or to provide some mechanism to automatically refresh the session after the five minute period has elapsed.

Once you have been successful in calling the payment fields from the SDK, we must now capture the card information provided by the customer, and generate a unique payment session.


How to Unmount(optional) :

Some merchants design their payment pages similarly to Stepper, where the user enters information in multiple steps. If your payment page is designed in this way, you must mount and unmount the card inputs to account for all possible scenarios and obtain correct behavior for our SDK. Therefore, you must use the unMountCardinputs function from the window.NI object to unmount the SDK

window.NI.unMountCardInputs()


3. Generate Session ID

The Session ID (sessionId) is a unique reference value valid against the context of the entered card details, which can be used only one time, and is required to make a payment with N-Genius Online.

To generate the sessionId you need to call an asynchronous JavaScript method called generateSessionId in the NI namespace.

Example:

/* This code would only work if its called after the mountCardInput has been called successfully */
<div>
      <div id="mount-id"></div>
  	  <button onclick="createSession()" class="checkoutButton">
              Check out
      </button>
</div>
<script>
let sessionId;
async function createSession() {
  try {
    const response = await window.NI.generateSessionId();
    sessionId = response.session_id;
  } catch (err) {
    console.error(err);
  }
}
</script>
/* This code would only work if its called after the mountCardInput has been called successfully */
<div>
      <div id="mount-id"></div>
  	  <button onclick="createSession()" class="checkoutButton">
              Check out
      </button>
</div>
<script>
"use strict";
var sessionId;
function createSession() {
  window.NI.generateSessionId().then(function (response) {
    sessionId = response.session_id;
  }).catch(function (error) {
    return console.error(err);
  });
}
</script>

Typically, the generateSessionId method would be called by the onClick (or similar) function of a button in your control labelled "Pay" or "Pay Now".

Importantly, you should store the resulting (sessionId) of successful generateSessionId calls for use in the later steps.

🚧

Auth Timeout

Note that the above generateSessionId will fail with an error if the authentication window of the previous step (mountCardInput) has expired. For testing purposes, you should ensure that you refresh the page or reload the SDK handling component at appropriate intervals in order to avoid such errors.

Once you have successfully generated a valid sessionId from the generateSessionId call, we may now move on to performing the final step in completing the requested payment.

4. Make a call to your own server to make the payment

Now that we have the sessionId generated we can proceed to make the payment, the final step which requires your back-end services to call the N-Genius Online APIs to complete the payment.

The flow diagram below describes the remaining process.

You should pass the sessionId received in the previous step to your back-end service, perhaps via a POST request or similar. This will be used to complete the payment process.

❗️

Data Integrity

Since the sessionId variable is a one-time value, it is safe to pass this information from your front-end to your back-end service.

However, it is extremely important that you do not attempt to pass any sensitive, order/payment related data in this way, as it is visible to the consumer's browser and may be compromised.

Instead, sensitive data related to the payer's order (i.e. amount, keys, etc.) should be retrieved and sourced from the back-end service directly, perhaps from a database containing records of customer baskets, or similar.

It is your responsibility to ensure that your site is not passing sensitive data across domains in an insecure manner.

Request an access token

We must first validate ourselves with the identity service and obtain an access token. This token allows us to execute operations on the gateway APIs with authority, and whilst a single token will expire after 5 minutes, we can use our Service Account API key to generate one at any time.

HTTP Request Method: POST
Resource (URI): https://api-gateway.sandbox.ngenius-payments.com/identity/auth/access-token

Headers

Header keyHeader value
Content-Typex-www-form-urlencoded
AuthorizationBasic: api_key

Body

Payload / body keyPayload / body value
grant_typeclient_credentials

Example request

{
	‘grant_type’: ‘client_credentials’
}

Example response

{     
  “access_token”: “eyJhbGciOiJSUzI1N…0eDloZnVRI”,     
  “expires_in”: 300,     
  “refresh_expires_in”: 1800,     
  “refresh_token”: “eyJhbGciOi…eDloZnVRIn0”,
  “token_type”: “bearer”,
  “not-before-policy”: 0,
  “session_state”: “1f2e7d6e-e1f6-41f8-b763-37be5ef747d2”,
  “scope”: “”
}

Retrieve the access_token value and store it ready for the next step...

Complete the payment

Now we can call the N-Genius Online APIs to request completion of the payment we initiated earlier, using the following key values:

  • sessionId: passed from our front-end to our back-end service, at the beginning of step 4 of this integration guide
  • access_token: which we just received from our identification/authentication call
  • outletRef: the N-Genius Online outlet reference we used in the SDK authentication call in step 2 of this guide
  • amount: retrieved from a secure back-end source (not passed from front-end)

In particular, you should ensure that any static values in your code (i.e. outletRef) are not referenced/embedded directly, but pulled from a secure source. This will improve both the security and the flexibility of your integration.

HTTP Request Method: POST
Resource (URI): https://api-gateway.sandbox.ngenius-payments.com/transactions/outlets/ {outletRef} /payment/hosted-session/ {sessionId}

Headers

Header keyHeader value
AuthorizationBearer access_token
Content-Typeapplication/vnd.ni-payment.v2+json
Acceptapplication/vnd.ni-payment.v2+json

Body

Payload / body keyPayload / body value
action"SALE" (or "AUTH" for authorization only)
amount { }N/A
amount.currencyCode"AED" (or "USD", "GBP", per availability)
amount.value100 (in minor units, i.e. "100" = 1.00 AED)
payment { }N/A (For MCP transactions only)
payment.currencyPayer chosen currency, i.e. "USD" (For MCP Transactions only)

Example request

{
  "action": "SALE",
  "amount": { "currencyCode": "AED", "value":100 }
}

Code examples

// NodeJS example (using express.js framework)

async function getAuthToken() {
  const {
    data: { access_token: accessToken }
  } = await axios.post(
    IDENTITY_API_URL,
    querystring.stringify({ grant_type: 'client_credentials' }),
    {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        Authorization: `Basic ${API_KEY}` /* This is the Service Account API key to make the payment */
      }
    }
  );
  return accessToken;
}

app.post('/api/hosted-sessions/payment', async (req, res) => {
  try {
    const { sessionId, order, outletRef } = req.body;
    const accessToken = await getAuthToken();
    const { data } = await axios.post(
      `${TRANSACTIONS_SERVICE_URL}outlets/${outletRef}/payment/hosted-session/${sessionId}`,
      {
        ...order
      },
      {
        headers: {
          Authorization: `Bearer ${accessToken}`, // Note the access_token received in the previous step is passed here
          'Content-Type': 'application/vnd.ni-payment.v2+json',
          Accept: 'application/vnd.ni-payment.v2+json'
        }
      }
    );
    res.status(200).send(data);
  } catch (e) {
    res.send(500).send(e.message);
  }
});
<?php

// get these from secure back-end sources
$outlet = {outletRef};
$apikey = {apiKey};
$amount = {amount};

if (isset($_POST['sessionId'])) { 

  $session = $_POST['sessionId'];

  try {

    $idData = identify($apikey);

    if (isset($idData->access_token)) { 

      $token = $idData->access_token;
      $payData = pay($session, $token, $outlet);

      echo(json_encode($payData));

    }

  } catch (Exception $e) {

    echo($e->getMessage());

  }

}

/////////////////////

function identify($apikey) {

  $idUrl = "https://identity-uat.ngenius-payments.com/auth/realms/ni/protocol/openid-connect/token";
  $idHead = array("Authorization: Basic ".$apikey, "Content-Type: application/x-www-form-urlencoded");
  $idPost = http_build_query(array('grant_type' => 'client_credentials'));
  $idOutput = invokeCurlRequest("POST", $idUrl, $idHead, $idPost);
  return $idOutput;

}

function pay($session, $token, $outlet) {
    
  // construct order object JSON
  $ord = new stdClass;
  $ord->action = "SALE";
  $ord->amount = new stdClass;
  $ord->amount->currencyCode = "AED";
  $ord->amount->value = "100";

  $payUrl = "https://api-gateway-uat.ngenius-payments.com/transactions/outlets/".$outlet."/payment/hosted-session/".$session;
  $payHead = array("Authorization: Bearer ".$token, "Content-Type: application/vnd.ni-payment.v2+json", "Accept: application/vnd.ni-payment.v2+json");
  $payPost = json_encode($ord); 

  $payOutput = invokeCurlRequest("POST", $payUrl, $payHead, $payPost, true);

  return $payOutput;

}

function invokeCurlRequest($type, $url, $headers, $post, $json) {

  $ch = curl_init();

  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);		

  if ($type == "POST" || $type == "PUT") {
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
    if ($type == "PUT") {
      curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
    }
  }

  $server_output = curl_exec ($ch);
  return json_decode($server_output);

}

?>

This is just a sample snippet of handling the payment call from your server and it can be implemented in any language of your choice.

5. Handle Payment Response (Without 3DS)

Most customers will be processing payments enabled for 3-D Secure payer authentication. However, in some instances the 3DS step is either suppressed, switched off or not relevant.

Once you have received the response from the previous call, you will need to call the handlePaymentResponse method in the NI namespace, as follows:

/* paymentResponse is the response from the previous call */
const {status, error} = await window.NI.handlePaymentResponse(paymentResponse);
"use strict";

/* paymentResponse is the response from the previous call */
window.NI.handlePaymentResponse(paymentResponse).then(function (response) {
  console.log(response.status, response.error);
});

In order to understand the status of the payment you will need evaluate the status parameter.

For a non-3DS payment (one where 3-D Secure payer authentication process has been skipped, or is not relevant) the status can take one of 3 distinct states, found in the paymentStates object under the NI namespace:

  1. For successful authorization, status will be: window.NI.paymentStates.AUTHORISED
  2. For a successful sale, status will be: window.NI.paymentStates.CAPTURED
  3. For a successful card verification, status will be: window.NI.paymentStates.VERIFIED
  4. For a failed transaction, status will be window.NI.paymentStates.FAILED

Based on the result of the above status check, you may then provide an appropriate outcome message to your customer.

const {status, error} = await window.NI.handlePaymentResponse(paymentResponse);

if (
    status === window.NI.paymentStates.AUTHORISED ||
    status === window.NI.paymentStates.CAPTURED
) {
  // Same as before this signals successful payment
} else if (
    status === window.NI.paymentStates.FAILED ||
    // A new state to look out for is 3DS Challenge failure
    status === window.NI.paymentStates.THREE_DS_FAILURE
) {
  // payment failure signal
} else {
// FAILED authentications scenarios
}
"use strict";

window.NI.handlePaymentResponse(paymentResponse)
  .then(function (response) {
    if (response.status === window.NI.paymentStates.AUTHORISED ||
      response.status === window.NI.paymentStates.CAPTURED) {
      // do something to show status on UI
    } else if (response.status === window.NI.paymentStates.FAILED) {
      // do something for failed case
    }
  });

6. Handle Payment Response with 3DS Enabled

This will be pretty similar to the previous step the only distinction being we need a way mountId again to show the 3DS challenge form in your website. So the function call changes a little bit to look something like this:

const { status, error } = await window.NI.handlePaymentResponse(
  paymentResponse,
  {
    mountId: '3ds_iframe',
    style: { width: 500, height: 500 }
  }
);
"use strict";

window.NI.handlePaymentResponse(paymentResponse, {
  mountId: '3ds_iframe',
  style: {
    width: 500,
    height: 500
  }
}).then(function (response) {
  console.log(response.status, response.error);
});

The only difference being the additional object with mountId and style config to load 3DS challenge iframe.

To get the status of 3DS challenge is same as before with few other paymentStates :

const {status, error} = await window.NI.handlePaymentResponse(paymentResponse);

if (
    status === window.NI.paymentStates.AUTHORISED ||
    status === window.NI.paymentStates.CAPTURED
) {
  // Same as before this signals successful payment
} else if (
    status === window.NI.paymentStates.FAILED ||
    // A new state to look out for is 3DS Challenge failure
    status === window.NI.paymentStates.THREE_DS_FAILURE
) {
  // payment failure signal
} else {
// FAILED authentications scenarios
}
"use strict";

window.NI.handlePaymentResponse(paymentResponse, {
  mountId: '3ds_iframe',
  style: {
    width: 500,
    height: 500
  }
}).then(function (response) {
  var status = response.status;

  if (status === window.NI.paymentStates.AUTHORISED
    || status === window.NI.paymentStates.CAPTURED) {
    // Same as before this signals successful payment
  } else if (status === window.NI.paymentStates.FAILED || // A new state to look out for is 3DS Challenge failure
    status === window.NI.paymentStates.THREE_DS_FAILURE) {
    // payment failure signal
  }
});

So the new status the payment can be in is: NI.paymentStates.THREE_DS_FAILURE.