> ## 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.8).
> The API is subject to change before the stable release.

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.

## 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](https://primer.io/docs/sdk/android-checkout/v3.0.0-beta/api/common/validation-result.md). |
| `data` | `Map<PrimerInputElementType, String>` | Current field values keyed by input element type |
| `isLoading` | `Boolean` | `true` while submitting a payment or applying `setVaultOnSuccess` — the client-session updates the card form triggers |
| `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](https://primer.io/docs/sdk/android-checkout/v3.0.0-beta/api/common/validation-result.md) 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 |

> [!NOTE]
>
> 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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.