> This page is for version v2 (default).
> For other versions, use one of these documentation indexes:
> - v2 (default): https://developers.clearpay.co.uk/v-2/llms.txt
> - v1: https://developers.clearpay.co.uk/v-1/llms.txt

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

# Mobile SDK quickstart

This quickstart walks you through adding Clearpay to an iOS or Android app, from your first server request to your first captured sandbox payment. You set up two server endpoints, add the SDK to your app, present checkout, and capture the payment.

The SDK is published as the Afterpay SDK. With a UK configuration, it shows Clearpay branding. The quickstart uses Checkout V2, the recommended way to present checkout.

## Before you begin

Make sure that you have the following:

* A Clearpay sandbox merchant ID and secret key. Your Clearpay account manager provides them. For details, see [Test environments](/guides/api-development/test-environments).
* A backend server that you control. Your server calls the Clearpay API with your secret key. Never put your secret key in your app.
* For iOS: Xcode 14.0 or later and an app that targets iOS 12.0 or later.
* For Android: Android Studio and an app with a minimum SDK of API level 24 (Android 7.0) or later.

> **WebView support**
>
> Clearpay checkout runs in a web view. On iOS, it requires iOS 13.4 or later. On Android, it depends on the WebView version installed on the device. For details, see the [iOS SDK](/guides/sdk/ios-sdk) and [Android SDK](/guides/sdk/android-sdk) overviews.

## How it works

Your server holds your Clearpay credentials and makes every Clearpay API call. Your app gets the merchant configuration and a checkout token from your server, and the SDK presents checkout to the customer.

```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
    Server->>API: GET /v2/configuration
    API-->>Server: Minimum and maximum amounts
    App->>Server: Request configuration
    Server-->>App: Configuration
    App->>SDK: Set configuration
    App->>SDK: Present checkout
    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
    Note over SDK: Customer logs in and confirms
    SDK-->>App: Checkout result (order token)
    App->>Server: Complete order
    Server->>API: POST /v2/payments/capture
    API-->>Server: Payment
    Server-->>App: Order result
```

## Step 1: Fetch the merchant configuration

The SDK needs your merchant configuration to show checkout and its UI components with the correct currency and order limits. Your server fetches the configuration from the [Get Configuration](/api-reference/get-configuration) endpoint.

```bash
curl https://global-api-sandbox.afterpay.com/v2/configuration \
  --user 'MERCHANT_ID:SECRET_KEY' \
  --header 'Accept: application/json' \
  --header 'User-Agent: YOUR_USER_AGENT'
```

The response contains your order limits. The response omits `minimumAmount` if your account has no minimum order amount.

```json
{
  "minimumAmount": {
    "amount": "10.00",
    "currency": "GBP"
  },
  "maximumAmount": {
    "amount": "2000.00",
    "currency": "GBP"
  }
}
```

Expose an endpoint on your server that returns this response to your app, for example `GET /afterpay/configuration`. Your configuration doesn't change often, so call Get Configuration on a schedule, about once a day, and serve the stored result.

## Step 2: Create a checkout

When the customer starts checkout, your app asks your server for a checkout. Your server calls the [Create Checkout](/api-reference/create-checkout) endpoint and returns the checkout token.

```bash
curl https://global-api-sandbox.afterpay.com/v2/checkouts \
  --request POST \
  --user 'MERCHANT_ID:SECRET_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'User-Agent: YOUR_USER_AGENT' \
  --data '{
    "amount": {
      "amount": "100.00",
      "currency": "GBP"
    },
    "consumer": {
      "email": "test@example.com",
      "givenNames": "Joe",
      "surname": "Consumer"
    },
    "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 that origin and receives the result as a message from checkout, so checkout doesn't behave as expected without it. Create Checkout still requires `redirectConfirmUrl` and `redirectCancelUrl`, but checkout doesn't redirect to them.

* Set `amount` from the order on your server. Clearpay rejects the checkout if the amount is outside your configured limits or if the currency differs from your account currency.
* Set `merchantReference` to your order ID so that you can match the payment to the order.

The response contains the checkout token, its expiry time, and a checkout URL that Checkout V2 doesn't use:

```json
{
  "token": "002.ejh49ovam0acussgg4c6slpqqtn1ntqt3m4efqdh4qe0u8qk",
  "expires": "2023-08-29T00:57:25.754Z",
  "redirectCheckoutUrl": "https://portal.sandbox.afterpay.com/..."
}
```

Expose an endpoint on your server that creates the checkout and returns the `token` to your app, for example `POST /afterpay/checkout`.

## Step 3: Install the SDK

#### Swift (iOS)

Add the SDK with Swift Package Manager. In Xcode, choose **File > Add Package Dependencies**, enter `https://github.com/afterpay/sdk-ios.git`, and add the `Afterpay` library to your app target. In a Swift package, add the dependency to `Package.swift`:

```swift
dependencies: [
  .package(
    url: "https://github.com/afterpay/sdk-ios.git",
    .upToNextMajor(from: "5.9.0")
  )
]
```

To install with CocoaPods or Carthage, see the [iOS SDK](/guides/sdk/ios-sdk) overview.

#### Kotlin (Android)

Add the SDK to your module's `build.gradle.kts`:

```kotlin
dependencies {
  implementation("com.afterpay:afterpay-android:4.9.0")
}
```

The SDK's manifest declares the checkout activity and the `INTERNET` permission, so you don't need to change your app's manifest.

## Step 4: Configure the SDK

Request the configuration from your server and pass it to the SDK. Set the configuration before you present checkout. Do this when your app starts, and again when you refresh the configuration.

#### Swift (iOS)

```swift
import Afterpay

struct AmountResponse: Decodable {
  let amount: String
  let currency: String
}

struct ConfigurationResponse: Decodable {
  let minimumAmount: AmountResponse?
  let maximumAmount: AmountResponse
}

func applyConfiguration(_ response: ConfigurationResponse) throws {
  let configuration = try Configuration(
    minimumAmount: response.minimumAmount?.amount,
    maximumAmount: response.maximumAmount.amount,
    currencyCode: response.maximumAmount.currency,
    locale: Locale(identifier: "en_GB"),
    environment: .sandbox
  )

  Afterpay.setConfiguration(configuration)
}
```

The `Configuration` initializer throws a `ConfigurationError` if an amount, the currency code, or the locale isn't valid.

#### Kotlin (Android)

```kotlin
data class AmountResponse(val amount: String, val currency: String)

data class ConfigurationResponse(
  val minimumAmount: AmountResponse?,
  val maximumAmount: AmountResponse,
)

// Call this on the main thread.
fun applyConfiguration(response: ConfigurationResponse) {
  Afterpay.setConfiguration(
    minimumAmount = response.minimumAmount?.amount,
    maximumAmount = response.maximumAmount.amount,
    currencyCode = response.maximumAmount.currency,
    locale = Locale.UK,
    environment = AfterpayEnvironment.SANDBOX,
  )
}
```

`setConfiguration` throws an `IllegalArgumentException` if the amounts, the currency code, or the locale isn't valid, and a `NumberFormatException` if an amount isn't a number.

* Set the locale to `en_GB` for a Clearpay merchant account in the UK. The SDK uses it for Clearpay branding, terms, and currency formatting.
* The SDK supports only the customer languages available in your region. For example, the `en_GB` locale supports English. The SDK uses the device language unless you pass the optional `consumerLocale` parameter.
* Use the sandbox environment for development. The environment must match the Clearpay API host that your server uses.
* Store the configuration and pass it to the SDK each time your app starts, before the customer can start a checkout.

## Step 5: Present checkout

This quickstart uses Checkout V2. For the details and express checkout, see the implementation guide for [iOS](/guides/sdk/ios-sdk/implementation-guide) or [Android](/guides/sdk/android-sdk/implementation-guide).

The SDK also supports Checkout V1, which is deprecated. For details, see Checkout V1 for [iOS](/guides/sdk/ios-sdk/more/checkout-v1) or [Android](/guides/sdk/android-sdk/more/checkout-v1).

When the customer taps your payment button, start checkout right away. The SDK shows a loading screen and asks your app for a checkout token. Call your server's checkout endpoint from Step 2 and pass the token to the SDK. The SDK opens Clearpay checkout in the app and returns a result when the customer finishes. A successful result contains the order token, which your server uses to complete the payment.

#### Swift (iOS)

Call `presentCheckoutV2Modally` from the view controller that shows your cart:

```swift
func didTapPayWithAfterpay() {
  Afterpay.presentCheckoutV2Modally(
    over: self,
    didCommenceCheckout: { completion in
      // Call your server's checkout endpoint.
      // Pass the token or an error to completion.
      self.checkoutService.createCheckout { result in
        completion(result)
      }
    },
    completion: { result in
      switch result {
      case .success(let token):
        // Send the token to your server to complete the order.
        self.completeOrder(token: token)
      case .cancelled(let reason):
        print("Checkout cancelled: \(reason)")
      }
    }
  )
}
```

* If you pass an error to the `didCommenceCheckout` completion, the SDK shows a dialog that lets the customer retry or close checkout.
* If you haven't set the configuration, debug builds trigger an assertion failure, and release builds return without presenting checkout or calling the completion handler.
* If the customer's language isn't supported in your region, `Afterpay.enabled` is `false`, and the SDK doesn't present checkout.

Checkout V2 has a Swift API for UIKit. For express checkout and checkout options, see [iOS implementation guide](/guides/sdk/ios-sdk/implementation-guide).

#### Kotlin (Android)

In the activity that shows your cart, register a checkout handler that gets the token from your server. Then launch the checkout intent with an activity result launcher:

```kotlin
class CartActivity : AppCompatActivity() {

  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,
    ) = Unit // Express checkout only.

    override fun shippingOptionDidChange(
      shippingOption: ShippingOption,
      onProvideShippingOption: (ShippingOptionUpdateResult?) -> Unit,
    ) = Unit // Express checkout only.
  }

  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.
        }
      }
    }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    Afterpay.setCheckoutV2Handler(checkoutHandler)
  }

  override fun onDestroy() {
    // The SDK holds the handler globally. Clear it to avoid a leak.
    Afterpay.setCheckoutV2Handler(null)
    super.onDestroy()
  }

  private fun onAfterpayButtonClicked() {
    checkoutLauncher.launch(Afterpay.createCheckoutV2Intent(this))
  }
}
```

* 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.
* If you haven't set the 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`.

For express checkout and checkout options, see [Android implementation guide](/guides/sdk/android-sdk/implementation-guide).

## Step 6: Capture the payment

Your app sends the order token to your server. To capture the full order amount immediately, your server calls the [Capture Full Payment](/immediate-payment-flow/capture-full-payment) endpoint with the token. Look up the order by the checkout token that your server stored in Step 2, rather than trusting order details sent by the app.

```bash
curl https://global-api-sandbox.afterpay.com/v2/payments/capture \
  --request POST \
  --user 'MERCHANT_ID:SECRET_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'User-Agent: YOUR_USER_AGENT' \
  --data '{
    "token": "ORDER_TOKEN",
    "merchantReference": "merchant-order-123"
  }'
```

The response is a payment object. Fulfill the order only if its `status` is `APPROVED`. If the `status` is `DECLINED`, don't fulfill the order. Then return the result to your app.

To capture later instead, for example when you ship the order, call [Auth](/deferred-payment-flow/auth) with the token. Then call [Capture Payment](/deferred-payment-flow/capture-payment) with the `id` from the Auth response. An authorization expires after 13 days.

## Step 7: Test in the sandbox

1. Run your server with your sandbox credentials and the sandbox host `https://global-api-sandbox.afterpay.com`.
2. Run your app with the sandbox environment and start a checkout.
3. In checkout, create a sandbox customer account with a unique email address and phone number. The sandbox doesn't send SMS messages, so enter `111111` as the verification code.
4. Pay with a test card, such as Visa `4111 1111 1111 1111` with a future expiry date. Use CVV `000` for an approved payment and `051` for a declined payment.
5. Confirm that your server captures the payment and that your app shows the result.

For more test cards and sandbox accounts, see [Test environments](/guides/api-development/test-environments).

To see a complete working integration, run the [iOS example app](/guides/sdk/ios-sdk/implementation-guide#test-with-the-example-app) or the [Android sample app](/guides/sdk/android-sdk/implementation-guide#test-with-the-sample-app) with the SDK example server.

## Go live

Before you release your app, complete this checklist:

* Complete [certification](/testing-your-clearpay-integration). Clearpay issues your production credentials after you pass.
* On your server, use your production merchant ID and secret key, and the production API host `https://global-api.afterpay.com`.
* Update your `User-Agent` header with your production merchant ID.
* In your app, set the environment to production: `.production` on iOS or `AfterpayEnvironment.PRODUCTION` on Android.
* Make sure that your app and your server use the same environment. The SDK environment must match the Clearpay API host that your server uses to create checkouts.