Migrating to Version 2.0+
Overview
To simplify your application logic and integration with the Knot Android SDK, Version 2.0 includes a number of breaking changes that may affect the way your integration works or behaves.
The new version includes a number of significant improvements:
- Enhanced speed and stability in loading merchant flows.
- More streamlined initialization of the SDK.
- Simplified event handling, more informative event messaging, and uniform naming conventions for easier debugging.
- Improved maintainability and foundations for new feature compatibility.
Breaking Changes
Configuring and opening the Knot SDK has changed significantly in iOS Version 1.0 and requires some refactoring in order to initialize the SDK with a session. Errors are now encapsulated in a KnotError object which provides an enumerated value to debug with.
Session Initialization
Changes
CardOnFileSwitcher.getInstance()
andConfiguration(environment, clientId, sessionId)
are replaced with a more flexibleKnotConfiguration
.- The product type is now defined by
Product
enum values.card_switcher | .transaction_link
. - The new interface allows configuration of additional properties such as
useCategories
,useSearch
, andmerchantIds
. - The
open
action now requires aKnotConfiguration
and an optionalKnotEventDelegate
which is nowKnot.open(context, knotConfiguration, knotEventDelegate)
.
Before
CardOnFileSwitcher cardOnFileSwitcher = CardOnFileSwitcher.getInstance();
Configuration switcherConfig = new Configuration(environment, clientId, sessionId);
cardOnFileSwitcher.setMerchantIds(new int[]{44});
cardOnFileSwitcher.setUseCategories(true);
cardOnFileSwitcher.setUseSearch(true);
Options options = new Options();
String[] domainUrls = {"https://domain1.com", "https://domain2.com", ....};
options.setDomainUrls(domainUrls);
cardOnFileSwitcher.init(context, switcherConfig, options, onSessionEventListener);
cardOnFileSwitcher.openCardOnFileSwitcher("Onboarding");
val cardOnFileSwitcher = CardOnFileSwitcher.getInstance()
val switcherConfig = Configuration(environment, clientId, sessionId)
cardOnFileSwitcher.setMerchantIds(intArrayOf(44))
cardOnFileSwitcher.setUseCategories(true)
cardOnFileSwitcher.setUseSearch(true)
val options = Options()
val domainUrls = arrayOf("https://domain1.com", "https://domain2.com")
options.setDomainUrls(domainUrls)
cardOnFileSwitcher.init(context, switcherConfig, options, onSessionEventListener)
cardOnFileSwitcher.openCardOnFileSwitcher("Onboarding")
After
KnotConfiguration config = new KnotConfiguration(
"session_12345", // sessionId
"client_67890", // clientId
Environment.production, // environment
Knot.Product.card_switcher, // product
new int[]{101, 102, 103}, // merchantIds
true, // useCategories
true, // useSearch
new String[]{"https://example.com"}, // domainUrls
"onboarding" // entryPoint
);
KnotEventDelegate knotEventDelegate = new KnotEventDelegate() {
@Override
public void onSuccess(String merchant) {
// Handle successful operation
}
@Override
public void onError(KnotError knotError) {
// Handle error operation using knotError
}
@Override
public void onExit() {
// Handle SDK exit event
}
@Override
public void onEvent(KnotEvent knotEvent) {
// Extract and process event details from KnotEvent
}
};
Knot.open(context, config, knotEventDelegate);
val config = KnotConfiguration(
"session_12345", // sessionId
"client_67890", // clientId
Environment.PRODUCTION, // environment
Knot.Product.card_switcher, // product
intArrayOf(101, 102, 103), // merchantIds
true, // useCategories
true, // useSearch
arrayOf("https://example.com"), // domainUrls
"onboarding" // entryPoint
)
KnotEventDelegate knotEventDelegate = new KnotEventDelegate() {
@Override
public void onSuccess(String merchant) {
// Handle successful operation
}
@Override
public void onError(KnotError knotError) {
// Handle error operation using knotError
}
@Override
public void onExit() {
// Handle SDK exit event
}
@Override
public void onEvent(KnotEvent knotEvent) {
// Extract and process event details from KnotEvent
}
};
Knot.open(context, config, knotEventDelegate)
Event Handling
Changes
- Event handling is now managed through
KnotEventDelegate
instead of closures. - Events like
onSuccess
,onError
, andonExit
are now explicit methods inside a delegate. - The
onEvent
method introduces theKnotEvent
object to better handle Knot emitted events. - The
KnotError
type provides improved error descriptions. - The
sendCard
parameter is deprecated and its functionality incorporated into themetaData
dictionary withinKnotEvent
whenKnotEvent.event
equalsAUTHENTICATED
.
Before
cardOnFileSwitcher.setOnSessionEventListener(new OnSessionEventListener() {
@Override
public void onSuccess(String merchant) {
Log.d("onSuccess", merchant);
}
@Override
public void onError(String errorCode, String errorMessage) {
Log.d("onError", errorCode + " " + errorMessage);
}
@Override
public void onExit() {
Log.d("onExit", "exit");
}
@Override
public void onEvent(String eventName, String merchantName, String taskId) {
Log.d("onEvent", eventName + " " + merchantName + " " + taskId);
}
});
cardOnFileSwitcher.setOnSessionEventListener(object : OnSessionEventListener {
override fun onSuccess(merchant: String) {
Log.d("onSuccess", merchant)
}
override fun onError(errorCode: String, errorMessage: String) {
Log.d("onError", "$errorCode $errorMessage")
}
override fun onExit() {
Log.d("onExit", "exit")
}
override fun onEvent(eventName: String, merchantName: String, taskId: String) {
Log.d("onEvent", "$eventName $merchantName $taskId")
}
})
After
KnotEventDelegate knotEventDelegate = new KnotEventDelegate() {
@Override
public void onSuccess(String merchant) {
// Handle successful operation
}
@Override
public void onError(KnotError knotError) {
// Handle error operation using knotError
}
@Override
public void onExit() {
// Handle SDK exit event
}
@Override
public void onEvent(KnotEvent knotEvent) {
// Extract and process event details from KnotEvent
}
};
val eventDelegate = object : KnotEventDelegate {
override fun onSuccess(merchant: String) {
// Handle successful operation
}
override fun onError(knotError: KnotError) {
// Handle error operation using knotError
}
override fun onExit() {
// Handle SDK exit event
}
override fun onEvent(knotEvent: KnotEvent) {
// Extract and process event details from KnotEvent
}
}
sendCard
sendCard
Note
Most apps do not use the explicit
sendCard
method, as it is rarely applicable to the integration with the Knot SDK.
The sendCard
parameter has been deprecated and its functionality is incorporated into the metaData
object within KnotEvent
when the KnotEvent.event
equals AUTHENTICATED
. This change enhances flexibility by allowing additional contextual data to be included in events without requiring separate parameters. Previously, sendCard
was accessed as a standalone value, but now developers can retrieve it from the metaData
object in the event callback. This approach ensures better extensibility and consistency across different event types. To access the sendCard
value, simply extract it from the event’s metaData
object.
@Override
public void onEvent(KnotEvent knotEvent) {
// Extract the metadata map from the KnotEvent
Map<String, Object> metaData = knotEvent.getMetaData();
// Check if the metadata contains a sendCard flag
if (knotEvent.getEvent().equalsIgnoreCase("AUTHENTICATED") && metaData != null && metaData.containsKey("sendCard")) {
Boolean sendCard = (Boolean) metaData.get("sendCard");
Log.d("KnotEvent", "sendCard: " + sendCard);
}
}
override fun onEvent(knotEvent: KnotEvent) {
val metaData = knotEvent.metaData
if (knotEvent.event.equals("AUTHENTICATED", ignoreCase = true) && metaData != null && metaData.containsKey("sendCard")) {
val sendCard = metaData["sendCard"] as? Boolean
Log.d("KnotEvent", "sendCard: $sendCard")
}
}
Event Names
The SDK now maps raw event names to standardized event names for easier handling.
Event Name Prior to 2.0 | 2.0 Event Name |
---|---|
refresh session request | REFRESH_SESSION_REQUEST |
merchant clicked | MERCHANT_CLICKED |
login started | LOGIN_STARTED |
authenticated | AUTHENTICATED |
otp required | OTP_REQUIRED |
security questions required | SECURITY_QUESTIONS_REQUIRED |
approval required | APPROVAL_REQUIRED |
Error Handling
Error handling has been improved with more structured and meaningful error messages.
Changes
- Errors are now encapsulated in the
KnotError
enum. - Each error has a human-readable description (
errorDescription
) and a unique error code (errorCode
). - Improved clarity and consistency across error messages.
Before
@Override
public void onError(String errorCode, String errorMessage) {
Log.d("onError", errorCode + " " + errorMessage);
}
override fun onError(error: String?, errorMessage: String?) {
Log.d("onError", "$error $errorMessage")
}
After
public void onError(KnotError error) {
switch (error) {
case INVALID_SESSION:
Log.e("KnotError", "Error: INVALID_SESSION - " + error.getErrorDescription());
break;
case EXPIRED_SESSION:
Log.e("KnotError", "Error: EXPIRED_SESSION - " + error.getErrorDescription());
break;
case INVALID_CLIENT_ID:
Log.e("KnotError", "Error: INVALID_CLIENT_ID - " + error.getErrorDescription());
break;
case INTERNAL_ERROR:
Log.e("KnotError", "Error: INTERNAL_ERROR - " + error.getErrorDescription());
break;
}
}
fun onError(error: KnotError) {
when (error) {
KnotError.INVALID_SESSION -> Log.e("KnotError", "Error: INVALID_SESSION - ${error.errorDescription}")
KnotError.EXPIRED_SESSION -> Log.e("KnotError", "Error: EXPIRED_SESSION - ${error.errorDescription}")
KnotError.INVALID_CLIENT_ID -> Log.e("KnotError", "Error: INVALID_CLIENT_ID - ${error.errorDescription}")
KnotError.INTERNAL_ERROR -> Log.e("KnotError", "Error: INTERNAL_ERROR - ${error.errorDescription}")
}
}
Error Types
The Knot SDK provides predefined error cases for you to handle based on your own needs.
Error Case | Description |
---|---|
INVALID_SESSION | The session is invalid. |
EXPIRED_SESSION | The session has expired. |
INVALID_CLIENT_ID | The client ID is invalid. |
INTERNAL_ERROR | An internal error occurred. |
Closing the SDK
Note
Most apps do not use the explicit
close
method, as it is infrequently applicable to the integration with the Knot SDK.
Changes
- Closing the SDK is now statically accessed via Knot.close() as opposed to being bound to the session object.
Before
cardOnFileSwitcher.closeCardOnFileSwitcher();
cardOnFileSwitcher.closeCardOnFileSwitcher()
After
Knot.close();
Knot.close()
Updated 7 days ago