# iOS checkout

> Open Bolt Checkout from an iOS app with an order token from your backend.

Source: https://help.boltapp.com/developers/sdks/ios-swift/checkout

<span id="overview" />

## Before you begin [#before-you-begin]

Complete [steps 1 through 5 of the Custom Cart integration](/custom-cart/overview#1-requirements). Your app also needs:

* The [iOS SDK installed and initialized](/developers/sdks/ios-swift#installation) with the publishable key, division, and environment for this integration.
* An application backend endpoint that returns a fresh order token for the current cart.

Never call `POST /v1/merchant/orders` or put a Bolt API key in the app. Follow [Create an order token](/custom-cart/create-order-token) for the server implementation.

<span id="generating-an-order-token-backend" />

<span id="request" />

<span id="request-body" />

<span id="response" />

<span id="end-to-end-flow" />

## 1. Get an order token [#1-get-an-order-token]

When the shopper selects checkout, send the current cart identifier to your backend. Wait for your backend to return the `token` from Bolt before opening checkout.

Create a new order token when the cart changes or the shopper starts another checkout attempt.

<span id="option-a-completion-handler-simplest" />

## 2. Open checkout [#2-open-checkout]

The completion handler works from UIKit and SwiftUI without requiring a presenting view controller:

```swift title="Swift"
import BoltCheckoutSDK

func openBoltCheckout(orderToken: String) {
    let config = BoltCheckoutConfig(orderToken: orderToken)

    BoltCheckout.shared.startCheckout(config: config) { outcome in
        switch outcome {
        case .success(let result):
            print("Bolt transaction: \(result.reference)")
        case .canceled:
            // Return the shopper to the cart.
            break
        case .failed(let errorReason):
            // Show a retry action and record the error.
            print("Bolt Checkout failed: \(errorReason)")
        }
    }
}
```

The app callback controls client-side state only. Use verified Merchant Callbacks and transaction webhooks as the source of truth for order and payment state.

### Choose another presentation method [#choose-another-presentation-method]

These are alternatives to the completion handler.

| Method          | Use it when                                        |
| --------------- | -------------------------------------------------- |
| Delegate        | A coordinator or view model owns checkout results. |
| View controller | UIKit code must control presentation directly.     |

<span id="option-b-delegate-pattern" />

#### Delegate [#delegate]

Implement the three required `BoltSimpleCheckoutDelegate` methods, then pass the delegate when checkout starts:

```swift title="Swift"
import BoltCheckoutSDK

final class CheckoutCoordinator: BoltSimpleCheckoutDelegate {
    func open(orderToken: String) {
        let config = BoltCheckoutConfig(orderToken: orderToken)
        BoltCheckout.shared.startCheckout(config: config, delegate: self)
    }

    func onCheckoutSuccess(result: BoltCheckoutResult) {
        print("Bolt transaction: \(result.reference)")
    }

    func onCheckoutError(errorReason: String) {
        print("Bolt Checkout failed: \(errorReason)")
    }

    func onCheckoutCancel() {
        // Return the shopper to the cart.
    }
}
```

<span id="option-c-present-a-view-controller-directly" />

#### View controller [#view-controller]

Present `BoltCheckoutViewController` with the same configuration and delegate:

```swift title="Swift"
let config = BoltCheckoutConfig(orderToken: orderToken)
let checkout = BoltCheckoutViewController(config: config, delegate: delegate)
present(checkout, animated: true)
```

<span id="pre-filling-customer-data" />

### Prefill shopper details [#prefill-shopper-details]

Pass optional hints when your app already has shopper information:

```swift title="Swift"
let hints = Bolt.HintsBuilder()
    .withFirstName("Ada")
    .withLastName("Lovelace")
    .withEmail("ada@example.com")
    .withCountry("US")
    .build()

let config = BoltCheckoutConfig(orderToken: orderToken, hints: hints)
```

Do not send placeholder or stale values. The shopper can review the information in checkout.

<span id="ui-modes" />

## 3. Verify the iOS client [#3-verify-the-ios-client]

In sandbox:

1. Start checkout from a cart and confirm your backend returns a new order token.
2. Confirm Bolt Checkout opens and shows the same items and total as the cart.
3. Complete a purchase with a [sandbox test card](/getting-started/test-cards).
4. Confirm the app receives a success result and your backend receives the expected callback and webhook events.
5. Cancel another attempt and confirm the app returns the shopper to the cart without treating it as a completed order.

If checkout does not open or the cart does not match, continue to [Custom Cart troubleshooting](/custom-cart/troubleshooting).

<span id="next" />
