Get started with Android

Use our SDK for Android apps

Get started with Android
1. Connect SDK to Your Project

In your app's build.gradle add this:

android {

...

// add these lines to your gradle file

dataBinding {

enabled = true

}

compileOptions {

sourceCompatibility 1.8

targetCompatibility 1.8

}

}

dependencies {

implementation 'com.okaythis.sdk:psa:1.1.0'

}

Add this to your project's build.gradle file:

allprojects {

repositories {

google()

jcenter()

maven {

url https://dl.bintray.com/okaythis/maven

}

}

}

2. Init PSA SDK

In order for Okay SDk to work correctly we will need to sync the SDK with PSS. Initialization of the PSA should be done within our Application class, inside the onCreate() method.

We use the PsaManager class from Okay to initialize our PSA. We will be using two methods from the PsaManager class, the init() and setPssAddress() methods. The init() and setPssAddress() method from the PsaManager class has the following structure

PsaManager psaManager = PsaManager.init(this, T extends ExceptionLogger);

psaManager.setPssAddress(PSS_SERVER_ENDPOINT);

A typical illustration of how our Application class should be

class OkayDemoApplication: Application { @Override fun onCreate() { super.onCreate(); // PsaManager.init(this, T extends ExceptionLogger); // The second argument that is being passed to PsaManager.init() method, must implement the ExceptionLogger interface. So will be creating our exception logger called OkayDemoLogger which implements that interface (this class could use any crash logger we choose like Crashlytics). val psaManager = PsaManager.init(this, new OkayDemoLogger()); psaManager.setPssAddress("http://protdemo.demohoster.com"); } }

This is what my OkayDemoLogger class looks like

class OkayDemoLogger: ExceptionLogger { override fun setUserIdentificator(p0: String?) { Log.e("SET ID: ", "Successfully set user identificator $p0 ") } override fun exception(p0: String?, p1: Exception?) { Log.e("Exception: ", "Okay Error $p0 -- Exception: $p1") } }

We will need to add our application class to our manifest file like so.

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.ogieben.okaydemo"> <application android:name=".OkayDemoApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> ... </application>

The Okay SDK requires certain kinds of permissions to work properly. We will have to ask the users to grant these permissions before we proceed. We can easily create these helper methods to handle permission resolution for us.

// PermissionHelper.kt class PermissionHelper(private val activity: Activity) { val REQUEST_CODE = 204 fun hasPermissions(ctx: Context, permission: Array<String>): Boolean = permission.all { ActivityCompat.checkSelfPermission(ctx, it) == PackageManager.PERMISSION_GRANTED } fun requestPermissions(permission: Array<String>) = ActivityCompat.requestPermissions(activity, permission, REQUEST_CODE) }

The Okay SDK comes with a prepacked method PsaManager.getRequiredPermissions() that helps us fetch an array of all required permissions.

// MainActivity.kt val permissionHelper = PermissionHelper(activity) private fun checkPermissions() { // prepacked method val requiredPermissions = PsaManager.getRequiredPermissions() if (!permissionHelper.hasPermissions(this, requiredPermissions)) { permissionHelper.requestPermissions(requiredPermissions) } }

We can now use the checkPermission() method within our MainActivity.kt's onCreate method to request for all permission.

// MainActivity.kt override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) checkPermissions() }

We will need our device token from Firebase to be able to finish our enrollment.

If we have firebase successfully setup, we could request for our device token using the code sample below. If you have not been able to setup Firebase please refer to this documentaion as Okay requires this service to work correctly.

// MainActivity.kt private var preferenceRepo: PreferenceRepo = PreferenceRepo(context) private fun fetchInstanceId () { FirebaseInstanceId.getInstance().instanceId .addOnCompleteListener(OnCompleteListener { task -> if (!task.isSuccessful) { Log.w("", "getInstanceId failed", task.exception) Toast.makeText(this@MainActivity, "Error could not fetch token", Toast.LENGTH_LONG).show() return@OnCompleteListener } val token = task.result?.token // save token to SharedPreference storage for easy retrieval preferenceRepo.persistAppPns(token.toString()) }) }

If all is good we can now invoke this method from our onCreate() method within our activity like so.


class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) // request permissions checkPermissions() // fetch token fetchInstanceId() } }

We can now proceed with our enrollment if all permissions has been granted and our appPns(also know as Firebase token) has been retrieved successfully.

private fun beginEnrollment() { val appPns = preferenceRepo.getAppPns() // retrieve Firebase token from SharedPreference storage val pubPssB64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxgyacF1NNWTA6rzCrtK60se9fVpTPe3HiDjHB7MybJvNdJZIgZbE9k3gQ6cdEYgTOSG823hkJCVHZrcf0/AK7G8Xf/rjhWxccOEXFTg4TQwmhbwys+sY/DmGR8nytlNVbha1DV/qOGcqAkmn9SrqW76KK+EdQFpbiOzw7RRWZuizwY3BqRfQRokr0UBJrJrizbT9ZxiVqGBwUDBQrSpsj3RUuoj90py1E88ExyaHui+jbXNITaPBUFJjbas5OOnSLVz6GrBPOD+x0HozAoYuBdoztPRxpjoNIYvgJ72wZ3kOAVPAFb48UROL7sqK2P/jwhdd02p/MDBZpMl/+BG+qQIDAQAB" val installationId = "9990" val spaEnroll = SpaEnrollData(appPns, pubPssB64, installationId, null, PsaType.OKAY) PsaManager.startEnrollmentActivity(this, spaEnroll) }

If the beginErollment() method was called successfully we will need a way to retrieve information from the enrollment Activity. So will override the onActivityResult() method within our MainActivity.kt class.

// MainActivity.kt override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == PsaConstants.ACTIVITY_REQUEST_CODE_PSA_ENROLL) { if (resultCode == RESULT_OK) { //We should save data from Enrollment result, for future usage data?.run { val resultData = PsaIntentUtils.enrollResultFromIntent(this) resultData.let { preferenceRepo.saveExternalID(it.externalId) } Toast.makeText(applicationContext, "Successfully got this externalId " + resultData.externalId, Toast.LENGTH_SHORT).show() } } else { Toast.makeText(this, "Error Retrieving intent after enrollment", Toast.LENGTH_SHORT).show() } } }

3. Permissions Request

At first your should request the required permissions from the System.

You can receive a collection of the required permissions by calling:

PsaManager.getRequiredPermissions()

The Okay SDK requires certain kinds of permissions to work properly. We will have to ask the users to grant these permissions before we proceed. We can easily create these helper methods to handle permission resolution for us.

// PermissionHelper.kt class PermissionHelper(private val activity: Activity) { val REQUEST_CODE = 204 fun hasPermissions(ctx: Context, permission: Array<String>): Boolean = permission.all { ActivityCompat.checkSelfPermission(ctx, it) == PackageManager.PERMISSION_GRANTED } fun requestPermissions(permission: Array<String>) = ActivityCompat.requestPermissions(activity, permission, REQUEST_CODE) }

The Okay SDK comes with a prepacked method PsaManager.getRequiredPermissions() that helps us fetch an array of all required permissions.

// MainActivity.kt val permissionHelper = PermissionHelper(activity) private fun checkPermissions() { // prepacked method val requiredPermissions = PsaManager.getRequiredPermissions() if (!permissionHelper.hasPermissions(this, requiredPermissions)) { permissionHelper.requestPermissions(requiredPermissions) } }

We can now use the checkPermission() method within our MainActivity.kt's onCreate method to request for all permission.

// MainActivity.kt override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) checkPermissions() }

We will need our device token from Firebase to be able to finish our enrolment.

If we have firebase successfully setup, we could request for our device token using the code sample below. If you have note been able to setup Firebase please refer to this documentation as Okay requires this service to work correctly.

// MainActivity.kt private var preferenceRepo: PreferenceRepo = PreferenceRepo(context) private fun fetchInstanceId () { FirebaseInstanceId.getInstance().instanceId .addOnCompleteListener(OnCompleteListener { task -> if (!task.isSuccessful) { Log.w("", "getInstanceId failed", task.exception) Toast.makeText(this@MainActivity, "Error could not fetch token", Toast.LENGTH_LONG).show() return@OnCompleteListener } val token = task.result?.token // save token to SharedPreference storage for easy retrieval preferenceRepo.persistAppPns(token.toString()) }) }

If all is good we can now invoke this method from our onCreate() method within our activity like so.

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) // request permissions checkPermissions() // fetch token fetchInstanceId() } }

We can now proceed with our enrolment if all permissions has been granted and our appPns(also know as Firebase token) has been retrieved successfully.

private fun beginEnrollment() { val appPns = preferenceRepo.getAppPns() // retrieve Firebase token from SharedPreference storage val pubPssB64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxgyacF1NNWTA6rzCrtK60se9fVpTPe3HiDjHB7MybJvNdJZIgZbE9k3gQ6cdEYgTOSG823hkJCVHZrcf0/AK7G8Xf/rjhWxccOEXFTg4TQwmhbwys+sY/DmGR8nytlNVbha1DV/qOGcqAkmn9SrqW76KK+EdQFpbiOzw7RRWZuizwY3BqRfQRokr0UBJrJrizbT9ZxiVqGBwUDBQrSpsj3RUuoj90py1E88ExyaHui+jbXNITaPBUFJjbas5OOnSLVz6GrBPOD+x0HozAoYuBdoztPRxpjoNIYvgJ72wZ3kOAVPAFb48UROL7sqK2P/jwhdd02p/MDBZpMl/+BG+qQIDAQAB" val installationId = "9990" val spaEnroll = SpaEnrollData(appPns, pubPssB64, installationId, null, PsaType.OKAY) PsaManager.startEnrollmentActivity(this, spaEnroll) }

If the beginErollment() method was called successfully we will need a way to retrieve information from the enrollment Activity. So will override the onActivityResult() method within our MainActivity.kt class.

// MainActivity.kt override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == PsaConstants.ACTIVITY_REQUEST_CODE_PSA_ENROLL) { if (resultCode == RESULT_OK) { //We should save data from Enrollment result, for future usage data?.run { val resultData = PsaIntentUtils.enrollResultFromIntent(this) resultData.let { preferenceRepo.saveExternalID(it.externalId) } Toast.makeText(applicationContext, "Successfully got this externalId " + resultData.externalId, Toast.LENGTH_SHORT).show() } } else { Toast.makeText(this, "Error Retrieving intent after enrollment", Toast.LENGTH_SHORT).show() } } }

4. Push Notification

In your app, you should configre the receiver for the Firebase Cloud Messages.

You should save Firebase instanceId for later usage - that will be your appPNS.

PSS will send push notifications to your app when User authorization scenario would be launched. PSS must be properly configured to send push notifications.

We will also need to set up Firebase for our project. If you are not familiar with integrating Firebase messaging please check this documentaion for more information as Okay SDK depends on it.

dependencies {

implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

implementation 'androidx.appcompat:appcompat:1.1.0'

implementation 'androidx.core:core-ktx:1.1.0'

implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

implementation 'com.google.android.material:material:1.0.0'

testImplementation 'junit:junit:4.12'

androidTestImplementation 'androidx.test:runner:1.2.0'

androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

// Okay dependency

implementation 'com.okaythis.sdk:psa:1.1.0'

// Firebase dependency

implementation 'com.google.firebase:firebase-messaging:20.0.0'

}

We can now sync our app's gradle file to build.

5. Enroll procedure

We will begin by creating a new Android project on Android Studio.

Before we begin enrollment with Okay on our Android app, we will need to add Okay as a dependency to our app level build.gradle file (i.e app\build.gradle)

... dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.core:core-ktx:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'com.google.android.material:material:1.0.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' // Okay dependency implementation 'com.okaythis.sdk:psa:1.1.0' }

After successfully being granted permissions, you should invoke the enroll procedure by:


PsaManager.startEnrollmentActivity(activity Activity, data SpaEnrollData);

activity - Your activity that will receive enrollment result onActivityResult.

SpaEnrollData contains:

  • String appPns - Firebase instanceId
  • String pubPss - Should be the same on both app and server.
  • String installationId - Should be the same on both app and server.
  • PageTheme pageTheme - Can be used for the Authorization UI customization Documentation (pass null if you don't need UI customization)
  • PsaType psaType - Use PsaType.OKAY

pubPss and installationId - You can take there

And after successful enrollment - save enrollment result data. Use PsaIntentUtils.enrollResultFromIntent to extract enrollment result from Intent. For example:


if (requestCode == PsaConstants.ACTIVITY_REQUEST_CODE_PSA_ENROLL && resultCode == Activity.RESULT_OK) { PsaEnrollResultData responseData = PsaIntentUtils.enrollResultFromIntent(data); // Now save **externalID** and **enrollmentID** for future usage yourPreferencesStorage.putEnrollmentId(data.getEnrollmentId()); yourPreferencesStorage.putExternalId(data.getExternalId()); // you can navigate user to your app } else { // show error and/or try enroll again }

6. Push Notification Parsing

From FCM you can receive push notification as a RemoteMessage object.

From this remoteMessage you can extract notificationData:

Map<String, String> notificationData = remoteMessage.getData(); String type = notificationData.get("type"); String data = notificationData.get("data");

If "type" equals 10 then it is a "WakeUpNotification" and it's data JSON containing two fields:


Long tenantId;
Long sessionId;
7. Authorization

Now you can start authorization request to the SDK by two ways:

7.1 With activity

PsaManager.startAuthorizationActivity(Activity activity, SpaAuthorizationData authorizationData)

In which you will receive:

activity.onActivityResult()

for example:

if (requestCode == PsaConstants.ACTIVITY_REQUEST_CODE_PSA_AUTHORIZATION) { if (resultCode == Activity.RESULT_OK || resultCode == AuthorizationActivity.RESULT_OK_CONSUMED_PUSH) { // Show Success message to User } else { PsaErrorData psaErrorData = PsaIntentUtils.psaErrorDataFromIntent(intent); String psaErrorMessage = TextUtils.isEmpty(psaError.getMessage()) ? psaError.getStatus().toString() : psaError.getMessage(); // Show psaErrorMessage to User. } }

or by LocalBroadcastReceiver with:

intent filter action = PsaConstants.ACTION_AUTHORIZATION_RESULT_EVENT

7.2 With context

PsaManager.startAuthorizationActivity(Context context, SpaAuthorizationData authorizationData)

In which you will receive results only from LocalBroadcastReceiver

SpaAuthorizationData

When you create SpaAuthorizationData you should provide:

  • Long sessionId - From the push notification.
  • String appPNS - Firebase instanceID
  • PageTheme pageTheme - Can be used for the Authorization UI customization Documentation (pass null if you don't need UI customization)
  • PsaType psaType - Use PsaType.OKAY
8. PageTheme

PageTheme is a class (provided by Okay SDK) that describes style of authorization screens. If you want customize authorithation screen provided by Okay SDK - you should create PageTheme object, configure it fields. And use this object, as field of SpaAuthorizationData, when you are calling:


PsaManager.startAuthorizationActivity(Activity activity, SpaAuthorizationData authorizationData)

Android

public class PageTheme implements Parcelable { private Bundle bundle = new Bundle(); private static final String ACTION_BAR_BACKGROUND_COLOR = "actionBarBackgroundColor"; private static final String ACTION_BAR_TEXT_COLOR = "actionBarTextColor"; private static final String BUTTON_TEXT_COLOR = "buttonTextColor"; private static final String BUTTON_BACKGROUND_COLOR = "buttonBackgroundColor"; private static final String SCREEN_BACKGROUND_COLOR = "screenBackgroundColor"; private static final String NAME_TEXT_COLOR = "nameTextColor"; private static final String TITLE_TEXT_COLOR = "titleTextColor"; private static final String MESSAGE_TEXT_COLOR = "messageTextColor"; private static final String ACTION_BAR_LOGO_PATH = "actionBarLogoPath"; private static final String ACTION_BAR_TITLE = "actionBarTitle"; private static final String INPUT_TEXT_COLOR = "inputTextColor"; private static final String INPUT_SELECTION_COLOR = "inputSelectionColor"; private static final String INPUT_ERROR_COLOR = "inputErrorColor"; private static final String INPUT_DEFAULT_COLOR = "inputDefaultColor"; private static final String QUESTION_MARK_COLOR = "questionMarkColor"; private static final String TRANSACTION_TYPE_TEXT_COLOR = "transactionTypeTextColor"; private static final String INFO_SECTION_TITLE_COLOR = "infoSectionTitleColor"; private static final String INFO_SECTION_VALUE_COLOR = "infoSectionValueColor"; private static final String FROM_TEXT_COLOR = "fromTextColor"; private static final String AUTH_INFO_BACKGROUND_COLOR = "authInfoBackgroundColor"; private static final String SHOW_DETAILS_TEXT_COLOR = "showDetailsTextColor"; private static final String CONFIRM_BUTTON_BACKGROUND_COLOR = "confirmButtonBackgroundColor"; private static final String CONFIRM_BUTTON_TEXT_COLOR = "confirmButtonTextColor"; private static final String CANCEL_BUTTON_BACKGROUND_COLOR = "cancelButtonBackgroundColor"; private static final String CANCEL_BUTTON_TEXT_COLOR = "cancelButtonTextColor"; private static final String AUTH_CONFIRMATION_BACKGROUND_COLOR = "authConfirmationBackgroundColor"; private static final String AUTH_CONFIRMATION_TITLE_COLOR = "authConfirmationTitleColor"; private static final String AUTH_CONFIRMATION_MESSAGE_COLOR = "authConfirmationMessageColor"; private static final String AUTH_CONFIRMATION_THUMB_COLOR = "authConfirmationThumbColor"; private static final String AUTH_CONFIRMATION_APOSTROPHE_COLOR = "authConfirmationApostropheColor"; private static final String AUTH_CONFIRMATION_BUTTON_BACKGROUND_COLOR = "authConfirmationButtonBackgroundColor"; private static final String AUTH_CONFIRMATION_BUTTON_TEXT_COLOR = "authConfirmationButtonTextColor"; private static final String AUTH_CANCELLATION_BACKGROUND_COLOR = "authCancellationBackgroundColor"; private static final String AUTH_CANCELLATION_TITLE_COLOR = "authCancellationTitleColor"; private static final String AUTH_CANCELLATION_MESSAGE_COLOR = "authCancellationMessageColor"; private static final String AUTH_CANCELLATION_THUMB_COLOR = "authCancellationThumbColor"; private static final String AUTH_CANCELLATION_APOSTROPHE_COLOR = "authCancellationApostropheColor"; private static final String AUTH_CANCELLATION_BUTTON_BACKGROUND_COLOR = "authCancellationButtonBackgroundColor"; private static final String AUTH_CANCELLATION_BUTTON_TEXT_COLOR = "authCancellationButtonTextColor"; private static final String PIN_TITLE_TEXT_COLOR = "pinTitleTextColor"; private static final String PIN_VALUE_TEXT_COLOR = "pinValueTextColor"; private static final String PIN_NUMBER_BUTTON_TEXT_COLOR = "pinNumberButtonTextColor"; private static final String PIN_NUMBER_BUTTON_BACKGROUND_COLOR = "pinNumberButtonBackgroundColor"; private static final String PIN_REMOVE_BUTTON_TEXT_COLOR = "pinRemoveButtonTextColor"; private static final String PIN_REMOVE_BUTTON_BACKGROUND_COLOR = "pinRemoveButtonBackgroundColor"; // getters and setters }

Explanation of fields:

Next pictures will help you to understand which key you need

9. Client Settings
9.1 AuthData Types

The following types of user answers that may come from the mobile application:

  • Code:

    • 101

      • Value:

        • CANCEL

    • 102

      • Value:

        • PIN

    • 103

      • Value:

        • OK

9.2 Mobile Client Settings

To use the Okay SDK, you need installationId and pubPss key. They are issued for every version.

Android:
PSMP Version: 1.0.3
Installation: 9990

Public PSS Keys

Note: Keys are encoded in PKCS#1 format.


Installation 9980

-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAxgyacF1NNWTA6rzCrtK60se9fVpTPe3HiDjHB7MybJvNdJZIgZbE 9k3gQ6cdEYgTOSG823hkJCVHZrcf0/AK7G8Xf/rjhWxccOEXFTg4TQwmhbwys+sY /DmGR8nytlNVbha1DV/qOGcqAkmn9SrqW76KK+EdQFpbiOzw7RRWZuizwY3BqRfQ Rokr0UBJrJrizbT9ZxiVqGBwUDBQrSpsj3RUuoj90py1E88ExyaHui+jbXNITaPB UFJjbas5OOnSLVz6GrBPOD+x0HozAoYuBdoztPRxpjoNIYvgJ72wZ3kOAVPAFb48 UROL7sqK2P/jwhdd02p/MDBZpMl/+BG+qQIDAQAB -----END RSA PUBLIC KEY-----

Installation 9990

-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAxgyacF1NNWTA6rzCrtK60se9fVpTPe3HiDjHB7MybJvNdJZIgZbE
9k3gQ6cdEYgTOSG823hkJCVHZrcf0/AK7G8Xf/rjhWxccOEXFTg4TQwmhbwys+sY
/DmGR8nytlNVbha1DV/qOGcqAkmn9SrqW76KK+EdQFpbiOzw7RRWZuizwY3BqRfQ
Rokr0UBJrJrizbT9ZxiVqGBwUDBQrSpsj3RUuoj90py1E88ExyaHui+jbXNITaPB
UFJjbas5OOnSLVz6GrBPOD+x0HozAoYuBdoztPRxpjoNIYvgJ72wZ3kOAVPAFb48
UROL7sqK2P/jwhdd02p/MDBZpMl/+BG+qQIDAQAB
-----END RSA PUBLIC KEY-----
9.3 Maven Artifacts Repository

While using bintray to distribute artifacts, use the following settings to add dependencies in your project.

Firstly, add the bintray repository to the repositories section in pom.xml:

<repositories> <repository> <snapshots> <enabled>false</enabled> </snapshots> <id>bintray-okaythis-maven</id> <name>bintray</name> <url>https://dl.bintray.com/okaythis/maven</url> </repository> </repositories>

Then add the multi-tenant-gateway-client dependency:

<dependency> <groupId>com.protectoria.pss</groupId> <artifactId>multi-tenant-gateway-client</artifactId> <version>1.0.3</version> </dependency>

The latest version is 1.0.3

10. Linking a user with Okay SDK

In order to successfully finish the initialization stage we need to link the user with Okay. This allows us to authorize/authenticate a particular user's action.`

Note: This section of the Okay SDK requires interaction with a dedicated server that sits as a middleman between PSS and PSA. We highly recommend that you have this server built/running while going through this section of the documentation. However, if you do not have a server you can just generate your linking codes from this url http://protdemo.demohoster.com/tenant-fb/ then sign in with "tenant" as your username and "password" as your password.`

To enable linking on the your app you will need to add this line of code to your app's Application file in my case it is going to be OkayDemoApplication.kt.

class OkayDemoApplication: Application() {

override fun onCreate() {

super.onCreate()

initPsa()

// Added this method call

initGatewayServer()

}

private fun initPsa() {

val psaManager = PsaManager.init(this, OkayDemoLogger())

psaManager.setPssAddress("http://protdemo.demohoster.com")

}

// Added this method

private fun initGatewayServer() {

GatewayRestServer.init(PsaGsonFactory().create(), "http://protdemo.demohoster.com/gateway/")

}

}

We will send a request to our server to start the linking process. We will be sending the  generated from Okay SDK as a parameter to our server. If our request was processed successfully we will recieve a response with the linkingCode required to complete the linking. The linkCode is a six digit number generated for this purpose.

We make a very simple request to our server to initiate the linking process using retrofit like so.

//MainActivity.kt class MainActivity : AppCompatActivity() { private lateinit var preferenceRepo: PreferenceRepo private val permissionHelper = PermissionHelper(this) private val retrofitWrapper = RetrofitWrapper() private val transactionHandler = retrofitWrapper.handleTransactionEndpoints() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) preferenceRepo = PreferenceRepo(this) checkPermissions() fetchInstanceId() handleIntent(intent) enrollmentButton.setOnClickListener { view -> beginEnrollment() } linkingButton.setOnClickListener{ startServerLinking(preferenceRepo.externalId) } } ... private fun linkUser(linkingCode: String) { val psaManager = PsaManager.getInstance() val linkingScenarioListener: LinkingScenarioListener = object: LinkingScenarioListener{ override fun onLinkingCompletedSuccessful(var1: Long, var3: String){ Toast.makeText(this@MainActivity, "Linking Successful", Toast.LENGTH_LONG).show() } override fun onLinkingFailed(var1: ApplicationState) { Toast.makeText(this@MainActivity, "Linking not Successful: linkingCode: ${linkingCodeEditText.text} errorCode: ${var1.code} ", Toast.LENGTH_LONG).show() } } psaManager.linkTenant(linkingCode, preferenceRepo, linkingScenarioListener) } ... private fun startServerLinking(userExternalId: String?) { transactionHandler.linkUser(userExternalId).enqueue(object: Callback<OkayLinking>{ override fun onFailure(call: Call<OkayLinking>, t: Throwable) { Toast.makeText(this@MainActivity, "Error making request to Server ${t.localizedMessage}", Toast.LENGTH_LONG).show() t.printStackTrace() } override fun onResponse(call: Call<OkayLinking>, response: Response<OkayLinking>) { linkUser(response?.body()!!.linkingCode) } }) } }

After we successfully generated the linking code we can now proceed to linking the user with Okay SDK.

PsaManager provides us with a helper function that allows us to link users with SPS right from Okay SDK. The structure of the method we can use is like so.

PsaManager.linkTenant(linkingCode: String, spaStorage: SpaStorage, linkingScenarioListener: LinkingScenarioListener)

The LinkingScenarioListener must be implemented, as it allows us to listen for two possible events: onLinkingCompletedSuccessful and onLinkingCompletedSuccessful. We will be implementing this listener soon.

We will also need to implment the SpaStorage interface in our application. I think the easiest place to do this, is from one of our repositories(PreferenceRepo class in this case). Of course this is just for convenience.

Below is a typical example of what my PreferenceRepo class might look like.

class PreferenceRepo(context: Context): SpaStorage { private val prefStorage: SharedPreferences = context.getSharedPreferences(PREFERENCE_KEY, Context.MODE_PRIVATE) override fun getPubPssBase64(): String? { return prefStorage.getString(PUB_PSS_B64, "") } override fun putAppPNS(p0: String?) { with(prefStorage.edit()) { putString(APP_PNS, p0) commit() } } override fun putPubPssBase64(p0: String?) { with(prefStorage.edit()) { putString(PUB_PSS_B64, p0) commit() } } override fun getAppPNS(): String? { return prefStorage.getString(APP_PNS, "") } override fun getEnrollmentId(): String? { return prefStorage.getString(ENROLLMENT_ID, "") } override fun putInstallationId(p0: String?) { with(prefStorage.edit()) { putString(INSTALLATION_ID, p0) commit() } } override fun putExternalId(p0: String?) { with(prefStorage.edit()) { putString(EXTERNAL_ID, p0) commit() } } override fun putEnrollmentId(p0: String?) { with(prefStorage.edit()) { putString(ENROLLMENT_ID, p0) commit() } } override fun getInstallationId(): String? { return prefStorage.getString(INSTALLATION_ID, "") } override fun getExternalId(): String? { return prefStorage.getString(EXTERNAL_ID, "") } companion object { const val PREFERENCE_KEY = "firebase_instance_id" const val APP_PNS = "app_pns" const val EXTERNAL_ID = "external_id" const val PUB_PSS_B64 = "pub_pss_b64" const val ENROLLMENT_ID = "enrollment_id" const val INSTALLATION_ID = "installation_id" } }

This is a typical way to make a call to linkTenant() method.

// MainActivity.kt private var preferenceRepo = PreferenceRepo(this@MainActivity) fun linkUser(linkingCode: String) { val psaManager = PsaManager.getInstance() val linkingScenarioListener: LinkingScenarioListener = object: LinkingScenarioListener{ override fun onLinkingCompletedSuccessful(var1: Long, var3: String){ Toast.makeText(this@MainActivity, "Linking Successful", Toast.LENGTH_LONG).show() } override fun onLinkingFailed(var1: ApplicationState) { Toast.makeText(this@MainActivity, "Linking not Successful: ${var1.code} ", Toast.LENGTH_LONG).show() } } psaManager.linkTenant(linkingCode, preferenceRepo, linkingScenarioListener) }

We can now inititiate the call to PsaManager.linkTenant() from our apps onCreate method like so.

// MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

setSupportActionBar(toolbar)

checkPermissions()

fetchInstanceId()

enrollmentButton.setOnClickListener { view ->

beginEnrollment()

}

linkingButton.setOnClickListener{ view ->

// pass in the linking code from the server or entered by the user

linkUser(linkingCodeEditText.text.toString())

}

}

11. Authorizing a Transaction with Okay

If we have successfully linked our user we can now proceed to authorizing transactions or authenticating users.

The steps are pretty straight forward. We make a request to our server(SPS) to begin our authorization, Our server will make a call to PSS, PSS in turn, will send a push notification to our app with the current tenantId of our server and the current transaction sessionId fields. Once we receive the push notification from Okay Servers we can now proceed to using the SDK's PsaManager.startAuthorizationActivity(activity: Activity, spaAuthorizationData :SpaAuthorizationData) method, passing in the current instance of our activity and an instance of SpaAuthorizationData class.

Starting an authorization begins with a simple request to your server like so. This is just a simple request using Retrofit in Android.


//MainActivity.kt private val retrofitWrapper = RetrofitWrapper() // A simple wrapper around retrofit for network callsprivate val transactionHandler = retrofitWrapper.handleTransactionEndpoints() private fun startServerAuthorization(userExternalId: String?) { transactionHandler.authorizeTransaction(userExternalId).enqueue(object: Callback<AuthorizationResponse> { override fun onFailure(call: Call<AuthorizationResponse>, t: Throwable) { Toast.makeText(this@MainActivity, "Error making request to Server", Toast.LENGTH_LONG).show() } override fun onResponse( call: Call<AuthorizationResponse>, response: Response<AuthorizationResponse> ) { Toast.makeText(this@MainActivity, "Request made successfully", Toast.LENGTH_LONG).show() } }) }

This code sends a request to our demo server that initiates the authorization with PSS. Our application will recieve an Push Notification that will be handled by our FirebaseMassagingService (This service is part of Firebase messaging if you are yet to setup Firebase please see this documentaion). In this illustration we extend this service in our app using the OkayDemoFirebaseMessagingService class.

This is what our OkayDemoFirebaseMessagingService class looks like.

// OkayDemoFirebaseMessagingService.kt class OkayDemoFirebaseMessagingService : FirebaseMessagingService() { override fun onNewToken(token: String) { super.onNewToken(token) token?.run { PreferenceRepo(this@OkayDemoFirebaseMessagingService).putExternalId(token) } } override fun onMessageReceived(remoteData: RemoteMessage) { if(remoteData.data.isNotEmpty()){ // handle notification val notificationData = NotificationHandler.extractRemoteData(remoteData) // You can handle the data from the push notification here // However you seem fit // But in this illustration we just send sessionId as an Intent extra to MainActivity startActivity(Intent(this, MainActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) putExtra(ACTIVITY_WAKE_UP_KEY, notificationData.sessionId!!.toLong() ) }) } } override fun onDeletedMessages() { super.onDeletedMessages() } companion object { val ACTIVITY_WAKE_UP_KEY = "wake_up_key" }

We simply receive this sessionId inside MainActivity.kt

// MainActivity.kt class MainActivity : AppCompatActivity() { private lateinit var preferenceRepo: PreferenceRepo private val permissionHelper = PermissionHelper(this) private val retrofitWrapper = RetrofitWrapper() private val transactionHandler = retrofitWrapper.handleTransactionEndpoints() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) preferenceRepo = PreferenceRepo(this) checkPermissions() fetchInstanceId() handleIntent(intent) enrollmentButton.setOnClickListener { view -> beginEnrollment() } linkingButton.setOnClickListener{ startServerLinking(preferenceRepo.externalId) } authorizeButton.setOnClickListener { startServerAuthorization(preferenceRepo.externalId) } } private fun handleIntent(intent: Intent?) { intent?.apply { val sessionId = getLongExtra(OkayDemoFirebaseMessagingService.ACTIVITY_WAKE_UP_KEY, 0) if (sessionId > 0) { Toast.makeText(this@MainActivity, "Current sessionId $sessionId ", Toast.LENGTH_LONG).show() // Start Authorization with retrieved session Id startAuthorization(sessionId) } } } ... private fun startAuthorization(sessionId: Long) { PsaManager.startAuthorizationActivity(this, SpaAuthorizationData(sessionId, preferenceRepo.appPNS, null, PsaType.OKAY)) } private fun startServerAuthorization(userExternalId: String?) { transactionHandler.authorizeTransaction(userExternalId).enqueue(object: Callback<AuthorizationResponse> { override fun onFailure(call: Call<AuthorizationResponse>, t: Throwable) { Toast.makeText(this@MainActivity, "Error making request to Server", Toast.LENGTH_LONG).show() } override fun onResponse( call: Call<AuthorizationResponse>, response: Response<AuthorizationResponse> ) { // Notify user request was sent Toast.makeText(this@MainActivity, "Request made successfully", Toast.LENGTH_LONG).show() } }) } }

If you successfully retrieved the sessionId the Authorization process begins immediately, allowing Okay SDK to communicate with PSS.