> ## Documentation Index
> Fetch the complete documentation index at: https://primer.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# PrimerVaultedPaymentMethods

> Saved (vaulted) payment methods composable with slot-based rows

<Warning>
  Primer Checkout iOS SDK is currently in **beta** (v3.0.0-beta.1).
  The API is subject to change before the stable release.
</Warning>

`PrimerVaultedPaymentMethods` renders the customer's saved (vaulted) payment methods as a list, composed from three slots: a header, a per-method row, and a submit button. Tapping a row selects that method; the submit button pays with the currently selected vaulted method.

The view resolves its [`PrimerSelectionSession`](/docs/sdk/ios-checkout/v3.0.0-beta/api/payment-methods/payment-methods-session) from the environment. Selection, deletion, and CVV recapture are all driven through that session — `PrimerVaultedPaymentMethods` only composes the UI.

<Note>
  This view reads its session from the environment injected by the `.primerCheckoutSession(_:onCompletion:)` modifier. Place it inside a view hierarchy that applies that modifier. When no session is present it renders an empty placeholder.
</Note>

## PrimerVaultedPaymentMethods

```swift theme={"dark"}
@available(iOS 15.0, *)
public struct PrimerVaultedPaymentMethods: View {

  public typealias VaultedMethod = PrimerHeadlessUniversalCheckout.VaultedPaymentMethod

  public init(
    header: @escaping (PrimerSelectionSession) -> AnyView
      = { AnyView(VaultedPaymentMethodsDefaults.header($0)) },
    item: @escaping (VaultedMethod, _ isSelected: Bool, _ onSelect: @escaping () -> Void) -> AnyView
      = { AnyView(VaultedPaymentMethodsDefaults.item($0, isSelected: $1, onSelect: $2)) },
    submitButton: @escaping (_ isLoading: Bool, _ isEnabled: Bool, _ onSubmit: @escaping () -> Void) -> AnyView
      = { AnyView(VaultedPaymentMethodsDefaults.submitButton(isLoading: $0, isEnabled: $1, onSubmit: $2)) }
  )
}
```

| Parameter      | Type                                                                                  | Default                                           | Description                                                                                                             |
| -------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `header`       | `(PrimerSelectionSession) -> AnyView`                                                 | `VaultedPaymentMethodsDefaults.header($0)`        | Header shown above the list. Receives the session so custom headers can read state or trigger session actions.          |
| `item`         | `(VaultedMethod, _ isSelected: Bool, _ onSelect: @escaping () -> Void) -> AnyView`    | `VaultedPaymentMethodsDefaults.item(...)`         | Row for each vaulted method. Receives the method, whether it is currently selected, and a callback to select it.        |
| `submitButton` | `(_ isLoading: Bool, _ isEnabled: Bool, _ onSubmit: @escaping () -> Void) -> AnyView` | `VaultedPaymentMethodsDefaults.submitButton(...)` | Submit button below the list. Receives loading state, enabled state, and a callback that pays with the selected method. |

<Info>
  Slots are type-erased to `AnyView` rather than using opaque return types. The three-argument `item` and `submitButton` builders hit Swift's generic-default inference limits, so this view trades the opaque-return ergonomics of [`PrimerCardForm`](/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/primer-card-form) for guaranteed composition. Wrap any custom slot content in `AnyView`.
</Info>

### VaultedMethod typealias

```swift theme={"dark"}
public typealias VaultedMethod = PrimerHeadlessUniversalCheckout.VaultedPaymentMethod
```

`VaultedMethod` is a convenience alias for `PrimerHeadlessUniversalCheckout.VaultedPaymentMethod`, the model passed to the `item` slot.

```swift theme={"dark"}
public final class VaultedPaymentMethod: Codable {
  public let id: String
  public let paymentMethodType: String
  public let paymentInstrumentType: PaymentInstrumentType
  public let paymentInstrumentData: Response.Body.Tokenization.PaymentInstrumentData
  public let analyticsId: String
}
```

| Property                | Type                                               | Description                                                                                   |
| ----------------------- | -------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `id`                    | `String`                                           | Unique identifier for this vaulted method. Use it to drive selection and deletion.            |
| `paymentMethodType`     | `String`                                           | Payment method type (e.g. `"PAYMENT_CARD"`, `"PAYPAL"`).                                      |
| `paymentInstrumentType` | `PaymentInstrumentType`                            | Type of payment instrument (e.g. card, bank account).                                         |
| `paymentInstrumentData` | `Response.Body.Tokenization.PaymentInstrumentData` | Detailed instrument data: card network, masked digits, expiry, external payer info, BIN data. |
| `analyticsId`           | `String`                                           | Identifier used for analytics tracking.                                                       |

## Usage

The default slots produce a complete, themed vaulted list — no slot arguments are required:

```swift theme={"dark"}
import SwiftUI
import PrimerSDK

@available(iOS 15.0, *)
struct SavedMethodsView: View {
  var body: some View {
    PrimerVaultedPaymentMethods()
  }
}
```

Wrap the view in a hierarchy that injects a checkout session:

```swift theme={"dark"}
import SwiftUI
import PrimerSDK

@available(iOS 15.0, *)
struct CheckoutScreen: View {
  let clientToken: String

  var body: some View {
    ScrollView {
      PrimerVaultedPaymentMethods()
        .padding()
    }
    .primerCheckoutSession(clientToken) { result in
      // Handle the payment result
    }
  }
}
```

## Customizing slots

Provide any combination of slots; omitted slots fall back to their defaults. Custom content must be wrapped in `AnyView`.

```swift theme={"dark"}
import SwiftUI
import PrimerSDK

@available(iOS 15.0, *)
struct CustomVaultedMethods: View {
  var body: some View {
    PrimerVaultedPaymentMethods(
      header: { _ in
        AnyView(
          Text("Pay faster with a saved card")
            .font(.headline)
            .frame(maxWidth: .infinity, alignment: .leading)
        )
      },
      item: { method, isSelected, onSelect in
        AnyView(
          Button(action: onSelect) {
            HStack {
              Text(method.paymentMethodType)
              Spacer()
              if isSelected {
                Image(systemName: "checkmark.circle.fill")
              }
            }
            .padding()
          }
          .buttonStyle(.plain)
        )
      }
    )
  }
}
```

<Tip>
  Use the `isSelected` flag the `item` slot receives to render selection affordances (checkmarks, borders, highlights). Calling `onSelect` marks the row's method as the active vaulted method so the next submit targets it.
</Tip>

## Driving selection, deletion, and CVV

`PrimerVaultedPaymentMethods` delegates all behavior to [`PrimerSelectionSession`](/docs/sdk/ios-checkout/v3.0.0-beta/api/payment-methods/payment-methods-session). Read `vaultedPaymentMethods` and call the session methods directly from custom slots.

| Member                  | Signature                                                                                   | Description                                                                                                                 |
| ----------------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `vaultedPaymentMethods` | `var vaultedPaymentMethods: [PrimerHeadlessUniversalCheckout.VaultedPaymentMethod] { get }` | Saved methods for the current customer, loaded once during checkout initialization. Empty while loading or when none exist. |
| `selectVaulted(_:)`     | `func selectVaulted(_ method: PrimerHeadlessUniversalCheckout.VaultedPaymentMethod)`        | Marks a vaulted method as selected so a subsequent submit targets it. Backing the `item` slot's `onSelect`.                 |
| `delete(_:)`            | `func delete(_ method: PrimerHeadlessUniversalCheckout.VaultedPaymentMethod)`               | Routes to the delete-confirmation screen for a vaulted method.                                                              |
| `showAll()`             | `func showAll()`                                                                            | Navigates to the full list of saved payment methods.                                                                        |
| `updateCvvInput(_:)`    | `func updateCvvInput(_ cvv: String)`                                                        | Updates and validates the CVV during CVV recapture, driving `state.cvvInput`, `state.isCvvValid`, and `state.cvvError`.     |

The selection state read by the slots lives on `session.state` (`PrimerPaymentMethodSelectionState`):

| Property                       | Type                                                    | Description                                                                                      |
| ------------------------------ | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `selectedVaultedPaymentMethod` | `PrimerHeadlessUniversalCheckout.VaultedPaymentMethod?` | The currently selected vaulted method, or `nil`. Compare its `id` to highlight the selected row. |
| `isVaultPaymentLoading`        | `Bool`                                                  | `true` while a vaulted payment is in flight. Backs the submit slot's `isLoading`.                |
| `requiresCvvInput`             | `Bool`                                                  | `true` when the selected card requires CVV recapture before submitting.                          |
| `cvvInput`                     | `String`                                                | The CVV value entered during recapture.                                                          |
| `isCvvValid`                   | `Bool`                                                  | `true` when the entered CVV passes validation.                                                   |
| `cvvError`                     | `String?`                                               | CVV validation error message, if any.                                                            |

<Note>
  CVV recapture is handled by the SDK and is **not** a customizable slot. When the selected vaulted card requires a CVV, the view inserts a managed CVV field between the rows and the submit button, wired to `updateCvvInput(_:)`. The submit button stays disabled until a method is selected and — when CVV recapture is required — until a valid CVV is entered.
</Note>

## VaultedPaymentMethodsDefaults

The default slot content. Because `PrimerVaultedPaymentMethods` erases its slots to `AnyView`, these helpers return `some View`. Compose them inside custom slots to keep the default look while overriding only part of the layout.

```swift theme={"dark"}
@available(iOS 15.0, *)
public enum VaultedPaymentMethodsDefaults {

  public static func header(_ session: PrimerSelectionSession) -> some View

  public static func item(
    _ method: PrimerHeadlessUniversalCheckout.VaultedPaymentMethod,
    isSelected: Bool,
    onSelect: @escaping () -> Void
  ) -> some View

  public static func submitButton(
    isLoading: Bool,
    isEnabled: Bool,
    onSubmit: @escaping () -> Void
  ) -> some View

  @MainActor
  @ViewBuilder
  public static func cvvInput(_ session: PrimerSelectionSession) -> some View

  public static func unavailable() -> some View
}
```

| Method                                        | Description                                                                                                                                                               |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `header(_:)`                                  | Default section header ("All saved payment methods"). The `session` argument is accepted only to match the slot signature; the header is static.                          |
| `item(_:isSelected:onSelect:)`                | Default vaulted method row with brand icon, label, and a selection checkmark.                                                                                             |
| `submitButton(isLoading:isEnabled:onSubmit:)` | Default pay button. Shows a progress spinner while `isLoading` and dims while disabled.                                                                                   |
| `cvvInput(_:)`                                | SDK-managed CVV recapture field, shown only when `session.state.requiresCvvInput` is `true`. Rendered automatically by the view — not a slot you pass in.                 |
| `unavailable()`                               | Empty placeholder rendered when no [`PrimerSelectionSession`](/docs/sdk/ios-checkout/v3.0.0-beta/api/payment-methods/payment-methods-session) is available in the environment. |

Reuse a default while customizing a sibling slot:

```swift theme={"dark"}
PrimerVaultedPaymentMethods(
  submitButton: { isLoading, isEnabled, onSubmit in
    AnyView(VaultedPaymentMethodsDefaults.submitButton(
      isLoading: isLoading,
      isEnabled: isEnabled,
      onSubmit: onSubmit
    ))
  }
)
```

## See also

<CardGroup cols={2}>
  <Card title="PrimerSelectionSession" icon="list-check" href="/docs/sdk/ios-checkout/v3.0.0-beta/api/payment-methods/payment-methods-session">
    Selection, deletion, and CVV actions for payment methods and vaulted methods.
  </Card>

  <Card title="PrimerPaymentMethods" icon="credit-card" href="/docs/sdk/ios-checkout/v3.0.0-beta/api/payment-methods/primer-payment-methods">
    Slot-based list of available (non-vaulted) payment methods.
  </Card>

  <Card title="PaymentMethodsDefaults" icon="palette" href="/docs/sdk/ios-checkout/v3.0.0-beta/api/payment-methods/payment-methods-defaults">
    Default slot content for the payment methods list.
  </Card>

  <Card title="PrimerCardForm" icon="table-cells" href="/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/primer-card-form">
    Slot-based card entry form for new cards.
  </Card>
</CardGroup>
