Javascript SDK Integration

Introduction

The Fintech Bridge SDK is intended for embedding small, widget-like applications inside a larger app. The application is structured in the following parts:

Including embedded applications thus requires including the bridge library, configuring the SubaioManager and instantiating a SubaioView.

Prerequisites

Installation

The web SDK is versioned using semver 2.0.0, prefixed with v, as is NPM standard. Thus, patch and minor versions should require no changes. We recommend always using the latest version if possible.

Using NPM/yarn through NPM mirror

Run either of the following commands:

npm install --save @subaio/fintech-bridge-web
yarn add @subaio/fintech-bridge-web

Using NPM/yarn with local tarball

Run either of the following commands:

npm install --save ./path/to/file/fintech-bridge-web-<version>.tgz
yarn add file:./path/to/file/fintech-bridge-web-<version>.tgz

Manually adding local tarball

Extract the package-folder from the tarball. In the dist folder, the following files are found:

Copy the relevant files to your own project.

Setup SubaioManager

The SubaioManager is a singleton class used to configure shared settings and manage authentication. Before initializing any views, the manager needs these configuration settings. The sections below outline the purpose and options of the different sections. To get started, the following should be run during app start:

SubaioManager.shared.configure({
	baseUrl: '<app-url>',
	language: 'en',
	onTokenRequired: async (language) => {
		if (/* user is logged out */) {
            return null
        }
        /* fetch subaio-token */
        if (/* token is available */) {
            return token
        } else {
            // A network outage or similar prevents fetching, retry later
            throw new Error('Something went wrong')
        }
	},
})

Authentication

The SubaioViews use JSON web tokens for authentication. All views use a shared token, administred by the SubaioManager, fetched through the onTokenRequired callback. The tokens generally expire after one hour, so the SubaioManager has internal logic to automatically refresh the token when necessary.

Getting a Subaio-token should be handled through your own backend, to ensure your own user security is observed.

The onTokenRequired callback must handle three cases:

The onTokenRequired callback can function with both promises and callbacks. The example above uses async/await.

Example using the Promise-constructor:

onTokenRequired: (language) => new Promise((resolve, reject) => {
    if (/* user is logged out */) {
        resolve(null)
        return
    }
    /* fetch subaio-token */
    if (/* token is available */) {
        resolve(token)
    } else {
        // Any error thrown will assume there's a temporary problem and retry
        // after 1 minute
        reject(new Error('Something went wrong'))
    }
}),

Example using node-style callback (callback(error, result)):

onTokenRequired: function (language, callback) {
    if (/* user is logged out */) {
        callback(null, null)
        return
    }
    /* fetch subaio-token */
    if (/* token is available */) {
        callback(null, token)
    } else {
        // Any error thrown will assume there's a temporary problem and retry
        // after 1 minute
        callback(new Error('Something went wrong'), null)
    }
},

Besides providing the callback, you should invoke SubaioManager.shared.refreshToken when the current user is unauthenticated or a new user is authenticated, to force an update of the token.

As a convenience, the current language is passed to onTokenRequired, since this should be passed along to the subaio backend during authentication. If the language is not set, a token is not requested. If the language is changed, a new token is automatically requested.

While the authentication flow through your backend is setup, you can use a long-lived hardcoded token provided by Subaio for the integration environment.

Language

Language is defined using a BCP 47 language tag, such as en or da-DK. The current language can be changed at any time, immediately to be reflected in all open views. The fallback language when an unknown language is english, but can be changed if necessary.

Since the language is used as part of the token request, a token isn’t requested before a language has been provided, which prohibits any views from loading.

Custom configuration (optional)

If necessary, custom configuration values can be sent to the webview. This is useful if the app allows for in-app theme changes or if user information needs to be shared.

Note that custom configuration is only necessary for dynamic theming that can only be known at runtime. General theming should be decided as part of the design process.

customConfig is added to the SubaioConfiguration as an optional string-dictionary:

SubaioManager.shared.configure({
    ...
    customConfig: {
        name1: 'value1',
    },
    ...
})

Custom Promise implementation

If a custom Promise library is used, such as BlueBird or Q, this can be passed to configure as promiseLib. This will default to the native browser implementation, if available.

Multiple SubaioManager instances

If it makes sense to eschew the singleton-pattern, SubaioManager-instances can be created freely to be used instead of SubaioManager.shared. To use a non-singleton SubaioManager-instance with a SubaioView, simply pass a managerInstance to the constructor options-object.

If multiple SubaioManager instances are created, it is recommended to dispose of them by calling manager.cleanup() to stop all token refresh logic.

Create a SubaioView

SubaioView is used to inject an iframe into the page and handle communication.

How a view should be created is largely dependant on the underlying framework. SubaioView is an ES6 class. As part of its constructor, it should be passed a DOM-element (likely a div), as well as a number of event handlers. It will then inject an iframe into the element, with styling to take fill the element, and start sending events back and forth.

Example:

const view = new SubaioView({
    element, // A DOM element
    page, // An initial page or null (see Navigation section)
    eventHandlers: {
        onNavigateTo: function (page) {
            // Handle navigation (see Navigation section)
        },
        onBack: function (view) {
            // Handle navigation (see Navigation section)
        },
        onStatusChange: function (status) {
            // Optionally handle different states
            //     'initializing' while iframe is loading content
            //     'empty' when loaded and no page has been set
            //     'loading' while specific page is loading content
            //     'running' when everything successfully has loaded
            //     'errored' if an error occurs
            // If a custom spinner is used, show this instead of `element`
            // unless status is 'running' or 'errored'
            // If a custom error page is always used, show this instead of
            // `element` on 'errored' (see onError event)
        },
        onTitleChange: function (title) {
            // Optionally receive a localized title. This can for instance be used for
            // setting a navigation-header or `document.title`.
        },
        onError: function (type) {
            // Handle specific errors
            //     'load' when iframe fails to initialize. This should *always*
            //         be handled by the host-application, since the view cannot show
            //         anything.
            //     'internal' when an error occurs in the running view. The view
            //         will always show a custom error message here, so this can
            //         be safely ignored.
        },
    },
})

A SubaioView uses global event handlers, so take care to call view.cleanup() to remove the iframe-element and all eventhandlers before removing the DOM-node.

To better facilitate a deep integration, the subaio app is separated in pages that can be injected whereever necessary in the host app.

Each instance of an embedded view can be set to show a specific Page, defined by a name and optionally a set of parameters, e.g. { name: 'OVERVIEW', params: nil } or { name: 'SUBSCRIPTION', params: { serviceId: '...' } }. The exact pages will depend on the application. Different strategies for navigation is discussed in the following sections. The navigation strategy should be decided up front, depending on the host app.

Views will push new pages using the onNavigateTo event. Either a new view can be created, or the page-property of a view can be assigned.

Notifications for the embedded app should generally be implemented to pass Page-objects provided by Subaio on to the host application to easily handle navigation.

URL-based Navigation Strategy

When embedding on web, Pages can be decorated with a special url-parameter. This parameter will be present on all Pages sent from the SubaioView and from notifications. The value is a relative URL that can be appended to a subscription-path. Instead of providing a specific Page, a special Page{ name: 'UNKNOWN', params: { url: '...' } } – can be used.

The host app router should then be set to route all subscription-related URLs to a SubaioView and pass the URL-fragment.

As an example, take a host application that places all subscriptions on /subscriptions/. When navigating here, a SubaioView is created with the page { name: 'UNKNOWN', params: { url: '' } }. The onNavigateTo event is invoked with the page { name: 'CANCELLATION', params: { serviceId: '1234', url: '1234/cancel/' }}. The host application then navigates to /subscriptions/1234/cancel/, and sets view.page to { name: 'UNKNOWN', params: { url: '1234/cancel/' } }.

Note that it is a performance boost to reuse the same SubaioView if possible, for as long as Subaio-content is shown, since it avoids the overhead of creating a new iframe on each navigation.

Deeply Integrated Navigation Strategy

If the host application places content in vastly different places that is hard to replicate inside an iframe, this strategy can be necessary. An example is a host app where list-views are shown centered below a static header, but detail-views are opened in a sidebar overlapping the header. This strategy requires the host application to be very aware of the different pages of the embedded app, and is thus not recommended unless necessary.

In this case, a list of views and their placement should be agreed upon as part of the design-process, and the host application should handle them depending on the page name.

Internal Navigation Strategy

A very simple solution to navigation is to let the SubaioView handle all navigation internally. This makes the host-application implementation incredibly easy, since null can simply be passed instead of a specific Page. Partial history-API support is even possible. However, if the iframe is ever recreated, e.g. from navigating to a non-Subaio view and back or from reloading the page, all history will be lost. The URL will also not reflect the current page. Because of these limitations, URL-based navigation is recommended instead.

Layout

The injected iframe defaults to 100% height, width, max-width and max-height, and flex 1 1 auto. To should allow it to completely fill the containing element no matter the styles applied. Sizing should thus be accomplished on the containing element.

In some design cases, it makes sense to let the iframe dictate its own height, such as when embedding in the middle of other content, or when the host apps scroll-behavior needs full height content.

In this case, the containing element should be given the expected initial height (based on design). When initializing the SubaioView, set enableResize: true and pass an onResize event-handler. This will be invoked every time the content size changes. Note that this disables scrollbars to avoid jumps between content resize and container element resize. The content of the SubaioView should never overflow on the x-axis of any realistic screen-size.