Subaio Fintech Bridge for Android

This document provides informations of how to use the Subaio Fintech Bridge in your projects. There exists 3 Fintech Bridge libraries;

The libraries provides user interfaces, API integrations and other functionality related to Subaio. The purpose of the libraries is to reduce the implementation and maintenance cost the host applications, while enabling the Subaio application to evolve parallel with the host application. While there are slight differences between them because of differences in syntax and convention, the general structure is the same.

Architecture

The fintech bridge 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 BridgeManager and instantiating a BridgeView.

Each bridge project includes an example project demonstrating how to use the manager and the views.

Navigation

To better facilitate native transitions between pages, the subaio app is separated in pages that can be placed in separate view controllers and pushed separately to the nagivation stack.

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. new Page("OVERVIEW", null) or new Page("SUBSCRIPTION", new JSONObject(new HashMap() {{ put("subscriptionId", "..."); }})). The exact pages will depend on the application. For simple integrations where the views are simply started as new activities, the host application only need knowledge of the initial overview page, available as Page.overview() for convenience.

Views will push new pages using the onViewNavigated event.

Notifications for the embedded app should generally be implemented to pass name and params provided by subaio on to the host application to easily handle navigation. See the Push Notifications section for details.

Authentication

The subaio views use JSON web tokens for authentication. All views use a shared token, administred by the BridgeManager. The tokens generally expire after one hour, so the BridgeManager has internal logic to automatically refresh the token when necessary.

The preferred method of obtaining a subaio token is to let the host application make an authenticated request to its own backend, which will then request a token from the subaio backend.

The host application is responsible for providing a callback for obtaining a token to the BridgeManager. This callback will be passed a handler class, where exactly one of the following methods should be called:

Example implementation:

new BridgeManager.TokenHandler() {
    @Override
    public void onTokenRequired(String language, BridgeManager.TokenResultHandler resultHandler) {
        if (/* User is unauthenticated */) {
            resultHandler.unauthenticated();
        }
        try {
            String token = /* Fetch token from backend */;
            resulthandler.success(token);
        } catch (Exception e) {
            resultHandler.unavailable();
        }
    }
}

Besides providing the callback, the host application should invoke BridgeManager.getInstance().refreshToken() whenever the user is logged out or a new user is logged in.

See BridgeManager.TokenHandler.onTokenRequired.

Getting a BridgeManager

A singleton BridgeManager is automatically available as BridgeManager.getInstance(). Alternatively, they can be instantiated manually. If the shared instance is not used, the manager should be passed to the BridgeView constructor as the last argument.

Configuring the BridgeManager

The following options need to be provided to the BridgeManager before any views can be instantiated:

Optionally, the following may be set:

These can be set through a fluent interface by calling startConfiguration and ending with apply.

Example configuration (see app/src/main/java/com/subaio/example/LoadingActivity.java):

BridgeManager.getInstance()
    .startConfiguration()
    .setBaseUrl(baseUrl)
    .setLanguage(language)
    .setCustomConfig(customConfig)
    .setAllowExternalUrls(true)
    .setAllowShare(true)
    .setRequestHandler(new TrustKitRequestHandler(this /* Android context */))
    .setTokenHandler(...)
    .apply()

Instantiating a BridgeView

The BridgeView is a UIView that can be inserted in a controller. An example of a controller wrapping a navigation bar and a BridgeView can be found in app/src/main/java/com/subaio/example/SubaioActivity.java.

BridgeView can be added to an activity like a normal Layout. Setup requires setting a Page and a BridgeView.BridgeViewListener implementing event handlers like so:

BridgeView.setPage(page)
BridgeView.setListener(new BridgeView.BridgeViewListener() {
	...
}

The listener needs to handle the following events:

Optionally, the following events can be handled:

Push notifications

Subaio is able to generate several different notifications, e.g.

These notifications are sent from Subaio to the backend of the host project, which in turn decides how/if the notification is presented to the user.

The push notification contains information identifying the user, a user facing message, and a payload with details regarding which component should be shown if the notification is activated.

To reduce the amount of maintenance required by the host project, the library provides a method that can be parsed the notification payload which then is turned into a navigation event to reuse the logic implemented there.

SSL pinning

Configuration of SSL pinning is done through network_security_config.xml as a domain-config with pin-set. See app/src/main/res/xml/network_security_config.xml for example. Generally, subaio domains will be called <name>.<env>.subaio.com, with *.<env>.subaio.com sharing a single certificate.

Testing for external partners will generally be kept on *.qa.subaio.com and production will be run on *.prod.subaio.com. Note that some production services, e.g. error logging is still accessed by apps on *.qa.subaio.com, so *.prod.subaio.com should always be allowed.

Changelog

2.0.0

Naming (breaking)

The framework as well as a number of classes have been renamed. The initial implementation had the package called com.subaio.subaio and many classes prefixed with Subaio. The package has been renamed to com.subaio.fintechbridge. The following classes have been renamed:

Share and open URL events removed (breaking)

Events onViewDidShare and onViewDidOpenUrl from BridgeViewListener have been moved to internal handling. Since both can already be disabled during configuration, it seems unnecessary to require an implementation from library consumers, and it is unlikely that custom logic will be needed.

Configuration (breaking)

The SubaioConfiguration class has been removed entirely. The options normally passed to the SubaioConfiguration constructor can now be passed directly into the .configure call. No overloads exist for optional properties. To ease configuration, a fluent interface has been added, and it is recommended that this is used instead of directly calling .configure. To use it, start by calling startConfiguration and end by calling apply. Configuration must include calls to

Optionally, you can also call

Synchronicity

Previously, the configure-call was asynchronous. During the configuration, base-HTML and placeholder images were fetched, and an initial token was requested.

All this initialization has been moved to the creation of the first view. Calling configure is now entirely passive and synchronous, and can thus be done whenever convenient, as long as it is before the first view is created.

SSL pinning

To allow the usage of other libraries for authentication, TrustKit has been moved to the class TrustKitRequestHandler, which should be imported and passed to setRequestHandler during configuration. network_security_config.xml is still used to define the expected pins.

If another library than TrustKit is used for SSL pinning or to otherwise validate connections, the RequestHandler abstract class should be used.

If built-in handling of SSL is sufficient, NativeRequestHandler can be used. It has to be passed manually to ensure SSl pinning is not accidentally omitted.

Example of new configuration

BridgeManager.getInstance()
    .startConfiguration()
    .setBaseUrl(baseUrl)
    .setLanguage(language)
    .setCustomConfig(customConfig)
    .setAllowExternalUrls(true)
    .setAllowShare(true)
    .setRequestHandler(new TrustKitRequestHandler(this /* Android context */))
    .setTokenHandler(...)
    .apply()

Token management (breaking)

The signature of TokenHandler.onTokenRequired has been updated to better guide the implementation. Previously, the library consumer was responsible for setting the token to a value or null at the appropriate times. Now, a handler class makes handling specific cases more explicit. The new handler has three methods, exactly one of which should be called every time the function is invoked:

Example implementation:

new BridgeManager.TokenHandler() {
    @Override
    public void onTokenRequired(String language, BridgeManager.TokenResultHandler resultHandler) {
        if (/* User is unauthenticated */) {
            resultHandler.unauthenticated();
        }
        try {
            String token = /* Fetch token from backend */;
            resulthandler.success(token);
        } catch (Exception e) {
            resultHandler.unavailable();
        }
    }
}

The BridgeManager will no longer maintain a token while there are no active BridgeViews, nor will it automatically refresh the token before it expires. New tokens are only requested when:

Multiple manager instances (feature)

The BridgeManager constructor is now public, allowing manual administration of a single manager and allowing multiple instances to co-exist. Example:

// Using shared manager
BridgeManager.getInstance()
    .startConfiguration()
    ...
    .apply()
let view = new BridgeView(this, page: ...)

// Using own manager instance
let manager = new BridgeManager()
    .startConfiguration()
    ...
    .apply()
let view = new BridgeView(this, page: ..., manager)

Be aware that related views should share their manager. There is an initialization overhead when the first view is created for a manager, tokens are maintained and shared through the manager, and the views communicate to refresh stale data through the manager, so creating a new manager for every view is not a good idea. Multiple managers should only be used if multiple apps are embedded.

Prewarming cache (feature)

Previously, base-HTML and placeholder images were fetched on configuration. This has now been moved to first view creation. Since it will have no cached placeholder images, the very first BridgeView a device opens might experience a short flash of white. To avoid this, you can call prewarm on the BridgeManager at any point after configuration to ensure the cache is populated. Note that this is optional, and will have no effect if called simultaneously with view creation.

Custom actions and events (feature)

Support has been added for custom actions (sent to the embedded app) and events (sent from the embedded app). They generally consist of a name and some data, both of which should be agreed upon between the host and embedded apps.

Example of a custom event being used for navigation in the host application:

new BridgeView.BridgeViewListener() {
    ...

    @Override
    public void onViewCustomEvent(BridgeView view, String name, Object data) {
        switch (name) {
            case "navigateExternal":
                try {
                    JSONObject dictionary = (JSONObject) data;
                    String viewName = dictionary.getString("name");
                    JSONObject params = dictionary.getJSONObject("params");
                    Log.d("FintechBridge", "Go to native " + viewName + ' ' + params);
                } catch (Exception e) {
                    // Log problem
                }
                break;
            default:
                Log.d("FintechBridge", "customEvent: " + name + ' ' + data);
                break;
         }
    }

    ...
}

Example of a custom action being used to control internal scroll:

bridgeView.sendCustomAction("scroll", "top")

Optional events (feature)

Previously, BridgeViewListener was an interface, requiring all events to be implemented. It has now been changed to an abstract class where only necessary events must be handled.

Events that require implementation:

Events that are optional:

Caching (internal)

Previously, SharedPreferences was used as a cache. This has now been moved to the cache directory. The previously set preferences should be automatically cleaned up the first time the new bridge runs.

1.1.0

Custom configuration (feature)

It is now possible to inject values into views using the customConfig API. customConfig can be provided during configuration or by calling setCustomConfig on the SubaioManager. The value should be a Map<String, String>.

Possible uses are dynamic theming, dark/light mode and user information that is more easily accessed from the host app than through the embedded app's backend.

Names and possible values should be agreed upon between host and embedded app.

1.0.0

Initial stable release

Package com.subaio.fintechbridge