Authorise payments with the Android SDK
Learn how to install and set up the SDK so your users can authorise payments.
The Android SDK enables your users to authorise payments that you've made through the /v3/payments
endpoint.
The process for authorising payments is very similar to the one for authorising mandates. However, if you want to authorise mandates with the Android SDK, you should check our guide for mandates.
Before you start
Register a return URI in Console. Go to Console > Settings > Allowed redirect URIs to do this. Your user is redirected back to your redirect URL, typically your website or application, at the end of the payment journey.
You also need an integration that can create a payment, and get a payment id
, to start the Android SDK. Learn more about how to create a payment, and authenticate, and sign your requests (our payment quickstart guide provides an overview).
Configuration overview for payments with the Android SDK
There are four steps to configuring the Android SDK with the Payments API v3:
1. Install the SDK
Before you add the SDK to your project, ensure your build.gradle
configuration is configured to support minSdk
24 (Android 7.0) or higher, and configure the packagingOptions
below:
android {
defaultConfig {
applicationId "com.example.myapp"
minSdk 24 // Sdk 24 or higher supported
}
packagingOptions {
resources {
pickFirsts += ['META-INF/LICENSE-MIT']
}
}
}
To add the SDK to your project, include the TrueLayer Payments SDK in your dependencies (you can find the latest version on our GitHub page):
// Add to your projects `build.gradle`.
implementation "com.truelayer.payments:ui:3.1.0"
If you plan to support Android versions below 8.0, then you need to make sure your project has desugaring enabled. This is important because without desugaring, in specific scenarios, the SDK may crash on devices running Android API versions below 26.
The following code example shows how to enable desugaring in your project.
```groovy
android {
// this part will enable core library desugaing
compileOptions {
coreLibraryDesugaringEnabled true
}
// this part will remove excess LICENSE-MIT files
packagingOptions {
resources {
pickFirsts += [‘META-INF/LICENSE-MIT’]
}
}
}
dependencies {
// Add to your projects `build.gradle`.
// We are currently using following version of desuga libraries
coreLibraryDesugaring “com.android.tools:desugar_jdk_libs:2.0.4”
}
```
2. Initialise the SDK
To use the SDK, you have to initialise it first before invoking any other SDK method.
This is an example of how to initialise the SDK:
import com.truelayer.payments.core.domain.configuration.Environment
import com.truelayer.payments.core.domain.configuration.HttpConnectionConfiguration
import com.truelayer.payments.core.domain.configuration.HttpLoggingLevel
import com.truelayer.payments.ui.TrueLayerUI
// Initialize the SDK with your application context.
TrueLayerUI.init(context = applicationContext) {
// optionally choose which environment you want to use: PRODUCTION or SANDBOX
environment = Environment.PRODUCTION
// Make your own custom http configuration, stating custom timeout and http request logging level
httpConnection = HttpConnectionConfiguration(
httpDebugLoggingLevel = HttpLoggingLevel.None
)
}
Initialise with AppStartup
If you are using AppStartup for your integration, you need to also add WorkManagerInitializer
to your dependencies:
import androidx.work.WorkManagerInitializer
(...)
override fun dependencies(): List<Class<out Initializer<*>>> {
return listOf(WorkManagerInitializer::class.java)
3. Process the payment
Depending on your requirements, you can integrate the Android SDK into your payment process with either AndroidX Activity, the older Android Activity or with Jetpack Compose.
Whichever approach you use, you need to provide the id
and the resourceToken
obtained from the backend at payment creation. Pass the id
and resourceToken
to the SDK via PaymentContext
.
The following kdoc
explains all the available parameters:
/**
* ProcessorContext to identify payment to be processed
* [Payment API](https://docs.truelayer.com/)
*
* @property id payment identifier obtained from the
* create payment API [create payment API](https://docs.truelayer.com/reference/create-payment)
* @property resourceToken resource token obtained from
* the create payment API [create payment API](https://docs.truelayer.com/reference/create-payment)
* @property redirectUri uri that will be invoked once payment authorization is completed
*/
@Parcelize
sealed class ProcessorContext : Parcelable {
abstract val id: String
abstract val resourceToken: String
abstract val redirectUri: String
/**
* PaymentContext to identify payment to be processed
* [Payments API](https://docs.truelayer.com/docs/create-a-payment)
*
* @property id payment identifier obtained from create payment API [create payment API](https://docs.truelayer.com/reference/create-payment)
* @property resourceToken resource token obtained from create payment API [create payment API](https://docs.truelayer.com/reference/create-payment)
* @property redirectUri uri that will be invoked once payment authorization is completed
* @property preferences object containing various preferences that allow to override default SDK
* behaviour in certain aspects.
*/
@Parcelize
data class PaymentContext(
override val id: String,
override val resourceToken: String,
override val redirectUri: String,
val preferences: PaymentPreferences? = null
) : ProcessorContext()
/**
* Preferences class that allows to set preferences and override default SDK behaviour
* in certain aspects.
*
* @property preferredCountryCode (optional) in case there are available payment providers from
* multiple countries the SDK will try select most appropriate one automatically.
* You may want to override that behaviour.
* By setting the preferredCountryCode (ISO 3166-1 alpha-2, example: "GB" or "FR"),
* the SDK will try first to select preferred country before falling back to the
* auto selection.
* @property shouldPresentResultScreen (optional) true if the result screen should be presented
* before the final redirect to the merchant app. Default is true.
* @property waitTimeMillis the total time the result screen will wait to get a final status of the payment
* Default is 3 seconds. Minimum is 2 seconds. Maximum is 10 seconds.
*/
@Parcelize
data class PaymentPreferences(
val preferredCountryCode: String? = null,
val shouldPresentResultScreen: Boolean = true,
val waitTimeMillis: Long = 3_000
)
Changes in 3.8.0 for Android and AndroidX Activity
If you are using release 3.8.0, ensure that the Activity that starts the SDK is
android:launchMode="standard"
.This is to make sure that the Activity is not recreated when the SDK redirects the user back from their bank app. Otherwise, the result screen may display multiple times.
AndroidX Activity integration
This example shows how to process a payment with an AndroidX Activity integration:
import android.widget.Toast
import com.truelayer.payments.ui.screens.processor.ProcessorContext.PaymentContext
import com.truelayer.payments.ui.screens.processor.ProcessorActivityContract
import com.truelayer.payments.ui.screens.processor.ProcessorResult
// Register for the end result.
val contract = ProcessorActivityContract()
val processorResult = registerForActivityResult(contract) {
val text = when (it) {
is ProcessorResult.Failure -> {
"Failure ${it.reason}"
}
is ProcessorResult.Successful -> {
"Successful ${it.step}"
}
}
// present the final result
Toast.makeText(this, text, Toast.LENGTH_LONG).show()
}
// Obtain your payment context from your backend
val paymentContext = PaymentContext(
id = "your-payment-identifier",
resourceToken = "payment-resource-token",
redirectUri = "redirect-uri-that-will-be-invoked-when-coming-back-from-bank"
)
// 🚀 Launch the payment flow.
processorResult.launch(paymentContext)
Android Activity integration
This example shows how to process a payment with an Android Activity integration:
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import com.truelayer.payments.core.domain.configuration.Environment
import com.truelayer.payments.ui.TrueLayerUI
import com.truelayer.payments.ui.screens.processor.ProcessorContext.PaymentContext
import com.truelayer.payments.ui.screens.processor.ProcessorActivityContract
import com.truelayer.payments.ui.screens.processor.ProcessorResult
class ActivityIntegration : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialise the payments configuration
TrueLayerUI.init(context = applicationContext) {
environment = Environment.PRODUCTION
httpConnection = HttpConnectionConfiguration(
timeoutMs = 35000,
httpDebugLoggingLevel = HttpLoggingLevel.None
)
}
// Obtain your payment context from your backend
val paymentContext = PaymentContext(
id = "your-payment-identifier",
resourceToken = "payment-resource-token",
redirectUri = "redirect-uri-that-will-be-invoked-when-coming-back-from-bank"
)
// Create an intent to launch the SDK
val intent = ProcessorActivityContract().createIntent(this, paymentContext)
// 🚀 Launch the SDK
startActivityForResult(intent, 0)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Act on the SDK result
val text = when (val result = ProcessorResult.unwrapResult(data)) {
is ProcessorResult.Failure -> "Failure ${result.reason}"
is ProcessorResult.Successful -> "Successful ${result.step}"
null -> "Activity result failed."
}
Toast.makeText(this, text, Toast.LENGTH_LONG).show()
}
}
Jetpack Compose integration
This example shows how to process a payment with a Jetpack Compose integration:
// Your payment's custom theme or use the provided defaults as below
val theme = TrueLayerTheme(
lightPalette = LightColorDefaults,
darkPalette = DarkColorDefaults,
typography = TypographyDefaults
)
// Obtain your payment context from your backend
val paymentContext = PaymentContext(
id = "your-payment-identifier",
resourceToken = "payment-resource-token",
redirectUri = "redirect-uri-that-will-be-invoked-when-coming-back-from-bank"
)
setContent {
Theme(
theme = theme
) {
Processor(
context = paymentContext,
onSuccess = { successStep ->
// action on success
},
onFailure = { failureReason ->
// action on failure
},
)
}
}
4. Handle redirects and display the payment result
Once the redirects are complete, you receive a success or error callback, which renders in the payment result screen.
Handle redirects from the provider
At the end of a redirect flow the bank app will relaunch your app with the return URI you provided in Console.
In your activity that launches when a deep link is triggered, fetch the redirect parameters with the method below.
val params = intent.data.extractTrueLayerRedirectParams()
This must include the payment_id
. For more information on handling deep links, follow the documentation here.
Whenever you are redirected to your app, you should reinvoke the SDK, until you receive a success
or error
callback.
By default the SDK offers a payment result screen, which displays the result of the payment and advises the user on what to do in case of a failed payment. If you disable the payment result screen, you can use the success
or error
callback to render a screen for your user when they return to your app.
Success callbacks
A success result means that the SDK has completed its role in initiating the payment. However, it doesn't necessarily mean that the payment itself is complete.
The table below shows all the possible responses:
Response | Description |
---|---|
Redirect | The user was redirected to their provider to authorise the payment. [Removed in 3.8.0] |
Wait | The SDK flow is complete, but a decoupled authorisation action is still pending with the user and/or their provider. |
Authorized | The user has authorised the payment with their provider. |
Successful | The bank has confirmed that they have accepted the payment request, and they intend to execute it. |
Settled | The payment has reached its destination and the money is in the payee's account. |
ResultShown | The result that was displayed to the user on the payment result screen. |
Error callbacks
A payment can fail for a variety of reasons. You can use the error callback to figure out why a payment failed and whether you can resolve it. The error callbacks are shared between payments and mandates.
Expand to see all payment failure callbacks
Payment result | Description |
---|---|
NoInternet | There was a problem connecting to the internet. This usually happens when the user has no internet connection. This can also be caused by a network configuration, if no route to TrueLayer services can be established. |
UserAborted | The user purposefully ended the payment process. |
UserAbortedFailedToNotifyBackend | The user did not continue with the payment but the application failed to notify the backend, or the backend responded with an error. |
CommunicationIssue | Communication with the backend has failed. It may be an invalid payment token, or just the payment service responding in an unexpected manner. |
ConnectionSecurityIssue | A secure connection to payment services could not be established. |
PaymentFailed | The payment is in the failed state. A recovery from this state is not possible. |
WaitAbandoned | The user purposefully abandoned the payment flow while the flow was awaiting authorisation from the bank. The authorisation from the bank may or may not depend on a user action, so the payment may still go through. |
WaitTokenExpired | The authorisation waiting time was so long that the resource token has expired. We are no longer able to query the payment status. |
ProcessorContextNotAvailable | A technical failure. It was not possible to read the ProcessorContext in which you set the payment/mandate id and resource_token . If you see this error, check that you're following the steps in this guide correctly, or raise a support ticket with us if you need further help. |
Unknown | The SDK was unable to identify a reason for the failure. |
ResultShown | The result that was displayed to the user on the payment result screen. |
Get the payment status
As a best practice, we recommend that you obtain the payment status from your backend through webhook notifications.
However, the SDK also enables you to fetch the current status of a payment.
val outcome = TrueLayerUI.getPaymentStatus("your-payment-identifier", "payment-resource-token")
For more details on these statuses, see the API reference page the /v3/payments/{id}
endpoint.
Display the payment result screen
The payment result screen is enabled by default. To display it to your user after the payment is authorised (or fails) you must relaunch the SDK using the same PaymentContext
you used when you first processed the payment.
Possible payment results and callbacks
When your user sees the payment result screen, you receive a ResultShown
callback. This applies for both successful and failed payments, and contains the following properties, which explain the result the user saw:
List of payment result callbacks
ResultShown property | Description |
---|---|
None | No result screen was shown because it's either been opted out by the client or the user has been redirected. |
Initiated | The payment did not reach a final state and the user was shown the "Payment in progress" screen. |
Success | The payment was completed successfully. |
InsufficientFunds | The payment failed due to insufficient funds. |
PaymentLimitExceeded | The payment failed due to exceeding the payment limit. |
UserCanceledAtProvider | The payment failed due to the user cancelling at the provider. |
AuthorizationFailed | The payment failed due to authorization failure. |
Expired | The payment failed due to expiring. |
InvalidAccountDetails | The payment failed due to the invalid account details. |
InvalidGenericWithoutRetry | The payment failed and retry is not possible. |
Failed | The payment failed to be set up. |
How to disable the payment result screen
To disable the payment result screen, ensure you provide a value of false
for shouldPresentResultScreen
within PaymentPreferences
when you process the payment.
Updated about 2 months ago