Skip to navigation

Android implementation guide

This guide walks you through integrating the Android SDK: install and configure it, create checkouts on your server, present checkout in your app, and complete the order.

When the customer taps your payment button, the SDK shows a loading screen and asks your app for a checkout token. Your app gets the token from your server and passes it to the SDK, which opens Afterpay checkout in a web view. The customer stays in your app, and the SDK receives the result directly from checkout, so your app doesn’t handle redirect URLs. You can offer standard checkout or express checkout, where the customer chooses a shipping address and shipping option in checkout.

This guide uses the SDK’s Checkout V2 API.

Checkout V1 (Deprecated)

The SDK also includes Checkout V1, which is deprecated and supports standard checkout only. Use Checkout V2 instead.

To accept Cash App Pay, see Cash App Pay. For a front-end-only integration that returns a one-time virtual card, see the Afterpay Button, available in the US and Canada.

How it works

Your server creates the checkout with your merchant credentials, the SDK presents checkout to the customer, and your server completes the payment. Your merchant credentials never leave your server.

Cash App Pay uses the same server flow, but Pay Kit sends the customer to Cash App instead of the SDK presenting checkout. For the steps, see Cash App Pay.

Start checkout intent Request checkout token Request checkout POST /v2/checkouts Checkout token Checkout token Checkout token Activity result (order token) Complete order Capture or authorize payment App Afterpay SDK Merchant server Afterpay API

The Afterpay Button works differently: the SDK creates the checkout itself and returns a virtual card, which you process like any other card payment.

Step 1: Install the SDK

Add the SDK to your module’s build.gradle.kts or build.gradle file. The SDK uses Java 8+ APIs, so also enable core library desugaring:

android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
}
dependencies {
implementation("com.afterpay:afterpay-android:4.9.0")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.2")
}

R8 applies the SDK’s shrinking and obfuscation rules automatically. If you use ProGuard, apply the rules in consumer-rules.pro manually.

Step 2: Configure the SDK

The SDK uses your merchant configuration to show UI components with the right branding, terms, and currency format. Checkout and Cash App Pay also require it. The Afterpay Button uses its own configuration, described in Afterpay Button.

Your server fetches the configuration from the Get Configuration endpoint and passes it to your app:

val configuration = api.getConfiguration()
Afterpay.setConfiguration(
minimumAmount = configuration.minimumAmount?.amount,
maximumAmount = configuration.maximumAmount.amount,
currencyCode = configuration.maximumAmount.currency,
locale = Locale.US,
environment = AfterpayEnvironment.SANDBOX,
consumerLocale = Locale.ENGLISH, // Optional. Overrides the device language.
)
  • Call setConfiguration on the main thread, before you start a checkout or initialize any UI component.
  • Use AfterpayEnvironment.SANDBOX for development and AfterpayEnvironment.PRODUCTION for release builds. The environment must match the environment your server uses to create checkouts.
  • Refresh the configuration about once a day, because your merchant configuration can change.
  • A UK locale displays Clearpay branding, terms, and currency formatting.

Step 3: Create a checkout on your server

When the SDK asks for a token, your app calls your server. Your server calls Create Checkout and returns the token from the response:

{
"amount": { "amount": "100.00", "currency": "USD" },
"consumer": { "email": "test@example.com" },
"merchant": {
"redirectConfirmUrl": "https://example.com/checkout/confirm",
"redirectCancelUrl": "https://example.com/checkout/cancel",
"popupOriginUrl": "https://static.afterpay.com"
},
"merchantReference": "merchant-order-123"
}

Set merchant.popupOriginUrl to https://static.afterpay.com. The SDK opens checkout from this origin and receives the result from it, so checkout doesn’t work correctly without it.

For express checkout, also set mode to express. For the other express fields, see Express checkout.

Step 4: Set a checkout handler

The SDK calls your AfterpayCheckoutV2Handler to get the checkout token and, for express checkout, the shipping options. Register it with Afterpay.setCheckoutV2Handler before you start checkout. The SDK holds the handler in a global reference until you replace it or pass null.

private val checkoutHandler = object : AfterpayCheckoutV2Handler {
override fun didCommenceCheckout(
onTokenLoaded: (Result<String>) -> Unit,
) {
lifecycleScope.launch {
// Call your server's checkout endpoint.
val token = runCatching { checkoutService.createCheckout() }
onTokenLoaded(token)
}
}
override fun shippingAddressDidChange(
address: ShippingAddress,
onProvideShippingOptions: (ShippingOptionsResult) -> Unit,
) {
// Express checkout only. See Step 6.
}
override fun shippingOptionDidChange(
shippingOption: ShippingOption,
onProvideShippingOption: (ShippingOptionUpdateResult?) -> Unit,
) {
// Express checkout only. See Step 6.
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Afterpay.setCheckoutV2Handler(checkoutHandler)
}
override fun onDestroy() {
// The SDK holds the handler globally. Clear it to avoid leaking the activity.
Afterpay.setCheckoutV2Handler(null)
super.onDestroy()
}

If you pass a failure to onTokenLoaded, the SDK shows a dialog that lets the customer retry or close checkout. Call onTokenLoaded on the main thread, because the SDK shows the error dialog on the calling thread.

Step 5: Start the checkout

Start checkout when the customer taps your payment button.

Launch the intent from createCheckoutV2Intent with an activity result launcher:

private val checkoutLauncher =
registerForActivityResult(StartActivityForResult()) { result ->
val data = result.data ?: return@registerForActivityResult
when (result.resultCode) {
RESULT_OK -> {
val token = Afterpay.parseCheckoutSuccessResponse(data)
// Send the token to your server to complete the order.
}
RESULT_CANCELED -> {
val status = Afterpay.parseCheckoutCancellationResponse(data)
// Handle the cancellation.
}
}
}
private fun onAfterpayButtonClicked() {
val options = AfterpayCheckoutV2Options(buyNow = true)
checkoutLauncher.launch(Afterpay.createCheckoutV2Intent(this, options))
}

If you haven’t set the SDK configuration, or the customer’s language isn’t supported in your region, checkout closes with the cancellation status LANGUAGE_NOT_SUPPORTED. If you haven’t set the handler, it closes with NO_CHECKOUT_HANDLER. If the customer closes checkout, the status is USER_INITIATED.

Checkout options

Pass AfterpayCheckoutV2Options to change the customer’s experience in checkout:

OptionEffect
buyNowShows a Buy Now button at the end of checkout, which signals that you capture the payment right away. Set it to true only if you capture the payment immediately, without a review step in your app.
pickupFor express checkout. Lets you set a pickup address in the shipping object when you create the checkout, and stops the customer from choosing a delivery address.
shippingOptionRequiredFor express checkout. Set it to false to stop the customer from choosing a shipping option in checkout.

Step 6: Provide shipping options (express checkout)

For express checkout, the SDK calls your handler when the customer chooses a shipping address and when they choose a shipping option. Return the shipping options for the address, or an error:

override fun shippingAddressDidChange(
address: ShippingAddress,
onProvideShippingOptions: (ShippingOptionsResult) -> Unit,
) {
val usd = Currency.getInstance("USD")
val standard = ShippingOption(
id = "standard",
name = "Standard",
description = "3-5 days",
shippingAmount = Money(BigDecimal("5.00"), usd),
orderAmount = Money(BigDecimal("105.00"), usd),
taxAmount = null,
)
onProvideShippingOptions(ShippingOptionsSuccessResult(listOf(standard)))
}
override fun shippingOptionDidChange(
shippingOption: ShippingOption,
onProvideShippingOption: (ShippingOptionUpdateResult?) -> Unit,
) {
// Return an updated amount for the option, or null to keep it.
onProvideShippingOption(null)
}
  • The SDK calls both methods on a background thread. Call your server from them, and pass the result to the callback from any thread.
  • orderAmount is the order total including shipping and tax.
  • To reject an address, pass ShippingOptionsErrorResult with SHIPPING_ADDRESS_UNRECOGNIZED, SHIPPING_ADDRESS_UNSUPPORTED, SERVICE_UNAVAILABLE, or BAD_RESPONSE.
  • To update the selected option, pass ShippingOptionUpdateSuccessResult with a ShippingOptionUpdate. To report an error, pass ShippingOptionUpdateErrorResult with SERVICE_UNAVAILABLE or BAD_RESPONSE.

Step 7: Complete the order

Send the order token to your server. To capture the full amount immediately, your server calls Capture Full Payment with the token. To capture later, call Auth and then Capture Payment.

Security

To reduce the risk of a man-in-the-middle attack during checkout, pin the certificate for the Afterpay portal with an Android network security configuration. Add a domain configuration to res/xml/network_security_config.xml, and reference the file from your manifest with <application android:networkSecurityConfig="@xml/network_security_config">:

<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain>portal.afterpay.com</domain>
<pin-set expiration="YYYY-MM-DD">
<pin digest="SHA-256">YOUR_PIN_BASE64</pin>
</pin-set>
</domain-config>
</network-security-config>

Keep the pins and the expiration date up to date. When the certificate rotates, outdated pins block checkout until the expiration date passes.

Test with the sample app

The sample app uses the example server, which creates sandbox checkouts with your merchant credentials. It includes screens for Checkout V2, the Afterpay Button, Cash App Pay with the Afterpay Button, and the UI components.

  1. Start the example server. It requires Node.js and listens on http://localhost:3000. In .env, set AFTERPAY_MERCHANT_ID and AFTERPAY_SECRET_KEY to your sandbox credentials, and set AFTERPAY_REGION, for example en-US.

    git clone https://github.com/afterpay/sdk-example-server.git
    cd sdk-example-server
    cp .env.example .env
    npm install
    npm run build
    npm run start
  2. Clone the SDK and add your merchant ID to local.properties in the project root. The sample uses it as the shopDirectoryMerchantId for the Afterpay Button.

    merchantId="your_merchant_id"
  3. Run the sample module on an Android emulator. The sample connects to the example server at http://10.0.2.2:3000, which is the emulator’s address for your computer. To use a different address, change MERCHANT_API_BASE_URL in sample/src/main/java/com/example/api/MerchantApi.kt.

  4. Start a checkout and log in with a sandbox customer account. For details, see Test environments.

Debug builds use a mock checkout

The sample builds the SDK from source. In debug builds, the SDK replaces the checkout page and the widget scripts with local mocks. The mock checkout immediately returns a test order token, so the checkout closes right after it opens. To test against the sandbox, build the sample’s release variant with a signing configuration, or depend on the published com.afterpay:afterpay-android artifact instead of the local module.