Transaction
A Transaction is a message sent to a user's app when they are logged in to Security Service. To handle situations when the app is not running or in the background, a push notification is sent to inform the user.
The app can be in one of four states when you receive a Transaction:
There are mainly two types of transactions:
- Wire Transfer (without authentication confirmation)
- Wire Transfer - free text (with and without authentication confirmation)
One more type of transaction is Send Message which is mainly used to display some message in the application and does not require any pin confirmation. Below is the diagram to understand the Transaction event categorization:
A user will receive transactions only if they are logged into the application. Furthermore, a user can receive transaction requests irrespective of the screen they are on. The Transaction flow involves a specific set of events.
Note that the server side is expected to select a localization of the texts sent to the app based on the language information that was provided in the StartEvent.
Transaction Event Flow Diagram
The following diagram shows the event flow in detail.
Transaction Sequence Diagram
For a better understanding of this decisive flow, here is a diagram that additionally shows the interaction of the various screens in our demo application:
As an app developer you can trigger a transaction by following the instructions.
Implementation Examples
iOS/Swift
Here are two code snippets for Swift:
func receive(_ event: KsEvent,
withCompletionHandler completionBlock:
((KsEvent?) -> Void)? = nil) {
if event is KSMTriggerBannerEvent{
if let bannerEvent = event as? KSMTriggerBannerEvent{
switch (bannerEvent.bannerType) {
case .displayMessage:
// Handle display message
case .transaction:
//Handle transaction.
default:
break
}
}
}
}
After receiving TriggerBannerEvent UI will trigger
if KSMTriggerBannerEvent.bannerType == .transaction{
masterControllerAdapter.sendEvent2MasterController(
KSMStartTransactionEvent(), withCompletionHandler: nil)
}else {
masterControllerAdapter.sendEvent2MasterController(
KSMStartDisplayMessageEvent(), withCompletionHandler: nil)
}
Android/Kotlin
Below is the code sample to handle transaction events in Kotlin:
override fun onEventReceived(eventFrameworkEvent: EventFrameworkEvent) {
when (eventFrameworkEvent) {
is TriggerBannerEvent -> {
when (eventFrameworkEvent.bannerType) {
BannerType.TRANSACTION -> {
mcWrapper?.triggerStartTransactionEvent()
}
BannerType.DISPLAY_MESSAGE -> {
mcWrapper?.triggerStartDisplayMessageEvent()
}
}
}
is TransactionPinRequiredRequestEvent -> {
TransactionPinActivity.startTransactionPinActivity(
this,
eventFrameworkEvent.timerInformation
)
}
is DisplayConfirmationRequestEvent -> {
launchMain {
showHideProgressBar(false)
if (this@BaseActivity is TransactionPinActivity) {
finish()
}
DisplayConfirmationMessageActivity.startDisplayConfirmationMessageActivity(
this@BaseActivity,
eventFrameworkEvent.transactionInformation,
eventFrameworkEvent.timerValue
)
}
}
is DisplayConfirmationResultEvent -> {
}
is DisplayMessageEvent -> {
launchMain {
showHideProgressBar(false)
DisplayBannerMessageActivity.startDisplayMessageActivity(
this@BaseActivity,
eventFrameworkEvent.messageInformation
)
}
}
is TransactionEndEvent -> {
launchMain {
showHideProgressBar(false)
if (this@BaseActivity is TransactionPinActivity || this@BaseActivity is
DisplayConfirmationMessageActivity) {
finish()
}
}
when (eventFrameworkEvent.status) {
StatusType.OK -> Utils.showToast(this, getString(R.string.transaction_success))
StatusType.USER_CANCEL -> Utils.showToast(this, getString(
R.string.transaction_cancelled))
StatusType.USER_CONFIRMATION_TIMEOUT -> {
Utils.showToast(this, getString(R.string.transaction_cancelled_timeout))
}
else -> {
Utils.showToast(this, getString(R.string.some_error_occurred_in_transaction))
}
}
}
is ProvidePinResultEvent -> {
if (eventFrameworkEvent.status == StatusType.OK) {
transactionPinHandlerCallback.onProvidePinSuccess()
return
}
if (eventFrameworkEvent.status == StatusType.USER_CANCEL) {
transactionPinHandlerCallback.onProvidePinCancel()
return
}
if (eventFrameworkEvent.status == StatusType.NOT_REACHABLE) {
launchMain {
Utils.showAlertDialog(
context = baseActivity,
text = baseActivity.getString(
R.string.ks_ast_001_activation_useridactivationcodeview_alert_text_connectionnotestablished),
clickListener = null,
title = baseActivity.getString(R.string.label_error)
)
}
return
}
if (eventFrameworkEvent.status == StatusType.USER_CONFIRMATION_TIMEOUT) {
launchMain {
Utils.showAlertDialog(
context = baseActivity,
text = baseActivity.getString(R.string.msg_transaction_timeout),
clickListener = DialogInterface.OnClickListener { _, _ ->
transactionPinHandlerCallback.finishActivity() },
title = baseActivity.getString(
R.string.ks_sc_ast_tms_008a_verification_failed_title)
)
}
return
}
val retryCounter = eventFrameworkEvent.retryCounter
val errorMessage: String
if (retryCounter > 0) {
errorMessage = String.format(
baseActivity.resources.getQuantityString(
R.plurals.ks_wrong_pin_error_message,
retryCounter
), retryCounter
)
} else {
errorMessage =
baseActivity.getString(
R.string.ks_ast_015_login_view_alert_text_appdisabled)
}
launchMain {
Utils.showAlertDialog(
context = baseActivity,
text = errorMessage,
clickListener = DialogInterface.OnClickListener { _, _ ->
if (retryCounter <= 0) {
transactionPinHandlerCallback.restart()
}
},
title = baseActivity.getString(
R.string.ks_sc_ast_tms_008a_verification_failed_title)
)
}
}
}
}
Flutter/Dart
Below is the code sample to handle transaction events in Flutter:
class TransactionEventReceiver extends McWrapperApiEventReceiver {
@override
void onReceive(Object event) {
if (event is TriggerBannerEventT) {
_handleTriggerBannerEvent(event);
} else if (event is TransactionPinRequiredRequestEventT) {
_handleTransactionPinRequired(event);
} else if (event is DisplayConfirmationRequestEventT) {
_handleDisplayConfirmationRequest(event);
} else if (event is DisplayMessageEventT) {
_handleDisplayMessage(event);
} else if (event is TransactionEndEventT) {
_handleTransactionEnd(event);
}
}
void _handleTriggerBannerEvent(TriggerBannerEventT event) {
print("📱 Transaction triggered: ${event.bannerType}");
switch (event.bannerType) {
case BannerType.transaction:
case BannerType.pushReceived:
_triggerStartTransactionEvent();
break;
case BannerType.displayMessage:
_triggerStartDisplayMessageEvent();
break;
default:
print("❓ Unknown banner type: ${event.bannerType}");
}
}
void _handleTransactionPinRequired(TransactionPinRequiredRequestEventT event) {
print("🔐 PIN required for transaction");
print(" Timer value: ${event.timerValue}");
// Navigate to PIN input screen
// Handle the timer value for PIN timeout
}
void _handleDisplayConfirmationRequest(DisplayConfirmationRequestEventT event) {
print("✅ Display confirmation request");
print(" Transaction info: ${event.transactionInformation ?? 'No info'}");
print(" Timer value: ${event.timerValue}");
// Show transaction confirmation screen
}
void _handleDisplayMessage(DisplayMessageEventT event) {
print("💬 Display message event");
print(" Message: ${event.messageInformation ?? 'No message'}");
// Show message screen or popup
}
void _handleTransactionEnd(TransactionEndEventT event) {
print("🏁 Transaction ended with status: ${event.status}");
switch (event.status) {
case StatusType.ok:
print("✅ Transaction successful");
break;
case StatusType.userCancel:
print("❌ Transaction cancelled by user");
break;
case StatusType.userConfirmationTimeout:
print("⏰ Transaction timed out");
break;
default:
print("💥 Transaction failed: ${event.status}");
}
}
Future<void> _triggerStartTransactionEvent() async {
try {
final mcApi = locator<McWrapperApi>();
final startTransactionEvent = StartTransactionEventT();
final response = await mcApi.send(startTransactionEvent);
response.fold(
(error) => print("❌ Start transaction error: $error"),
(result) => print("✅ Start transaction sent successfully"),
);
} catch (e) {
print("❌ Exception in start transaction: $e");
}
}
Future<void> _triggerStartDisplayMessageEvent() async {
try {
final mcApi = locator<McWrapperApi>();
final startDisplayMessageEvent = StartDisplayMessageEventT();
final response = await mcApi.send(startDisplayMessageEvent);
response.fold(
(error) => print("❌ Start display message error: $error"),
(result) => print("✅ Start display message sent successfully"),
);
} catch (e) {
print("❌ Exception in start display message: $e");
}
}
}