> This page is for Afterpay Online Developer, version Main (default).
> For other versions, use one of these documentation indexes:
> - Main (default): https://developers.afterpay.com/afterpay-online-developer/main/llms.txt
> - v1: https://developers.afterpay.com/afterpay-online-developer/v-1/llms.txt
> - Chinese: https://developers.afterpay.com/afterpay-online-developer/chinese/llms.txt

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

# iOS implementation guide

This guide walks you through integrating the iOS 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](/afterpay-online-developer/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, which is available in Swift with UIKit.

> **Checkout V1 (Deprecated)**
>
> The SDK also includes [Checkout V1](/afterpay-online-developer/guides/sdk/ios-sdk/more/checkout-v1), which is deprecated and supports standard checkout only. Use Checkout V2 unless your app needs the SwiftUI or Objective-C API that only Checkout V1 provides.

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

#### Swift Package Manager

Swift Package Manager is the recommended installation method. 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")
  )
]
```

#### CocoaPods

```ruby
pod 'Afterpay', '~> 5.9'
```

#### Carthage

```text
github "afterpay/sdk-ios" ~> 5.9
```

To integrate the SDK manually, see [Integration](https://afterpay.github.io/sdk-ios/integration/) in the SDK documentation.

## 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](/afterpay-online-developer/guides/sdk/ios-sdk/more/afterpay-button).

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

```swift
let configuration = try Configuration(
  minimumAmount: response.minimumAmount?.amount,
  maximumAmount: response.maximumAmount.amount,
  currencyCode: response.maximumAmount.currency,
  locale: Locale(identifier: "en_US"),
  environment: .sandbox,
  // Optional. Overrides the device locale.
  consumerLocale: Locale(identifier: "en_US")
)

Afterpay.setConfiguration(configuration)
```

* Set the configuration before you present a checkout or display any UI component.
* Use `.sandbox` for development and `.production` for release builds. The environment must match the environment your server uses to create checkouts.
* Cache the configuration and refresh it about once a day. Bundle a default configuration in case the first request fails.
* If you haven't set a configuration, or the customer's language (the device language, or `consumerLocale`) isn't supported in the configured region, `Afterpay.enabled` is `false`. The lockup, price breakdown, and widget then hide themselves, but `PaymentButton` stays visible with blank artwork. Check this property to hide the payment button and your own Afterpay views.

## 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](/afterpay-online-developer/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](/afterpay-online-developer/guides/api-development/additional-features/express-checkout).

## Step 4: Present the checkout

Start checkout when the customer taps your [payment button](/afterpay-online-developer/guides/sdk/ios-sdk/ui-components/payment-button).

Call `presentCheckoutV2Modally` from the view controller that shows your cart. Set the [SDK configuration](/afterpay-online-developer/guides/sdk/ios-sdk/implementation-guide#step-2-configure-the-sdk) first. Otherwise, debug builds trigger an assertion failure, and release builds return without presenting checkout or calling the completion handler.

```swift
Afterpay.presentCheckoutV2Modally(
  over: self,
  options: CheckoutV2Options(buyNow: true),
  didCommenceCheckout: { completion in
    // Call your server. Pass the token or an error to completion.
    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.
      break
    case .cancelled(let reason):
      // Handle the cancellation.
      break
    }
  }
)
```

If you pass an error to the `didCommenceCheckout` completion, the SDK shows a dialog that lets the customer retry or close checkout. If the customer's language isn't supported in your region, `Afterpay.enabled` is `false` and the SDK doesn't present checkout.

### Checkout options

Pass `CheckoutV2Options` 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 5: Provide shipping options (express checkout)

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

```swift
Afterpay.presentCheckoutV2Modally(
  over: self,
  options: CheckoutV2Options(shippingOptionRequired: true),
  didCommenceCheckout: { completion in
    checkoutService.createExpressCheckout { completion($0) }
  },
  shippingAddressDidChange: { address, completion in
    let standard = ShippingOption(
      id: "standard",
      name: "Standard",
      description: "3-5 days",
      shippingAmount: Money(amount: "5.00", currency: "USD"),
      orderAmount: Money(amount: "105.00", currency: "USD")
    )
    completion(.success([standard]))
  },
  shippingOptionDidChange: { shippingOption, completion in
    // Return an updated amount for the option, or nil to keep it.
    completion(nil)
  },
  completion: { result in
    // Handle the result.
  }
)
```

`orderAmount` is the order total including shipping and tax. The `ShippingOptionsError` cases are `shippingAddressUnrecognized`, `shippingAddressUnsupported`, `serviceUnavailable`, and `badResponse`. Checkout shows an error message to the customer for each one.

### Use a checkout handler

To handle these callbacks outside the presenting view controller, implement `CheckoutV2Handler` and register it with `Afterpay.setCheckoutV2Handler(_:)`. The protocol has the same three methods: `didCommenceCheckout(completion:)`, `shippingAddressDidChange(address:completion:)`, and `shippingOptionDidChange(shippingOption:completion:)`. The SDK holds the handler weakly, so keep a strong reference to it.

## Step 6: Complete the order

A successful result contains the order token. A canceled result contains a `CancellationReason`, such as `userInitiated`.

Send the order token to your server. To capture the full amount immediately, your server calls [Capture Full Payment](/afterpay-online-developer/api-reference/reference/payments/capture-full-payment) with the token. To capture later, call [Auth](/afterpay-online-developer/api-reference/reference/payments/auth) and then [Capture Payment](/afterpay-online-developer/api-reference/reference/payments/capture-payment).

## Security

To add certificate pinning to the checkout and widget web views, register an authentication challenge handler. Return `true` if you handled the challenge, or `false` to use the default handling. This example uses [TrustKit](https://github.com/datatheorem/TrustKit):

```swift
Afterpay.setAuthenticationChallengeHandler { challenge, completionHandler in
  let validator = TrustKit.sharedInstance().pinningValidator
  return validator.handle(challenge, completionHandler: completionHandler)
}
```

## Test with the example app

The [example app](https://github.com/afterpay/sdk-ios/tree/master/Example) uses the [example server](https://github.com/afterpay/sdk-example-server), which creates sandbox checkouts with your merchant credentials.

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 open `Afterpay.xcworkspace` in Xcode. Open the workspace rather than the example project so that Xcode resolves the SDK's dependencies.

3. Select the **Example** scheme and run it on a simulator.

4. To use a different server host or port, change the values on the app's **Settings** tab.

5. Add items to the cart and check out. Log in with a sandbox customer account. For details, see [Test environments](/afterpay-online-developer/guides/api-development/test-environments).