Sonect Shop SDK for Android [PRELIMINARY]

In this document we will go through the necessary steps to integrate Sonect Shop SDK in your Android app.

Contact support@sonect.ch if additional info is needed.

Installation:

Add jitpack repo as a repository

e.g. in project build file

allprojects {
    repositories {
    	...
        maven { url 'https://jitpack.io' }
    }
}

Add dependency to the SDK

Latest version of SDK:

Add SDK to build.gradle of your app. SDK could work with both okhttp3 major versions 3 and 4. They have slightly incompatible changes so you must define which one you want to use.

You should

  • Exclude one of okhttp3 or okhttp4 from dependencies
  • Make sure that other lib is connected - both okhttp + loggingInterceptor

Sample of using okhttp3 major version 3.

dependencies {
	...
    implementation ("com.github.sonect:android-shop-sdk:{latestVersion}") {
        exclude module: "okhttp4"
    }
    // Okhttp must be provided as a separate dependency
    externalImplementation "com.squareup.okhttp3:okhttp:3.14.9"
    externalImplementation "com.squareup.okhttp3:logging-interceptor:3.14.9"
    ...
}

SDK Integration

To start SDK you need to create SonectSDK with provided Config. Config is created via Builder.

    val builder: SonectSDK.Config.Builder = SonectSDK.Config.Builder()
    val configBuilder = builder
        .enviroment(SonectSDK.Config.Enviroment.DEV) // Prod by default
        .userCredentials(
            SonectSDK.Config.UserCredentials(
                "Merchant ID obtained from Sonect",
                "Token SDK obtained from Sonect"
            )
        )
        .sdkCallbacks(object : SdkActionsCallback { // Callbacks from SDK
          	override fun onSdkLastFragmentClosed() {
            	finish() // E.g. when SDK closed we want to close the app itself
         		}
          
            override fun onTermsAccepted() {
              Log.e("!@#","T&C Accepted")
            }

            override fun onShopOnboardingComplete() {
              Log.e("!@#","Shop onboarding completed")
            }
        })
    val doWeWantToUseScanditForScanning = true

    if (doWeWantToUseScanditForScanning) {
        configBuilder.customScanditKey("Your Scandit Key obtained from Sonect")
    } else {
        configBuilder.customScannerFragment(CustomScannerFragment()) // Provide scanner fragment
    }

    val config = configBuilder.build()
    val sonectSDK = SonectSDK(this, config)

    supportFragmentManager.beginTransaction()
        .replace(R.id.container, sonectSDK.getStartFragment()) // Start SDK fragment
        .addToBackStack(null).commit()

Get callbacks from SDK

By providing SdkActionsCallback you're able to get onTermsAccepted and onShopOnboardingComplete events from SDK.

Provide system event from outer app

Provide back event into SDK

Whole sdk is built on fragments and in order to handle navigation stack properly BackPress event should be provided by wrapping Activity.

override fun onBackPressed() {
   var backIsHandled = false
   for (fragment in supportFragmentManager.fragments) {
       if ((fragment as? EntryPointFragment)?.handleBack() == true) {
           // Handle back by ourselves, SDK won't handle it anymore
           backIsHandled = true
       }
   }
   if (!backIsHandled) {
       super.onBackPressed()
   }
}

Provide Activity result into SDK

Sdk have an interface ActivityResultStorage with two methods getPendingResult and addNavigationResult. Activity in which you integrate Shop SDK have to implement this interface and return peding result for the request code. It is needed to support proper navigation in case of iDenfy for KYC inside SDK.

Possible simple implementation of the approach:

class MyAwesomeActivity : AppCompatActivity(), ActivityResultStorage {
  private val pendingResults = mutableMapOf<Int, ActivityResult>()
  ...
  override fun getPendingResult(requestCode: Int) = pendingResults.remove(requestCode)
  ...
  override fun addNavigationResult(requestCode: Int, result: ActivityResult) {
    pendingResults[requestCode] = result
  }
  ...
  override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        if (couldHanldeByYourself == true) {
          
        } else {
           // Save current result into pending results with proper requestCode
					addNavigationResult(requestCode, ActivityResult(resultCode, data))

          val topFragment = supportFragmentManager.let { it.fragments[it.fragments.size - 1] }
          // Here we call system's onActivityResult method, won't harm any other fragments.
          // Inside SDK we'll call for getPendingResult and handle result by SDK.
          topFragment?.onActivityResult(requestCode, resultCode, data)
          super.onActivityResult(requestCode, resultCode, data) 
        }
    }
  ...
}

Custom barcode scanner

If you want to provide your own scanning experience, you could override SDK's scanner (Scandit).

You have to override ScannerFragment from SDK. It has a listener inside which need to be called when scanning performed.

class CustomScannerFragment: ScannerFragment() { // ScannerFragment from SDK
    override fun getLayoutId(): Int = R.layout.fragment_custom_scanner

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        btnScannerComplete.setOnClickListener {
            listener.onScan("123456")
        }
    }
}

Provide shop info from outer app

In order to give info about shop SonectSDK has method in builder

fun shop(shop: Shop) = apply {
    this.shop = shop
}

Shop class has a Builder inside which let define Shop field by field.

Provided Shop will prefill data during the user onboarding.

Provide beneficiary info from outer app

In order to give info about beneficiary SonectSDK has method in builder

fun beneficiary(beneficiary: Beneficiary) = apply {
    this.beneficiary = beneficiary
}

Beneficiary class has a Builder inside which let define Beneficiary field by field.

Provided Beneficiary will prefill data during the user onboarding.

Provide additional info from outer app

Builder contains method that allow to pass extra info into SDK which will be proxied to Sonect Backend.

fun additionalParams(params: Map<String, String>) = apply {
    additionalParams = params
}

Compile time config

In order to provide compile time config override resource variables:

<!-- Show partner reference/operational ID on confirmed receipt -->
<bool name="sonect_should_show_partner_reference">false</bool> 

Proguard / R8

In case you're using proguard or R8 to obfuscate and optimize your code, the following rules should be enough to maintain all expected functionality. Please let us know if you find any issues.

-keepclassmembers class * {
    @com.google.gson.annotations.SerializedName <fields>;
}
-keepnames class androidx.navigation.fragment.NavHostFragment

This is needed to maintain json serialization after proguard.