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.
Start by creating a TransactionReference
object using the builder provided.
Important: The transaction reference object must contain at least one unique field.
1val internalTraceId = UUID.randomUUID().toString()2val 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.
1val intent = CardPaymentActivity.IntentBuilder(this)2// MANDATORY: Transaction amount in account currency3.amount(20000L)4// MANDATORY, Reference object created in previous step5.reference(reference)6// MANDATORY, enable login prompt in the payment flow if user is not yet logged-in7.enableLogin(enableLogin)8// OPTIONAL, you can enable tipping (disabled by default)9// This option will only work for markets with tipping support10.enableTipping(true)11// OPTIONAL, you can enable installments (enabled by default)12// This option will only work for markets with installments support13.enableInstalments(enableInstallments)14.build()1516// Start activity with the intent17startActivityForResult(intent, 0)
Note: If setting
enableLogin
totrue
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.
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.
You will receive the payment result as an Activity
result. The result Bundle
contains two values:
Name | Type | Description |
---|---|---|
CardPaymentActivity.RESULT_EXTRA_REQUEST | Bundle | The extras bundle from request intent. |
CardPaymentActivity.RESULT_EXTRA_PAYLOAD | CardPaymentResult | The payment result. |
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.Canceled | Payment was canceled by merchant or customer. Doesn't contain any additional data. |
CardPaymentResult.Failed | Payment failed. The failure reason is defined by the reason field. |
CardPaymentResult.Completed | Card 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.TechnicalError | Payment failed because of technical issues. Can be caused by a Bluetooth communication problem or other technical issue. |
FailureReason.NetworkError | Communication with iZettle servers failed |
FailureReason.NotAuthorized | There is no authorised user to process payment request |
FailureReason.AboveMaximum | Requested amount is greater than account limit |
FailureReason.BellowMinimum | Requested 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.
Name | Type | Description |
---|---|---|
amount | Long | Total transaction amount (also includes tip amount if applicable) |
gratuityAmount | Long? | Contains total tip amount if tipping is performed, null otherwise |
cardType | String? | Card brand: VISA, MASTERCARD and so on. |
cardPaymentEntryMode | String? | EMV, CONTACTLESS_EMV, MAGSTRIPE_CONTACTLESS, MAGSTRIPE etc. More entry modes might be added independent of SDK version |
cardholderVerificationMethod | String? | |
tsi | String? | EMV tags |
tvr | String? | EMV tags |
applicationIdentifier | String? | EMV tags (aid) |
cardIssuingBank | String? | Card issuing bank if provided |
maskedPan | String? | For example "** ** **** 1111" |
panHash | String? | Card pan hash |
applicationName | String? | |
authorizationCode | String? | |
installmentAmount | Long | Value of each installment |
nrOfInstallments | Int | Number of installment chosen |
mxFiid | String? | Mexico-specific data |
mxCardType | String? | Mexico specific data |
reference | TransactionReference? | Your reference object |
referenceNumber | String? | Zettles reference to the payment (not to be confused with the reference provided by you) |
transactionId | String? | The ID for transaction in the Zettle system. The ID is automatically generated by the Zettle system. |
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.
1IZettleSDK.refundsManager.retrieveCardPayment(internalTraceId, object : RefundsManager.Callback<CardPaymentPayload, RetrieveCardPaymentFailureReason> {2override fun onFailure(reason: RetrieveCardPaymentFailureReason) { /*...*/ }3override fun onSuccess(cardPaymentPayload: CardPaymentPayload) { /*...*/ }4})
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.
1val internalTraceId = UUID.randomUUID().toString()2val 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.
1val intent = RefundsActivity.IntentBuilder(cardPaymentPayload)2// Refund amount in account currency3// This amount must be less or equal to the original card payment.4// If not provided it will use original card payment amount5.refundAmount(20000L)6// Reference object created in previous step7.reference(reference)8// Optional, you can provide tax amount of this card payment to be displayed in the UI9.taxAmount(100L)10// Optional, you can provide the receipt number of this card payment to be displayed in the UI11.receiptNumber("#12345")12.build()1314// Start activity with the intent15startActivityForResult(intent, 0)
You will receive the refund result as an Activity
result. The result Bundle
contains two values:
Name | Type | Description |
---|---|---|
RefundsActivity.RESULT_EXTRA_REQUEST | Bundle | The extras bundle from request intent. |
RefundsActivity.RESULT_EXTRA_PAYLOAD | RefundResult | The refund result. |
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.Canceled | Refund was canceled by merchant. Doesn't contain any additional data. |
RefundResult.Failed | Refund failed. The failure reason is defined by the reason field. |
RefundResult.Completed | Card 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.Failed | Failure due to unknown reasons |
RefundFailureReason.NotAuthorized | There is no authorised user to process payment request |
RefundFailureReason.NotFound | Payment with given reference id was not found |
RefundFailureReason.NotRefundable | Payment is not refundable |
RefundFailureReason.NetworkError | Communication with Zettle servers failed |
RefundFailureReason.TechnicalError | Payment failed because of technical issues |
RefundFailureReason.AlreadyRefunded | Payment was already refunded |
RefundFailureReason.AmountTooHigh | Trying to perform refund with amount higher than original payment |
RefundFailureReason.RefundExpired | Payment refund is too old to be refunded |
RefundFailureReason.InsufficientFunds | Account does not have sufficient funds to perform refund |
RefundFailureReason.PartialRefundNotSupported | Partial 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.
Name | Type | Description |
---|---|---|
originalAmount | Long | Total original card payment amount (also includes tip amount if applicable) |
refundedAmount | Long | |
cardType | String? | card brand: VISA, MASTERCARD and so on |
maskedPan | String? | e.g. "** ** **** 1111" |
transactionId | String | The ID for transaction in the Zettle system. The ID is automatically generated by the Zettle system. It replaces cardPaymentUUID . |
cardPaymentUUID (Deprecated) | String |
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.
1val intent = CardReadersActivity.newIntent(context)2startActivity(intent)
When integrating with the SDK, you can test your integration in developer mode for card without a Zettle merchant account and real transactions.
If developer mode is not enabled, enable it by calling the following function in your integration project:
1IZettleSDK.init(2...,3isDevMode = true4)Launch the payment flow.
Check the responses.
The example shows test case "Successfully paid".
1"payload": {2"amount": <the payment amount you passed to SDK>, // If `enableTipping` set to `true`, a gratuity amount `300` will be added3"applicationIdentifier": "A0000000031010",4"applicationName": "Visa Credit",5"authorizationCode": "123456",6"cardIssuingBank": null,7"cardPaymentEntryMode": "CONTACTLESS_EMV",8"cardType": "VISA",9"cardholderVerificationMethod": "None",10"gratuityAmount": null, // If `enableTipping` set to `true`, the returned gratuity amount is `300`11"installmentAmount": 0,12"maskedPan": "************0119",13"mxCardType": null,14"mxFiid": null,15"mxPaymentMethodCode": null,16"nrOfInstallments": 0,17"panHash": "01086BED527FDA321CC8718F44839CD909378B4C",18"referenceNumber": "G5363OXS4K",19"transactionId": "bdcf72bc-6037-11ed-a055-e2294a8ade3d",20"tsi": null,21"tvr": "0000000000",22"reference": {23"id": "<the reference passed to the flow>"24}25}Launch the refund flow.
Check the responses.
1"payload": {2"cardType": "VISA",3"maskedPan": "************0119",4"originalAmount": 1000,5"refundAmount": <the refund amount you passed to the SDK>,6"transactionId": "2420db84-6041-11ed-a687-0fe1acbcd38a"7}