@k3imagine/imagine-extension-framework
TypeScript icon, indicating that this package has built-in type declarations

2.0.0 • Public • Published

Imagine Extension Framework - App JS library

Extensions

Imagine Extensions are designed to allow for deviation in the user flow and interaction of Imagine apps without any need for developer changes to the app itself.

3rd party developers can independently develop and configure their extension to be invoked at certain points within an app. These points are called Extension-Events. Triggering events invokes configured extensions by loading them in an Iframe within the app UI.

Each Extension-Event provides the extension with context to work with and allows the for the extension to return an output.

The output allows extensions to persist data in Imagine. The data that gets persisted contains a reference to a Basket entity (MS-Basket) or a Sales Transaction (MS-Transaction). The data can then be utilised in post processing, from another extension within the same sales transaction or from another Imagine App. This means that invoked extensions receive data that was captured by another extension for the same Basket.

Extension-Events

Extension-Events are defined during development, meaning they are hardcoded into the Imagine application UI. An application should trigger Extension-Events to its Gateway and in response receive a list of extensions which to invoke.

The order of invocation is specified by the response.

Extensions

Each extension must be registered in Imagine.

Upon registration a pre-shared key is generated which must be used to encrypt communication to and from Imagine and the extension. See Security chapter below for further information.

Design

Flow

Extensions are restricted to being rendered within in an Iframe in an Imagine App. It should preferably follow the look and feel of our UI.

See our UI components here

The Extension should do the decrypting and encrypting in its backend. This is to ensure that the pre-shared key doesn't get sent to a user’s browser.

Extension Data Model

During registration of an extension a JSON schema can be provided. This JSON allows the extension developer to define configuration that is needed to use the extension. This is for example important for Payment Extensions (see chapter on Payment Extensions) as these need Payment Provider specific data such as Merchant ID and secret from the Imagine tenant for them to work.

This configuration is then provided as part of the context for when the extensions is invoked.

Extensions must interact with the hosting Imagine App using the JS library.

Plain Javascript Example

See example extensions here

<h1 class="display-4" id="extensionNameLabel"></h1>
<button class="complete">Complete Execution</button>
<pre id="extensionContext">Context will be displayed here</pre>

<script type="module">
  import { ExtensionClient } from "/js/ImagineExtensionFramework/extensionClient.js";

  // Instantiate the JS library
  var extensionClient = new ExtensionClient();

  // Add listener for when the extension context from the Imagine App is received
  document.addEventListener(
    ExtensionClient.onContextReceived,
    async (event) => {
      const extension = await decryptData(event.detail);
      document.querySelector("#extensionNameLabel").textContent =
        extension.name;
      document.querySelector("#extensionContext").textContent = JSON.stringify(
        extension,
        null,
        2
      );
    }
  );

  // Button click event handler for signalling that the extension has completed its lifecycle
  document.addEventListener("click", async (event) => {
    event.preventDefault();
    if (event.target.matches(".complete")) {
      // JSON serialise data
      const dataGoingToImagine = { timestamp: new Date() };
      const jsonStringifiedData = JSON.stringify(dataGoingToImagine);
      const encryptedData = await encryptData(jsonStringifiedData);
      extensionClient.executionComplete(encryptedData);
    }
  });
</script>

Extensions that require other data than what is contained within the context provided to it, should utilise the Imagine Public API to access it.

The ExecutionComplete method can be supplied with information that the extension wants to store in Imagine. That data gets stored as Extension Data Entries in MS-Extension.

public class ExtensionDataEntry
{
    public int Id { get; set; }
    public string BasketId { get; set; }
    public int SalesTransactionId { get; set; }

    public int ExtensionId { get; set; }
    public string EventId { get; set; }
    public string ModuleAuthId { get; set; }
    public int Shop { get; set; }

    public JsonDocument Data { get; set; }
}

Extension Context

The context is generated by the Gateway upon triggering events by the UI.

Extension Context Data Model

See model here

export class Extension {
  id: number;
  name: string;
  url: string;
  moduleAuthId: string;
  eventId: string;
  encryptedExtension: string;
}
Property Example Value
id 1
name Extension 1
url https://extension.io/extension1
moduleAuthId Top
eventId OnBeginCheckout
encryptedExtension Extension metadata, aggregate of previous extension data entries, configuration JSON object as defined by Extension Configuration Model (see Extensions Data Model)

Decrypted Extension Example

{
  "id": 1,
  "name": "Extension 1",
  "url": "https://localhost:5001",
  "moduleAuthId": "Top",
  "eventId": "OnBeginCheckout",
  "configuration": {},
  "extensionDataEntries": [
    {
      "id": 1,
      "basketId": "0Ix0snGjKjNzswD-Ccr88vyaFORASO2ZKXbThzlEe8AURxty",
      "salesTransactionId": 0,
      "extensionId": 1,
      "eventId": "OnBeginCheckout",
      "moduleAuthId": "Top",
      "shop": 1,
      "data": {
        "timestamp": "2021-04-07T12:54:12.550Z"
      }
    }
  ]
}

Extension Data Entries

An extension can return any data it wants to Imagine in a serialised JSON string. That string must be encrypted with the pre-shared key that was generated upon the extension’s registration in Imagine.

See Security chapter below for further information

Should an extension provide any data when invoking ExecutionComplete, then that data it will be stored as an extension data entry. Each extension data entry has a reference to the app it got invoked by, a shop, a sales transaction, a basket (provided ms-basket is used for basket functionality), the event and the extension that the data originated from. Multiple extensions data entries can exist for each basket and sales transaction.

Extension Data Entries Model

class ExtensionDataEntry {
  basketId: string;
  extensionId: number;
  eventId: string;
  moduleAuthId: string;
  shop: number;
  encryptedData: string;
}
Property Example Value
basketId jSR_yiO7ZyCs3EuzcGZ2ZdSxBtS5ZowH_8Mi6mZilKfcyf7N
extensionId 1
eventId OnBeginCheckout
moduleAuthId Top
shop 1
encryptedData encrypted hash containing any JSON serialised data the extension would like to persist

Decrypted Extension Data Entry Model

{
  "id": 1,
  "basketId": "0Ix0snGjKjNzswD-Ccr88vyaFORASO2ZKXbThzlEe8AURxty",
  "salesTransactionId": 0,
  "extensionId": 1,
  "eventId": "OnBeginCheckout",
  "moduleAuthId": "Top",
  "shop": 1,
  "data": {
    "timestamp": "2021-04-07T12:54:12.550Z"
  }
}

Extension Subscription

Extensions subscriptions indicate what event an extension has been configured for. Multiple extensions can be configured for the same event. If there are more than one then they are triggered sequentially in ascending order specified by the Sequence No property.

Payment Extensions

Payment extensions are a special type of extensions which allow 3rd party developers to add integration to new payment providers for K3 Imagine apps.

A Payment Extension is also rendered in a Iframe by the Imagine frontend and communication is secured in the same manner as with “normal” extensions. The output provided from a Payment Extension to the invoking Imagine app is strictly defined as it must indicate whether payment transaction was successful or not.

Once a Payment Extension has been registered in Imagine merchant can select to enable them for their tenant by setting the defaultPaymentProvider tenant feature to PaymentExtension.

Design

Much like the "normal" extensions, Payment Extension are displayed in an Iframe. These extensions should then contain the implementation for interacting with a Payment Providers services. Payment Providers often provide Iframe integrations themselves, so this practise is quite common and allows us to bypass any PCI or other payment related certifications. Possible implementation of a Payment Extension could be one that processes the Extension Input from Imagine (fetches the basket and calculates the total sum) and initiates a payment transaction using a Payment Providers Iframe, within the Imagine Payment Extension Iframe.

Payment Extension Data Model

A JSON schema can be provided to describe configuration/parameters that need to be set for each Imagine tenant that uses the extension. When the merchant enables a Payment Extension, the Imagine Portal UI must autogenerate the configuration fields so that the merchant can input the configuration. This configuration could for example be the Merchant Id and Merchant Secret, necessary for the integration with the Payment Provider. This configuration is then provided as Extension input for when the extensions is triggered. See https://json-schema.org/learn/miscellaneous-examples.html for example use of JSON schema. Data Column Example Value ID 1 Name Nordic Payments Integration Description Adds support for Nordic Payments URL https://external.dev/nordicpayments

Communication Secret/PSK ABCD Configuration Schema JSON Schema Region Country code for the country where this extension is available for Payment Extension Logo https://external.dev/nordicpayments.png

Security

Communication between Imagine and the extensions is secured by signing all messages with a pre-shared key utilising an asymmetric encryption algorithm (AES-256). This is done for Extensions and Payment Extensions. When an extension gets triggered the Imagine backend encrypts the Extension input and config using the pre-shared key for that extension before responding with it to the Imagine frontend. The Imagine frontend then forwards it to the extension Iframe.

Symmetric Key Encryption

Symmetric key encryption will be used for ensuring that data going to and from an Extension has not been tampered with and to ensure that the data originates either from Imagine or the Extension itself. Symmetric key encryptions use the same key for both encrypting plaintext and decrypting ciphertext (the encrypted plaintext). The key is often referred to as a pre-shared key or a secret and in our use-case it will be an arbitrary string of a specific length. The pre-shared key is generated when an Extension is registered by the developer within Imagine. The Extension developer must safely store the generated pre-shared key and use it to encrypt any data intended for Imagine or decrypt data coming from Imagine. Vice versa applies for Imagine, meaning Imagine must use the pre-shared key to encrypt data it sends to the Extension and decrypt data coming from the Extension. See examples on how encryption and decryption can be implemented.

Imagine App showing basket

See examples of implementation for Symmetric Encryption and Symmetric Decryption from docs.microsoft.com Symmetric encryption algorithm of choice is AES-256 using GCM mode of operation (see for further info on AES GCM for .NET & Galois/Counter Mode).

Dependency on MS-Basket

MS-Basket is a microservice which moves the storing and handling of a customer’s basket from Imagines’ frontend to Imagines’ backend. The use-case for Extensions relies heavily on MS-Basket being implemented by the Self-Serve apps. Without the use of MS-Basket Imagine has no data on a customer’s basket until after a sale has been completed by the customer and therefore Extension Data Entries can only be linked to completed sale (Posted Sales Transaction) With the use of MS-Basket however a customer’s journey can be followed from start to finish. All Extension Data Entries can be linked to their basket during their browsing and checkout process. After completing a sale these entries can then be linked to the completed sale (Posted Sales Transaction). MS-Basket is currently being implemented into all Self-Serve apps by the Self-Serve team.

Readme

Keywords

none

Package Sidebar

Install

npm i @k3imagine/imagine-extension-framework

Weekly Downloads

0

Version

2.0.0

License

UNLICENSED

Unpacked Size

53.9 kB

Total Files

47

Last publish

Collaborators

  • k3mahendra
  • mattywarr
  • k3imagine.com
  • k3dave
  • sismith
  • robwestgeest
  • steven.galvin