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

# PrimerCardFormController

> Controller interface for managing card form state, field updates, validation, and submission

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

Controller interface for the card payment form. Exposes observable state, field update methods, and submission actions.

## Creation

```kotlin theme={"dark"}
@Composable
fun rememberCardFormController(
    checkout: PrimerCheckoutController,
): PrimerCardFormController
```

| Parameter  | Type                       | Description                                                   |
| ---------- | -------------------------- | ------------------------------------------------------------- |
| `checkout` | `PrimerCheckoutController` | Checkout controller from `rememberPrimerCheckoutController()` |

<Note>
  Must be called inside `PrimerCheckoutHost` content or a `PrimerCheckoutSheet` slot.
</Note>

## Property

| Property | Type               | Description                                                                                        |
| -------- | ------------------ | -------------------------------------------------------------------------------------------------- |
| `state`  | `StateFlow<State>` | Observable state containing field values, validation errors, loading state, and form configuration |

## Methods

### Field update methods

| Method                                | Parameter        | Description                                      |
| ------------------------------------- | ---------------- | ------------------------------------------------ |
| `updateCardNumber(value: String)`     | Raw card number  | Updates card number. Triggers network detection. |
| `updateCvv(value: String)`            | CVV digits       | Updates CVV field                                |
| `updateExpiryDate(value: String)`     | Raw expiry input | Updates expiry date. Auto-formats to MM/YY.      |
| `updateCardholderName(value: String)` | Full name        | Updates cardholder name                          |
| `updatePostalCode(value: String)`     | Postal/ZIP code  | Updates billing postal code                      |
| `updateCountryCode(value: String)`    | ISO country code | Updates billing country code                     |
| `updateCity(value: String)`           | City name        | Updates billing city                             |
| `updateState(value: String)`          | State/region     | Updates billing state                            |
| `updateAddressLine1(value: String)`   | Street address   | Updates billing address line 1                   |
| `updateAddressLine2(value: String)`   | Apt, suite, etc. | Updates billing address line 2                   |
| `updatePhoneNumber(value: String)`    | Phone number     | Updates billing phone number                     |
| `updateFirstName(value: String)`      | First name       | Updates billing first name                       |
| `updateLastName(value: String)`       | Last name        | Updates billing last name                        |

### Action methods

| Method                                                                | Description                                                                                                 |
| --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `submit()`                                                            | Submits the form for tokenization and payment. Sets `state.isLoading` to `true` during submission.          |
| `selectCardNetwork(network: PrimerCardNetwork)`                       | Selects a network for co-badged cards. Only applicable when `state.networkSelection` has multiple networks. |
| `onFieldFocusChange(type: PrimerInputElementType, hasFocus: Boolean)` | Notifies controller of field focus changes. Triggers on-blur validation.                                    |
| `requestCountrySelection()`                                           | Opens the country selection picker UI                                                                       |
| `setVaultOnSuccess(vault: Boolean)`                                   | Configures whether to save the payment method to vault after success. Requires `customerId` in session.     |

## State

```kotlin theme={"dark"}
data class State(
    val cardFields: List<PrimerInputElementType>,
    val billingFields: List<PrimerInputElementType>,
    val fieldErrors: List<SyncValidationError>?,
    val data: Map<PrimerInputElementType, String>,
    val isLoading: Boolean,
    val isFormEnabled: Boolean,
    val selectedCountry: PrimerCountry?,
    val networkSelection: NetworkSelection?,
    val binData: PrimerBinData?,
    val fieldFocusStates: Map<PrimerInputElementType, FieldState>,
    val isFormValid: Boolean,
    val vaultOnSuccess: Boolean,
)
```

| Property           | Type                                      | Description                                                                                                                                        |
| ------------------ | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cardFields`       | `List<PrimerInputElementType>`            | Card input fields required by the session                                                                                                          |
| `billingFields`    | `List<PrimerInputElementType>`            | Billing address fields required by the session. Empty if not required.                                                                             |
| `fieldErrors`      | `List<SyncValidationError>?`              | Validation errors per field. `null` before first validation. See [Validation](/docs/sdk/android-checkout/v3.0.0-beta/api/common/validation-result).     |
| `data`             | `Map<PrimerInputElementType, String>`     | Current field values keyed by input element type                                                                                                   |
| `isLoading`        | `Boolean`                                 | `true` while submitting a payment                                                                                                                  |
| `isFormEnabled`    | `Boolean`                                 | `true` when the form accepts input. `false` during submission.                                                                                     |
| `selectedCountry`  | `PrimerCountry?`                          | Selected billing country, or `null`                                                                                                                |
| `networkSelection` | `NetworkSelection?`                       | Co-badge network selection data. `null` when card is not co-badged.                                                                                |
| `binData`          | `PrimerBinData?`                          | BIN-data lookup result for the entered card number (issuer, funding type, regional debit network, etc.). `null` until the BIN range is recognised. |
| `fieldFocusStates` | `Map<PrimerInputElementType, FieldState>` | Focus state per field. `FieldState` contains `hasFocus`, `hasBeenFocused`, and `shouldShowError`.                                                  |
| `isFormValid`      | `Boolean`                                 | `true` when all required fields pass validation. Updates in real-time.                                                                             |
| `vaultOnSuccess`   | `Boolean`                                 | Whether payment method will be saved to vault on success                                                                                           |

### NetworkSelection

```kotlin theme={"dark"}
data class NetworkSelection(
    val availableNetworks: List<PrimerCardNetwork>,
    val selectedNetwork: PrimerCardNetwork?,
)
```

| Property            | Type                      | Description                           |
| ------------------- | ------------------------- | ------------------------------------- |
| `availableNetworks` | `List<PrimerCardNetwork>` | Card networks available for this card |
| `selectedNetwork`   | `PrimerCardNetwork?`      | Currently selected network, or `null` |

## PrimerInputElementType

| Value             | Field                  |
| ----------------- | ---------------------- |
| `CARD_NUMBER`     | Card number            |
| `EXPIRY_DATE`     | Expiry date (MM/YY)    |
| `CVV`             | CVV / security code    |
| `CARDHOLDER_NAME` | Cardholder name        |
| `POSTAL_CODE`     | Billing postal code    |
| `COUNTRY_CODE`    | Billing country        |
| `CITY`            | Billing city           |
| `STATE`           | Billing state/region   |
| `ADDRESS_LINE_1`  | Billing address line 1 |
| `ADDRESS_LINE_2`  | Billing address line 2 |
| `PHONE_NUMBER`    | Billing phone number   |
| `FIRST_NAME`      | Billing first name     |
| `LAST_NAME`       | Billing last name      |

## SyncValidationError

```kotlin theme={"dark"}
class SyncValidationError {
    val inputElementType: PrimerInputElementType
    val errorId: String
    val fieldId: Int
    val errorResId: Int?
    val errorFormatId: Int?
}
```

| Property           | Type                     | Description                                        |
| ------------------ | ------------------------ | -------------------------------------------------- |
| `inputElementType` | `PrimerInputElementType` | Field that has the error                           |
| `errorId`          | `String`                 | Error identifier describing the validation failure |
| `fieldId`          | `Int`                    | Field identifier                                   |
| `errorResId`       | `Int?`                   | Android string resource ID for the error message   |
| `errorFormatId`    | `Int?`                   | Android string resource ID for the error format    |

See [Validation](/docs/sdk/android-checkout/v3.0.0-beta/api/common/validation-result) for details.

## FieldState

```kotlin theme={"dark"}
data class FieldState(
    val hasFocus: Boolean,
    val hasBeenFocused: Boolean,
    val shouldShowError: Boolean,
)
```

| Property          | Type      | Description                                           |
| ----------------- | --------- | ----------------------------------------------------- |
| `hasFocus`        | `Boolean` | Whether the field currently has focus                 |
| `hasBeenFocused`  | `Boolean` | Whether the field has been focused at least once      |
| `shouldShowError` | `Boolean` | Whether the field should display its validation error |

<Info>
  Validation errors appear on blur (when the user leaves a field), not while typing. Use `isFormValid` for submit button state and `fieldErrors` for error messages.
</Info>
