> This page is for Afterpay.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.cash.app/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.cash.app/_mcp/server.

# 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](/cash-app-afterpay/guides/api-development/additional-features/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](/cash-app-afterpay/guides/sdk/android-sdk/more/checkout-v1), which is deprecated and supports standard checkout only. Use Checkout V2 instead.

To accept Cash App Pay, see [Cash App Pay](/cash-app-afterpay/guides/sdk/android-sdk/implementation-guide/cash-app-pay). For a front-end-only integration that returns a one-time virtual card, see the [Afterpay Button](/cash-app-afterpay/guides/sdk/android-sdk/more/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](/cash-app-afterpay/guides/sdk/android-sdk/implementation-guide/cash-app-pay).

```mermaid
%%{
  init: {
    'theme': 'base',
    'themeVariables': {
      'primaryColor': '#FFF',
      'primaryTextColor': '#000',
      'primaryBorderColor': '#000',
      'lineColor': '#000',
      'secondaryColor': '#fff',
      'tertiaryColor': '#fff',
      'noteBkgColor': '#fff',
      'noteBorderColor': '#000',
      'background': '#fff'
    }
  }
}%%
sequenceDiagram
    participant App
    participant SDK as Afterpay SDK
    participant Server as Merchant server
    participant API as Afterpay API
    App->>SDK: Start checkout intent
    SDK->>App: Request checkout token
    App->>Server: Request checkout
    Server->>API: POST /v2/checkouts
    API-->>Server: Checkout token
    Server-->>App: Checkout token
    App->>SDK: Checkout token
    SDK-->>App: Activity result (order token)
    App->>Server: Complete order
    Server->>API: Capture or authorize payment
```

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:

#### Kotlin DSL

```kotlin
android {
  compileOptions {
    isCoreLibraryDesugaringEnabled = true
  }
}

dependencies {
  implementation("com.afterpay:afterpay-android:4.9.0")
  coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.2")
}
```

#### Groovy

```groovy
android {
  compileOptions {
    coreLibraryDesugaringEnabled 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`](https://github.com/afterpay/sdk-android/blob/master/afterpay/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](/cash-app-afterpay/guides/sdk/android-sdk/more/afterpay-button).

Your server fetches the configuration from the [Get Configuration](/cash-app-afterpay/api-reference/reference/configuration/get-configuration) endpoint and passes it to your app:

```kotlin
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](/cash-app-afterpay/api-reference/reference/checkouts/create-checkout-1) and returns the `token` from the response:

```json
{
  "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"
}
```

> **Warning**
>
> 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](/cash-app-afterpay/guides/api-development/additional-features/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`.

```kotlin
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](/cash-app-afterpay/guides/sdk/android-sdk/ui-components/payment-button).

Launch the intent from `createCheckoutV2Intent` with an activity result launcher:

```kotlin
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:

| Option                   | Effect                                                                                                                                                                                                     |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `buyNow`                 | Shows 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. |
| `pickup`                 | For 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.                                        |
| `shippingOptionRequired` | For 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:

```kotlin
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](/cash-app-afterpay/api-reference/reference/payments/capture-full-payment) with the token. To capture later, call [Auth](/cash-app-afterpay/api-reference/reference/payments/auth) and then [Capture Payment](/cash-app-afterpay/api-reference/reference/payments/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](https://developer.android.com/privacy-and-security/security-config#CertificatePinning). 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">`:

```xml
<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](https://github.com/afterpay/sdk-android/tree/master/sample) uses the [example server](https://github.com/afterpay/sdk-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`.

   ```bash
   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.

   ```properties
   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](/cash-app-afterpay/guides/api-development/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.