Manage card payments with the Android SDK

The following describes how to work with card payments with the Android SDK. Operations include starting, processing, retrieving,and refunding. Examples are provided in Kotlin.

Starting card payments

Start by creating a TransactionReference object using the builder provided.

Important: The transaction reference object must contain at least one unique field.

1
val internalTraceId = UUID.randomUUID().toString()
2
val reference = TransactionReference.Builder(internalTraceId)
3
.put("PAYMENT_EXTRA_INFO", "Started from home screen")
4
.build()

In the constructor of the builder you can provide your own ID with a maximum of 128 characters to identify the payment. This ID can be used to perform payment refunds.

Using the put method you can add any value you want to this object. However, keep in mind that the total data size (including key names) in this object can't be bigger than 4 kilobytes.

Next step is to start the CardPaymentActivity. To do this you can use our helper which creates configured Intent object.

1
val intent = CardPaymentActivity.IntentBuilder(this)
2
// MANDATORY: Transaction amount in account currency
3
.amount(20000L)
4
// MANDATORY, Reference object created in previous step
5
.reference(reference)
6
// MANDATORY, enable login prompt in the payment flow if user is not yet logged-in
7
.enableLogin(enableLogin)
8
// OPTIONAL, you can enable tipping (disabled by default)
9
// This option will only work for markets with tipping support
10
.enableTipping(true)
11
// OPTIONAL, you can enable installments (enabled by default)
12
// This option will only work for markets with installments support
13
.enableInstalments(enableInstallments)
14
.build()
15
16
// Start activity with the intent
17
startActivityForResult(intent, 0)

Note: If setting enableLogin to true you need to setup auth for provided UI. Otherwise the user will be asked to login, but the app will never receive the result of the authentication.

About tipping

Setting enableTipping to true does not guarantee that tipping flow will be displayed. This is because tipping is not supported by all accounts and all card readers. Tipping is only supported with the Zettle Card Reader. The function is introduced market by market. If card reader software doesn’t support tipping, users will be prompted to either skip tipping or update card reader software.

Total tip amount is presented in CardPaymentResult.Completed completion with gratuityAmount property. See Tipping support.

Processing card payment results

You will receive the payment result as an Activity result. The result Bundle contains two values:

NameType Description
CardPaymentActivity.RESULT_EXTRA_REQUESTBundleThe extras bundle from request intent.
CardPaymentActivity.RESULT_EXTRA_PAYLOADCardPaymentResultThe payment result.

CardPaymentResult

The payment result is an instance of one of these classes, described in the following:

  • CardPaymentResult.Canceled
  • CardPaymentResult.Failed
  • CardPaymentResult.Completed

The CardPaymentResult is a Kotlin sealed class, meaning it's always one of the following implementations.

Name Description
CardPaymentResult.CanceledPayment was canceled by merchant or customer. Doesn't contain any additional data.
CardPaymentResult.FailedPayment failed. The failure reason is defined by the reason field.
CardPaymentResult.CompletedCard payment was successfully completed. Contains transaction info in the payload field.

The failed and completed results contain more detailed information regarding failure reason, or the payload of a successfully completed payment.

Extracting the failure reason

The CardPaymentResult.Failed contains an instance of FailureReason describing the failure, and can contain any of the following reasons.

Name Description
FailureReason.TechnicalErrorPayment failed because of technical issues. Can be caused by a Bluetooth communication problem or other technical issue.
FailureReason.NetworkErrorCommunication with iZettle servers failed
FailureReason.NotAuthorizedThere is no authorised user to process payment request
FailureReason.AboveMaximumRequested amount is greater than account limit
FailureReason.BellowMinimumRequested amount is smaller than allowed minimum

Extracting the success payload

A successful payment contains a payload of type ResultPayload with many fields describing the payment as listed in the following.

NameType Description
amountLongTotal transaction amount (also includes tip amount if applicable)
gratuityAmountLong?Contains total tip amount if tipping is performed, null otherwise
cardTypeString?Card brand: VISA, MASTERCARD and so on.
cardPaymentEntryModeString?EMV, CONTACTLESS_EMV, MAGSTRIPE_CONTACTLESS, MAGSTRIPE etc. More entry modes might be added independent of SDK version
cardholderVerificationMethodString?
tsiString?EMV tags
tvrString?EMV tags
applicationIdentifierString?EMV tags (aid)
cardIssuingBankString?Card issuing bank if provided
maskedPanString?For example "** ** **** 1111"
panHashString?Card pan hash
applicationNameString?
authorizationCodeString?
installmentAmountLongValue of each installment
nrOfInstallmentsIntNumber of installment chosen
mxFiidString?Mexico-specific data
mxCardTypeString?Mexico specific data
referenceTransactionReference?Your reference object

Retrieving card payments

To retrieve a card payment you need to provide the same unique reference ID provided to create the TransactionReference for the payment itself. See Starting card payments.

1
IZettleSDK.refundsManager.retrieveCardPayment(internalTraceId, object : RefundsManager.Callback<CardPaymentPayload, RetrieveCardPaymentFailureReason> {
2
override fun onFailure(reason: RetrieveCardPaymentFailureReason) { /*...*/ }
3
override fun onSuccess(cardPaymentPayload: CardPaymentPayload) { /*...*/ }
4
})

Refunding card payments

A refund works similar to a payment with the biggest exception that you will need to provide the same unique reference ID provided to create the TransactionReference for the payment. See Retrieving card payments.

When you have your payment object, you need to create a new unique TransactionReference string for each refund. This reference ID can be used to trace the refund in our system if needed.

Important: The transaction reference object must contain at least one unique field.

1
val internalTraceId = UUID.randomUUID().toString()
2
val reference = TransactionReference.Builder(internalTraceId)
3
.put("REFUND_EXTRA_INFO", "Started from home screen")
4
.build()

In the constructor builder you must provide the CardPayment retrieved from previous step.

Using the put method you can add any value to this object. However, keep in mind that the total data size (including key names) in this object can not be bigger than 4 kilobytes. You will get this reference back with transaction data and can always request it back from our servers.

Next step is to start RefundsActivity. Use the IntenBuilder to create create the configured Intent for the refund.

1
val intent = RefundsActivity.IntentBuilder(cardPaymentPayload)
2
// Refund amount in account currency
3
// This amount must be less or equal to the original card payment.
4
// If not provided it will use original card payment amount
5
.refundAmount(20000L)
6
// Reference object created in previous step
7
.reference(reference)
8
// Optional, you can provide tax amount of this card payment to be displayed in the UI
9
.taxAmount(100L)
10
// Optional, you can provide the receipt number of this card payment to be displayed in the UI
11
.receiptNumber("#12345")
12
.build()
13
14
// Start activity with the intent
15
startActivityForResult(intent, 0)

Processing card refund results

You will receive the refund result as an Activity result. The result Bundle contains two values:

NameType Description
RefundsActivity.RESULT_EXTRA_REQUESTBundleThe extras bundle from request intent.
RefundsActivity.RESULT_EXTRA_PAYLOADRefundResultThe refund result.

CardPayment RefundResult

The refund result is an instance of one of these classes, described in the following:

  • RefundResult.Canceled.
  • RefundResult.Failed.
  • RefundResult.Completed.

The RefundResult is a Kotlin sealed class, meaning it's always one of the following implementations.

Name Description
RefundResult.CanceledRefund was canceled by merchant. Doesn't contain any additional data.
RefundResult.FailedRefund failed. The failure reason is defined by the reason field.
RefundResult.CompletedCard refund was successfully completed. Contains transaction info in the payload field.

The failed and successful results contain more detailed information regarding failure reason, and the payload of a successful refund.

Extracting the failure reason

The RefundResult.Failed contains an instance of RefundFailureReason that describes the failure and can contain any of the following reasons.

Name Description
RefundFailureReason.FailedFailure due to unknown reasons
RefundFailureReason.NotAuthorizedThere is no authorised user to process payment request
RefundFailureReason.NotFoundPayment with given reference id was not found
RefundFailureReason.NotRefundablePayment is not refundable
RefundFailureReason.NetworkErrorCommunication with Zettle servers failed
RefundFailureReason.TechnicalErrorPayment failed because of technical issues
RefundFailureReason.AlreadyRefundedPayment was already refunded
RefundFailureReason.AmountTooHighTrying to perform refund with amount higher than original payment
RefundFailureReason.RefundExpiredPayment refund is too old to be refunded
RefundFailureReason.InsufficientFundsAccount does not have sufficient funds to perform refund
RefundFailureReason.PartialRefundNotSupportedPartial refund is not allowed for this payment

Extracting the success payload

A successfull refund contains a payload of type RefundPayload which has fields describing the refund. They are listed in the table below.

NameType Description
originalAmountLongTotal original card payment amount (also includes tip amount if applicable)
refundedAmountLong
cardTypeString?card brand: VISA, MASTERCARD and so on
maskedPanString?e.g. "** ** **** 1111"
cardPaymentUUIDString

Card reader settings

To provide a way to access card reader settings from outside the payment flow, you can use the following static method to get the intent.

1
val intent = CardReadersActivity.newIntent(context)
2
startActivity(intent)