The interceptor delivers responses to requests you sent. There is a second, separate asynchronous channel: the event observer, which tells you about the payment application's state — when a payment starts and when it finishes — regardless of who initiated it. This guide explains that channel and the one thing developers most want from it: pulling your own app back to the foreground at the right moment.
Two channels, two jobs
Keep these distinct in your mind:
- The interceptor (covered in How the message model works) receives
DomainMessageresponses — the result of a request you sent. - The event observer receives
ObservableEventstate notifications — what the payment application is doing right now.
You register the event observer at SDK initialization with bindEventObserver. The event you care about most is ObservableEvent.TransactionStateChanged.
.bindEventObserver { event ->
when (event) {
is ObservableEvent.ServerEvent ->
Logger.d("payment app emitted a server event")
is ObservableEvent.TransactionStateChanged ->
when (event.state) {
is PaymentTransactionState -> { /* a payment has started */ }
is IdleTransactionState -> { /* the payment app is done */ }
else -> { /* ignore other states */ }
}
}
}
The two states that matter
| State | Meaning | What you typically do |
|---|---|---|
PaymentTransactionState | Payment processing has started. | Optional: bring your app forward to show a custom screen during authorization (your branding, transaction details, an advert) instead of the payment app's default UI. |
IdleTransactionState | The payment application has finished with the transaction. | Recommended: bring your app forward to show the final result to the operator. |
The reason this channel exists separately from responses: during a payment, the payment application is in the foreground driving the card interaction, not your app. The state events let you decide when to take the screen back.
Bringing your app to the foreground
bringToForeground(MainActivity::class.java) pulls your application back in front of the payment app. Wire it to the state(s) you care about:
.bindEventObserver { event ->
when (event) {
is ObservableEvent.TransactionStateChanged ->
when (event.state) {
// Recommended: come forward when the payment finishes,
// to display the result.
is IdleTransactionState -> bringToForeground(MainActivity::class.java)
// Optional: come forward while authorization is in progress,
// e.g. to show your own UI instead of the payment app spinner.
is PaymentTransactionState -> bringToForeground(MainActivity::class.java)
else -> { /* ignore */ }
}
is ObservableEvent.ServerEvent -> Logger.d("server event")
}
}
If your app is single-activity, just pass your main activity class — you don't need to worry about which screen was showing; the SDK brings the activity forward and your own navigation state is preserved.
Required permission.
bringToForegroundonly works if your application has been granted theSYSTEM_ALERT_WINDOWpermission. Without it, the call does nothing. Request it as part of your app's setup; on many terminal builds it must be granted explicitly.
Choosing which states to handle
- Most integrations only handle
IdleTransactionState— come forward when the payment ends to show the result. That's the recommended minimum. - Handle
PaymentTransactionStatetoo only if you have a reason to be in front during authorization — a custom progress screen, transaction details, or advertising. If you don't, leave the payment app in front; it has its own cardholder UI. - Everything else can be ignored.
Related
- How the message model works — the interceptor channel, the counterpart to this one.
- Quickstart — where the event observer is first registered.
- Guide: payment — the operation whose state these events track.