Error Handling

In most error cases, the applications are still running and a Status object is passed to the POS. There are a couple of scenarios discussed later that involve hard crashes or uncaught exceptions that must be handled differently.

Status and its Subclasses

All of the events are derived from Status, and most methods return a Status object, with only a few exceptions. In these scenarios, if the status code (Status.getStatus()) returns non-zero, an error has occurred. In most cases, Status.getMessage() returns a localized, human readable message that can be displayed to the cashier if the error cannot be automatically handled. In some cases, the status code will be assigned some constant value that can be automatically handled, such as those defined in CommerceConstants or in the appropriate Status subclass for the event type, but some will not. In the cases where it is not found, it can be helpful to try again depending on the context (see state-matters).

When the status code is non-zero after calling a method

Many methods, such as TransactionManager.login(), return a Status object. Check this status object for a non-zero status code after calling to make sure that the login request could be submitted. An error when calling a method is generally due to some unready state or invalid parameter. Invalid parameters should be caught during development/testing, and should not be present in the field. An unready state can happen if the POI has not fully booted, such as after a recent restart of the device, and will generally require some delay before attempting the action.

Most of these errors that occur in the field can be handled by displaying Status.getMessage() to the cashier and enabling them to retry or cancel the action. If they cancel, it is the responsibility of the POS to restore the correct state, e.g., if some merchandise failed to add to the POI display, the merchandise should probably be removed from the cart so the display and the cart can be kept in sync.

When the status code is non-zero for a received event

It is possible for any of the events to be sent to the listener with an error. When an error has occurred, the event might not contain any of the expected objects, e.g., a PaymentCompletedEvent might return null for PaymentCompletedEvent.getPayment(). Depending on the exact event, these errors should be handled differently, and really depend on the product requirements and paradigms in the POS.

Note

A payment card decline or user-canceled payment is not considered an error, since it is a normally expected authorization result, therefore the status code will be 0 in this case. In payment cases, an error could be returned if the card was invalid, or the payment host returned an error, or many other scenarios. In this case, the status code will be one specific to the host or payment app, and should be included in the display to the cashier to help them if they reach out for support.


Errors without Status

  1. If the commerce listener is destroyed, the status will not be received.

  2. If the POS or the Payment Service crashes.

  3. An error status is returned indicating the connection is lost.

In any of these scenarios, it is best to use the common recovery scenarios below, selecting the appropriate one for the state during which the error occurred.

Note

The payment service runs in a separate process, and does not keep the commerce listener from being destroyed. See Create a Commerce Listener.


Common Error & Recovery Scenarios

Session Start Failure

This is generally caused by being in the wrong state when attempting to start a session (see state-matters). This can generally be resolved by querying the current state to determine what next steps ought to be taken, e.g., if the state is TransactionManager.STATE_SESSION_OPEN, then the POS should end the old session. If this is still not working, it might be due to a mismatch of the android state and the POI state. Use TransactionManager.abort() followed by TransactionManager.endSession(), thoroughly resetting the state of the POI and allowing the current state to become accurate, after which starting the session should work smoothly.

It’s also possible for this to fail because the POI is not yet fully booted. In this case, it’s best to display a prompt to the cashier allowing them to retry the operation at their discretion, prompting them to make sure the POI is ready.

Recovering Event not received for Payment

This is generally caused either by a crash or a destroyed commerce listener. If a crash, when the application comes back up, it must log in but starting a session will fail. Proceed to recovering the last transaction below.

If the listener is no longer valid, a new one must be created and added using TransactionManager.addSessionListener(listener), then proceed to recovering the last transaction below.

If the connection is lost, the POS will receive a PaymentCompletedEvent with PaymentCompletedEvent.getStatus() equaling StatusCode.DEVICE_CONNECTION_LOST. In this case, the only thing to do is to call PaymentSdk.tearDown() and then initialize to recover the connection. To obtain the last transaction result, create a TransactionQuery object, use TransactionQuery.setQueryingLastTransaction(true) to mark it as recovering the information about the most recent payment, and then examine the information returned in the TransactionQueryEvent. The payment’s authorization result will be IN_PROGRESS if it is still processing, it can be cancelled as Cancel the Transaction, or it will contain the actual result.

It is possible to abort a transaction if it is still in progress using TransactionManager.abort(). Sometimes this is preferred to recovering the status of the transaction, though it may require the customer to present their card again, or even possibly key in their loyalty information, and is not the ideal solution.

Recovering Event not received for Basket Action

This is generally caused either by a crash or a destroyed commerce listener. If this wasn’t received due to a crash, it is best to follow the steps for recovering-a-session. If the listener is no longer valid, a new one must be created and added using TransactionManager.addSessionListener(listener). See Create a Commerce Listener for more information on commerce listeners.

Recovering a Session

Losing the connection to an active session on the POI is generally caused only by a crash. If a payment was not in progress, it’s best to start a new session and restore the basket. See session-start-failure, as simply calling TransactionManager.startSession(...) will likely fail, and then restore the basket (Restoring a Basket from a Previous Order). If a payment was in progress when this occurred, see recovering-a-payment.

Invalid Card and similar issues during Payment

There are many common error scenarios where a payment will not have an AuthorizationResult, or even where the PaymentCompletedEvent will not have a Payment object. These cases include when the user has presented an invalid card (e.g. expired), or the card read has failed, or any number of other scenarios. The PaymentCompletedEvent.getStatus() will be non-zero, and the string from PaymentCompletedEvent.getMessage() will be localized for the cashier and should be displayed as a prompt, generally with an option to retry the payment.

Printer Error Codes

STATUS_SUCCESS 0

STATUS_ERROR 1

STATUS_OUT_OF_PAPER 2

STATUS_OVER_TEMPERATURE 4

STATUS_PAPER_JAM 8

STATUS_LOW_BATTERY 16