# Implementation

Complete guide to integrating Checkout Drop-in into your iOS application.

## Overview

Checkout Drop-in provides a complete, pre-built payment interface that automatically handles multiple payment methods: cards, PayPal, Apple Pay, and Aeropay (when enabled in your session).

It follows a simple three-step lifecycle:

1. **Initialise:** Configure Drop-in with your session and transaction data (`CheckoutDropIn(config:)` validates `sessionId` and `hmacKey`).
2. **Create:** Await `create()` to fetch site configuration, resolve the shopper via `onGetShopper`, and prepare payment components.
3. **Render:** Display the UI using `buildContent()` within a SwiftUI view.


Drop-in automatically detects available payment methods, applies server-driven branding, and signs PXP API requests using your session's HMAC key.

Backend verification is mandatory. Always verify payments on your backend before fulfilling orders. Frontend callbacks can be manipulated by malicious users.

## Before you start

Make sure you've activated the Checkout Drop-in service in the Unity Portal. Contact your account manager if you need access.

## Step 1: Add the SDK dependency

Add the iOS SDK to your project using Swift Package Manager.

1. In Xcode, go to **File > Add Package Dependencies**.
2. Enter the package URL: `https://github.com/PXP-IO/ios-components-sdk`.
3. Choose **Up to Next Major Version** from the latest release on [ios-components-sdk](https://github.com/PXP-IO/ios-components-sdk/releases), then click **Add Package**.


Alternatively, add it to your `Package.swift` file. Prefer the Xcode flow above, or check [ios-components-sdk releases](https://github.com/PXP-IO/ios-components-sdk/releases) for the current tag and use that as the floor:

```swift
dependencies: [
    .package(
        url: "https://github.com/PXP-IO/ios-components-sdk.git",
        .upToNextMajor(from: "{latestReleaseVersion}") // use current latest release as the floor
    )
]
```

## Step 2: Get your API credentials

In order to initialise Checkout Drop-in, you'll need to send authenticated requests to the PXP API.

To get your credentials:

1. In the Unity Portal, go to **Merchant setup > Merchant groups**.
2. Select a merchant group.
3. Click the **Inbound calls** tab.
4. Copy the *Client ID* in the top-right corner.
5. Click **New token**.
6. Choose a number of days before token expiry. For example, `30`.
7. Click **Save** to confirm. Your token is now created.
8. Copy the token ID and token value. Make sure to keep these confidential to protect the integrity of your authentication process.


As best practice, we recommend regularly generating and implementing new tokens.

## Step 3: Create a session on your backend

Checkout Drop-in requires a session from the PXP Sessions API. This must be done on your backend using HMAC authentication to keep your credentials secure.

### Understanding HMAC authentication

Our platform uses HMAC (Hash-based Message Authentication Code) with SHA256 for authentication to ensure secure communication and data integrity. This method involves creating a signature by hashing your request data with a secret key, which must then be included in the HTTP headers of your API request.

### Create an HMAC signature

Build the HMAC message by concatenating these four values with no separators:

- **Timestamp:** the current time in Unix seconds (e.g., `1754701373`)
- **Request ID:** a unique UUID for this request (e.g., `ce244054-b372-42c2-9102-f0d976db69f6`)
- **Request path:** the API endpoint path: `api/v1/sessions`
- **Request body:** the complete JSON request body as a minified string


Hash that message with your token value using HMAC SHA256 (uppercase hex). Put the Token ID in the `Authorization` header only, not in the HMAC message body:

```text
Authorization: PXP-UST1 {tokenId}:{timestamp}:{hmac}
```

Also send `X-Client-Id` and `X-Request-Id` headers with the request.

Example request body to minify:

```json
{
  "merchant": "MERCHANT-1",
  "site": "SITE-1",
  "sessionTimeout": 120,
  "merchantTransactionId": "0ce72cfd-014d-4256-a006-a56601b2ffc4",
  "transactionMethod": {
    "intent": {
      "card": "Authorisation",
      "paypal": "Purchase",
      "aeropay": "Authorisation"
    }
  },
  "amounts": {
    "currencyCode": "USD",
    "transactionValue": 25.00
  },
  "allowTransaction": true,
  "serviceType": "CheckoutDropIn",
  "customerProfileId": "your-customer-profile-id"
}
```

Omit `customerProfileId` for guest or anonymous checkout. Include it when the shopper is known and you want the session linked to their Unity Customer Profile. Set the value on your backend after the shopper signs in. Don't accept a client-supplied profile ID without verifying ownership.

When creating the HMAC signature, the request body must be minified (no whitespace or formatting). The formatted JSON above is for readability only.

### Session request parameters

The session request accepts the following parameters:

| Parameter | Description |
|  --- | --- |
| `merchant`string (≤ 20 characters) | Your unique merchant identifier, as assigned by PXP. You can find it in the Unity Portal, by going to **Merchant setup > Merchants** and checking the *Merchant ID* column. |
| `site`string (≤ 20 characters) | Your unique site identifier, as assigned by PXP. You can find it in the Unity Portal, by going to **Merchant setup > Sites** and checking the *Site ID* column. |
| `merchantTransactionId`string (≤ 50 characters) | A unique identifier of your choice that represents this transaction. |
| `sessionTimeout`number | The duration of the session, in minutes. |
| `transactionMethod`object | Details about the transaction method, including the intent for each payment type. |
| `transactionMethod.intent`object | The transaction intent for each payment method. |
| `transactionMethod.intent.card`string | The intent for card and Apple Pay transactions.Possible values:`Authorisation``Purchase``Verification``EstimatedAuthorisation` |
| `transactionMethod.intent.paypal`string | The intent for PayPal transactions.Possible values:`Authorisation``Purchase` |
| `transactionMethod.intent.aeropay`string | The intent for Aeropay transactions.Possible values:`Authorisation``Purchase``EstimatedAuthorisation` Payout isn't available in Drop-in. See [Aeropay](/guides/checkout/drop-in/ios/aeropay). |
| `amounts`object | Details about the transaction amount. |
| `amounts.currencyCode`string (3 characters) | The currency code associated with the transaction, in ISO 4217 format. See [Supported payment currencies](/guides/checkout/drop-in/how-it-works#supported-payment-currencies). |
| `amounts.transactionValue`number | The transaction amount. The numbers after the decimal will be zero padded if they are less than the expected `currencyCode` exponent. For example, GBP 1.1 = GBP 1.10, USD 1 = USD 1.00, or BHD 1.3 = 1.300. The transaction will be rejected if numbers after the decimal are greater than the expected `currencyCode` exponent (e.g., GBP 1.234), or if a decimal is supplied when the `currencyCode` of the exponent does not require it (e.g., JPY 1.0). |
| `allowTransaction`boolean | Whether or not to proceed with the transaction. |
| `serviceType`string | The service type. This must be set to `"CheckoutDropIn"` for Drop-in integrations. |
| `customerProfileId`string (≤ 255 characters) | Optional Unity Customer Profile identifier. Links the checkout session to a returning shopper profile. Omit for guest checkout. This field is session-request only. It isn't returned in the Sessions API response and isn't a property on `SessionData`, `CheckoutDropInConfig`, or any SDK component config. |


### Session response

If your request is successful, you'll receive a `200` response containing the session data. The response doesn't echo `customerProfileId`.

```json
{
  "sessionId": "c5f0799b-0839-43ce-abc5-5b462a98f250",
  "hmacKey": "904bc42395d4af634e2fd48ee8c2c7f52955a1da97a3aa3d82957ff12980a7bb",
  "encryptionKey": "20d175a669ad3f8c195c9c283fc86155",
  "sessionExpiry": "2025-05-19T13:39:20.3843454Z",
  "allowedFundingTypes": {
    "cardSchemes": [
      "Visa",
      "Diners",
      "Mastercard",
      "AmericanExpress"
    ],
    "cards": [],
    "wallets": {
      "paypal": {
        "allowedFundingOptions": [
          "paylater", 
          "paypal"
        ],
        "merchantId": "paypal-merchant-123"
      },
      "applePay": {
        "merchantId": "merchant.com.example.store",
        "merchantName": "Your Store Name"
      }
    },
    "payByBanks": {
      "aeropay": {
        "externalMerchantId": "your-aeropay-merchant-id",
        "configurationId": "your-aeropay-configuration-id"
      }
    }
  }
}
```

Checkout Drop-in automatically detects available payment methods from the `allowedFundingTypes` in your session data. You don't need to manually configure which payment methods to show.

For Aeropay pay-by-bank setup (`payByBanks.aeropay`, USD and `.ecom`, intents, and shopper data), see [Aeropay](/guides/checkout/drop-in/ios/aeropay).

## Step 4: Initialise Drop-in in your app

Import the necessary types and initialise Drop-in with your configuration.

```swift
import SwiftUI
import PXPCheckoutSDK

@MainActor
final class CheckoutViewModel: ObservableObject {
    @Published var dropIn: CheckoutDropIn?
    @Published var errorMessage: String?
    
    func loadDropIn() async {
        errorMessage = nil
        dropIn = nil
        do {
            // Fetch session data from your backend
            let session = try await fetchSessionFromBackend()
            
            // Build transaction data
            // Include card, paypal, and/or aeropay intents matching session.allowedFundingTypes
            let transactionData = DropInTransactionData(
                amount: Decimal(string: "49.99") ?? 0,
                currency: "USD",
                entryType: .ecom,
                intent: DropInTransactionIntentData(
                    card: .authorisation,
                    paypal: .purchase,
                    aeropay: .authorisation
                ),
                merchantTransactionId: UUID().uuidString,
                merchantTransactionDate: { Date() }
            )
            
            // Create Drop-in configuration
            let config = CheckoutDropInConfig(
                environment: .test,
                session: session,
                transactionData: transactionData,
                merchantShopperId: "shopper-123",
                ownerId: "merchant-group-id", // Merchant group ID (ownerType is always "MerchantGroup")
                kountDisabled: false,
                onGetShopper: {
                    TransactionShopper(id: "shopper-123")
                },
                onBeforeSubmit: { paymentMethod async in
                    // Validate your checkout state
                    return true
                },
                onSubmit: { paymentMethod in
                    print("Payment started: \(paymentMethod.rawValue)")
                },
                onSuccess: { result in
                    // CRITICAL: Verify on backend before fulfilling order
                    print("Payment successful: \(result.systemTransactionId)")
                },
                onError: { paymentMethod, error in
                    // Handles create() failures (paymentMethod may be nil) and payment errors
                    print("Payment failed: \(error.errorMessage)")
                    errorMessage = error.errorMessage
                }
            )
            
            // Initialise and create Drop-in
            // try catches config throws (empty sessionId/hmacKey). create() failures use onError.
            let instance = try CheckoutDropIn(config: config)
            await instance.create()
            if errorMessage == nil {
                dropIn = instance
            }
            
        } catch let error as BaseSdkException {
            errorMessage = error.errorMessage
        } catch {
            errorMessage = error.localizedDescription
        }
    }
    
    private func fetchSessionFromBackend() async throws -> SessionData {
        // Implement your backend API call here
        let url = URL(string: "https://your-backend.com/api/create-session")!
        let (data, _) = try await URLSession.shared.data(from: url)
        
        // Decode directly to SessionData to preserve all fields including sessionExpiry and restrictions
        return try JSONDecoder().decode(SessionData.self, from: data)
    }
}
```

### Configuration parameters

The `CheckoutDropInConfig` class accepts the following configuration parameters:

| Parameter | Description |
|  --- | --- |
| `environment`Environment | The environment type.Possible values:`.test`: For development and testing.`.live`: For live transactions. |
| `session`SessionData | Details about the checkout session returned from the Unity Sessions API. Includes `sessionId`, `hmacKey`, `encryptionKey`, and `allowedFundingTypes`. |
| `transactionData`DropInTransactionData | Details about the transaction. |
| `merchantShopperId`String | Your unique identifier for this shopper. Required at initialisation (separate from the `id` returned by `onGetShopper`, and separate from optional session `customerProfileId`). Used for card-on-file and other shopper-scoped flows. See [Customer Profile](#customer-profile). |
| `ownerId`String (≤ 20 characters) | Your merchant group identifier, as assigned by PXP. Drop-in sets `ownerType` to `"MerchantGroup"` automatically. You can find the merchant group ID in the Unity Portal. |
| `localisation`Localisation? | Optional custom labels and messages for the drop-in interface. Use this to override default text. |
| `locale`String? | Locale for built-in SDK strings (e.g., `"en-US"`, `"es-ES"`). When omitted, Drop-in defaults to `"en-US"`. It does not automatically follow the device language. See [Configuration](/guides/checkout/drop-in/ios/configuration#locale). |
| `paypalConfig`PayPalConfig? | Optional PayPal configuration used by SDK-level PayPal flows. |
| `restrictions`Restrictions? | Optional card restrictions supplied at SDK level. Can filter by card owner type (consumer/corporate) and funding source (credit/debit/prepaid). |
| `kountDisabled`Bool | Set to `true` to disable Kount fraud detection. Defaults to `false` when omitted from the initializer. |
| `methodConfig`DropInMethodConfig? | Optional payment-method-specific configuration for global, card, PayPal, Apple Pay, and Aeropay behaviour. Use `methodConfig.card` (`DropInCardConfig`) to show or hide card-on-file (`showCOF`) and new card entry (`showNewCard`). Both default to `true` when omitted. See [Configuration](/guides/checkout/drop-in/ios/configuration#payment-method-configuration), [Cards](/guides/checkout/drop-in/ios/cards#card-display-properties), and [Aeropay](/guides/checkout/drop-in/ios/aeropay). |
| `onGetShippingAddress`(() async -> ShippingAddress?)? | Async function to supply shipping address when Drop-in needs it (e.g., billing prefill or PayPal `.setProvidedAddress`). |
| `onGetShopper`(() async -> TransactionShopper?)? | Async function to retrieve shopper information. Card-on-file UI is created only when this returns `TransactionShopper(id:)` with a non-empty `id` and `showCOF` resolves to `true`. Separate from optional session `customerProfileId`. Align `merchantShopperId` and the returned shopper `id` with your vault. See [Customer Profile](#customer-profile), [Card display properties](/guides/checkout/drop-in/ios/cards#card-display-properties), and [Aeropay](/guides/checkout/drop-in/ios/aeropay). |
| `analyticsEvent`((BaseAnalyticsEvent) -> Void)? | Receives SDK analytics events so merchants can forward them to their analytics platform. See [Analytics](/guides/checkout/drop-in/ios/analytics). |
| `onBeforeSubmit`((DropInPaymentMethod) async -> Bool)? | Async validation function called before payment submission. Return `true` to proceed or `false` to block submission. For Aeropay, this runs when the shopper taps **Pay by bank** and returning `false` prevents the popup from opening. It does **not** run again at bank-account submission. |
| `onSubmit`((DropInPaymentMethod) -> Void)? | Called when payment processing starts for card, Apple Pay, and Aeropay. PayPal doesn't invoke `onSubmit`. For Aeropay, `onSubmit(.aeropay)` fires when the shopper confirms pay/withdraw on the bank-selection screen (after the popup flow), not when the main button is first tapped. Drop-in always proceeds after `onSubmit` (there's no merchant `onPreAuthorisation` gate). Show loading after `onBeforeSubmit` returns `true` or when Drop-in enters processing during PayPal order creation. See [Events — onSubmit](/guides/checkout/drop-in/ios/events#onsubmit). |
| `onSuccess`((DropInSubmitResult) -> Void)? | Called when payment succeeds. Strongly recommended in production. Always verify the payment on your backend using the provided transaction identifiers before fulfilling the order. |
| `onError`((DropInPaymentMethod?, BaseSdkException) -> Void)? | Called when initialisation or payment fails. Strongly recommended in production. Required for `create()` failures (site config, component render, invalid Aeropay shopper data at create) — `await create()` does not throw those. The `paymentMethod` parameter can be `nil` for initialisation or configuration errors that occur before a payment method is selected. Handle errors appropriately and show user-friendly messages. See [Error handling](/guides/checkout/drop-in/ios/error-handling). |


### Transaction data parameters

The `DropInTransactionData` struct describes the payment transaction:

| Parameter | Description |
|  --- | --- |
| `amount`Decimal | The transaction amount. |
| `currency`String | The three-letter currency code in ISO 4217 format (e.g., `"USD"`, `"GBP"`, `"EUR"`). Aeropay requires `"USD"`. |
| `entryType`EntryType | The transaction entry type.Possible values:`.ecom`: E-commerce transaction. Required for Drop-in wallet flows such as PayPal and for Aeropay.`.moto`: Mail order / telephone order card flows. |
| `intent`DropInTransactionIntentData | The transaction intent for each payment method. Use `DropInTransactionIntentData`, not the generic `TransactionIntentData`. Each intent property is optional — include `card`, `paypal`, and/or `aeropay` intents matching the methods present in `session.allowedFundingTypes`. Set `aeropay` to `.authorisation`, `.purchase`, or `.estimatedAuthorisation` when Aeropay is enabled. See [Aeropay](/guides/checkout/drop-in/ios/aeropay). |
| `merchantTransactionId`String | A unique identifier for this transaction. Use `UUID().uuidString` to generate. |
| `merchantTransactionDate`() -> Date | A closure that returns the current date. Typically `{ Date() }`. |
| `cardAcceptorName`String? | Optional card acceptor name when required by your flow. |
| `recurring`RecurringType? | Optional recurring payment data for subscription or instalment use cases. |
| `linkId`String? | Optional link ID when linking transactions together. |


## Step 5: Render the drop-in UI

Display the drop-in interface within a SwiftUI view:

```swift
import SwiftUI
import PXPCheckoutSDK

struct CheckoutView: View {
    @StateObject private var viewModel = CheckoutViewModel()
    
    var body: some View {
        Group {
            if let dropIn = viewModel.dropIn {
                dropIn.buildContent()
            } else if viewModel.errorMessage == nil {
                ProgressView("Loading checkout...")
            }

            if let error = viewModel.errorMessage {
                VStack(spacing: 16) {
                    Text("Unable to load checkout")
                        .font(.headline)
                    Text(error)
                        .font(.body)
                        .foregroundColor(.secondary)
                    Button("Retry") {
                        Task {
                            await viewModel.loadDropIn()
                        }
                    }
                }
                .padding()
            }
        }
        .task {
            await viewModel.loadDropIn()
        }
    }
}
```

The `buildContent()` method returns `AnyView` containing the Drop-in UI. When you assign `dropIn` only after `create()` completes, show an app-level loading indicator until then. Drop-in's built-in `CheckoutDropInInitializingView` appears when `buildContent()` is already on screen during an in-flight `create()`. After `create()` succeeds, `buildContent()` shows the payment interface. If creation fails without a hosted component, `buildContent()` may be empty. Wire `onError` so that isn't a silent blank screen. The SDK handles payment method selection and branding inside the hosted component.

## Customer Profile

Customer Profile links a Drop-in session to a returning shopper on the Unity platform. Pass optional `customerProfileId` when your backend creates the session (`POST api/v1/sessions`).

| Topic | Guidance |
|  --- | --- |
| Where to set it | Sessions API request body only. Not on `SessionData`, `CheckoutDropInConfig`, or component config. |
| When to include it | When the shopper is known and you want the session scoped to their Customer Profile. Omit for guest checkout. |
| Relationship to shopper ID | `customerProfileId` associates the Unity session with a platform profile. `merchantShopperId` and `onGetShopper` returning `TransactionShopper.id` identify the shopper for token vault, card-on-file, and vaulting. Align these for returning shoppers. |
| Card-on-file still needs shopper ID | Even with `customerProfileId`, card-on-file requires `merchantShopperId`, `onGetShopper` returning a non-empty `id`, and `showCOF` resolving to `true`. See [Cards](/guides/checkout/drop-in/ios/cards#card-display-properties). |
| Security | Create the session on your backend with Unity credentials. Bind `customerProfileId` to the authenticated user. Don't trust a client-supplied profile ID without verifying ownership. |


Don't confuse Customer Profile with the Paze `profileId` on session wallet funding. Those are unrelated.

## Step 6: Handle payment callbacks

Drop-in provides four key callbacks to manage the payment flow.

### onBeforeSubmit

Called before payment processing starts. Use this to validate your checkout state.

```swift
onBeforeSubmit: { paymentMethod async in
    // Validate terms accepted, shipping address, etc.
    guard hasAcceptedTerms else {
        showError("Please accept terms and conditions")
        return false
    }
    return true
}
```

### onSubmit

Called when Drop-in enters payment processing for **card**, **Apple Pay**, and **Aeropay**. PayPal doesn't invoke `onSubmit`. For Aeropay, `onSubmit(.aeropay)` fires when the shopper confirms pay/withdraw on the bank-selection screen (after the popup flow), not when the main button is first tapped. Show loading after `onBeforeSubmit` returns `true` or when Drop-in enters processing during PayPal order creation. See [Events — onSubmit](/guides/checkout/drop-in/ios/events#onsubmit).

```swift
onSubmit: { paymentMethod in
    showLoadingIndicator()
    print("Processing \(paymentMethod.rawValue) payment...")
}
```

### onSuccess

Called when payment succeeds. Always verify on your backend:

In Drop-in, `result.paymentData` is typically `nil` for card, PayPal, Apple Pay, and Aeropay. Always verify transaction state on your backend using `systemTransactionId`.

```swift
onSuccess: { result in
    // result is DropInSubmitResult
    let systemTransactionId = result.systemTransactionId
    let merchantTransactionId = result.merchantTransactionId // String?
    // Verify these identifiers on your backend.
    
    hideLoadingIndicator()
    
    // Send to backend for verification
    Task {
        do {
            try await verifyPaymentOnBackend(
                systemTransactionId: systemTransactionId,
                merchantTransactionId: merchantTransactionId
            )
            showSuccessScreen()
        } catch {
            showError("Payment verification failed")
        }
    }
}
```

### onError

Called when initialisation or payment fails. Read `error.errorCode` and `error.errorMessage` from `BaseSdkException`. See [Error handling](/guides/checkout/drop-in/ios/error-handling) for the full Drop-in code list (including Aeropay setup and payment codes such as `SDK1125` and `SDK1126`), rather than assuming codes end at `SDK1117`.

```swift
onError: { paymentMethod, error in
    let method = paymentMethod?.rawValue ?? "unknown"
    let code = error.errorCode
    let message = error.errorMessage
    
    hideLoadingIndicator()
    
    // Prefer stable Drop-in codes. Declines are not reliably SDK1116 + message substrings —
    // underlying provider codes may be preserved. See [Error handling](error-handling).
    switch code {
    case "SDK1114":
        showError("Authentication failed. Please try again or use a different card.")
    case "SDK0500":
        showError("Connection error. Check your internet connection and try again.")
    case "SDK1116":
        // Generic card-payment failure fallback when no more specific code was preserved
        showError("Card payment failed. Please try another payment method.")
    case "SDK0113", "SDK0115", "SDK1125":
        showError("Pay by Bank is unavailable. Please choose another payment method.")
    case "SDK1126":
        showError("Pay by Bank payment failed. Please try again.")
    default:
        showError("Payment failed. Please try again.")
    }
    
    // Log for debugging
    print("Error for \(method): \(code) — \(message)")
}
```

## Callback timeline

The sequence below lists the public `CheckoutDropInConfig` callbacks merchants implement. Steps between those callbacks (authentication, authorisation, order creation) are handled inside the SDK.

### Card

Typical card callback order:

1. `onBeforeSubmit(.card)`
2. `onSubmit(.card)`
3. SDK processes the payment (including any authentication steps required by your site configuration)
4. `onSuccess(...)` or `onError(.card, ...)`


### Apple Pay

Typical Apple Pay callback order:

1. `onBeforeSubmit(.applePay)` (when the customer taps the Apple Pay button, before the payment sheet opens)
2. Customer authorises in the Apple Pay sheet
3. `onSubmit(.applePay)` (when Drop-in enters processing after sheet authorisation)
4. `onSuccess(...)` or `onError(.applePay, ...)`


### PayPal

Typical PayPal callback order:

1. `onBeforeSubmit(.paypal)` (when the customer taps the PayPal button)
2. Drop-in enters processing and creates the PayPal order (`onSubmit` is not called)
3. Buyer approval on PayPal.com
4. `onSuccess(...)` or `onError(.paypal, ...)`


PayPal does not invoke `onSubmit`. `onSuccess` fires after the customer approves on PayPal.com, not when the button is first tapped. See [Events](/guides/checkout/drop-in/ios/events#onsubmit).

### Aeropay

Typical Aeropay callback order:

1. `onBeforeSubmit(.aeropay)` (when the shopper taps **Pay by bank**, before the popup opens). Returning `false` prevents the popup from opening. This does **not** run again at bank-account submission.
2. Shopper completes consumer data, OTP, and bank selection in the Aeropay popup (as required)
3. `onSubmit(.aeropay)` (when the shopper confirms pay/withdraw on the bank-selection screen). Use this for loading state. Drop-in always proceeds after this; there's no merchant `onPreAuthorisation` gate.
4. `onSuccess(...)` or `onError(.aeropay, ...)`


If the shopper closes the popup, `methodConfig.global.onCancel(.aeropay, nil)` fires instead of `onError`. See [Aeropay](/guides/checkout/drop-in/ios/aeropay) and [Events](/guides/checkout/drop-in/ios/events).

## Cleanup

Call `destroy()` to explicitly release the hosted drop-in UI when you're done:

```swift
dropIn.destroy()
```

The SDK automatically cleans up when the drop-in instance is deallocated, so calling `destroy()` is optional in most cases.

## Security recommendations

Follow these practices for a secure integration:

- Create checkout sessions on your backend only.
- Never log HMAC keys, raw payment payloads, or full wallet/token payloads.
- Verify the payment result server-side before shipment or fulfilment.
- Use HTTPS for all backend traffic.
- Treat transaction identifiers as references, not as final proof of settlement.


## What's next?

Continue with these guides:

- **[Configuration reference](/guides/checkout/drop-in/ios/configuration):** Explore all configuration options in detail.
- **[Payment methods](/guides/checkout/drop-in/ios/cards):** Configure cards, PayPal, Apple Pay, and [Aeropay](/guides/checkout/drop-in/ios/aeropay).
- **[Events and callbacks](/guides/checkout/drop-in/ios/events):** Learn about all available callbacks.
- **[Error handling](/guides/checkout/drop-in/ios/error-handling):** Implement robust error handling.
- **[Testing](/guides/checkout/drop-in/ios/testing):** Test your integration with test cards and credentials.