> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs-staging.coinflow.cash/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs-staging.coinflow.cash/_mcp/server.

# Card Form Components

> Use Coinflow's Card Form SDK components to securely collect and tokenize credit card information with a single `tokenize()` call. Available for React, Vue, Angular, and React Native.

Coinflow's Card Form components provide a streamlined, PCI-compliant way to collect credit card information directly in your application. Instead of managing separate card number, CVV, and expiration inputs, these components bundle everything into a single embeddable form with one `tokenize()` method.

## When to Use Card Form Components

Use these components when you want to:

* Collect card details for a **new card checkout** without building your own form layout
* Tokenize a **card number with expiration** for use with the [Card Checkout](/api-reference/api-reference/checkout/card-checkout) API
* Re-collect a **CVV only** for a saved card before calling the [Saved Card Checkout](/api-reference/api-reference/checkout/token-checkout) API
* Apply **custom theming** (fonts, colors, placeholders) to match your brand

## Available Components

| Component                | Description                                                                                                                                                         |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CoinflowCardForm`       | Full card input — card number, expiration date, and CVV. Renders in a single row on wide containers and automatically reflows to two rows when space is constrained |
| `CoinflowCardNumberForm` | Card number and expiration date only (no CVV)                                                                                                                       |
| `CoinflowCvvForm`        | CVV-only input for re-tokenizing a saved card                                                                                                                       |

All three components expose a `tokenize()` method via a ref that returns a token you can pass to the Coinflow checkout APIs.

## Installation

#### npm

```shell
npm install @coinflowlabs/react
```

#### Vue

```shell
npm install @coinflowlabs/vue
```

#### Angular

```shell
npm install @coinflowlabs/angular
```

#### React Native

```shell
npm install @coinflowlabs/react-native
```

## Tokenizing a New Card

Use `CoinflowCardForm` to collect the full card details (number, expiration, CVV) and tokenize them in a single call.

#### React

```typescript
import {useRef} from 'react';
import {
  CoinflowCardForm,
  CardFormRef,
  CoinflowEnvs,
} from '@coinflowlabs/react';

function NewCardCheckout() {
  const cardFormRef = useRef<CardFormRef>(null);

  const handleTokenize = async () => {
    try {
      const result = await cardFormRef.current?.tokenize();
      if (!result) return;

      console.log('Token:', result.token);
      console.log('Exp Month:', result.expMonth);
      console.log('Exp Year:', result.expYear);

      // Pass result.token, result.expMonth, result.expYear
      // to the Card Checkout API endpoint
    } catch (err) {
      console.error('Tokenization failed:', err);
    }
  };

  return (
    <div>
      <CoinflowCardForm
        ref={cardFormRef}
        merchantId="your-merchant-id"
        env="sandbox"
      />
      <button onClick={handleTokenize}>Pay</button>
    </div>
  );
}
```

#### Vue

```vue
<script setup lang="ts">
import {ref} from 'vue';
import {CoinflowCardForm} from '@coinflowlabs/vue';

const cardFormRef = ref<InstanceType<typeof CoinflowCardForm> | null>(null);

async function handleTokenize() {
  try {
    const result = await cardFormRef.value?.tokenize();
    if (!result) return;

    console.log('Token:', result.token);
    console.log('Exp Month:', result.expMonth);
    console.log('Exp Year:', result.expYear);

    // Pass result.token, result.expMonth, result.expYear
    // to the Card Checkout API endpoint
  } catch (err) {
    console.error('Tokenization failed:', err);
  }
}
</script>

<template>
  <div>
    <CoinflowCardForm
      ref="cardFormRef"
      :args="{
        merchantId: 'your-merchant-id',
        env: 'sandbox',
        variant: 'card-form',
      }"
    />
    <button @click="handleTokenize">Pay</button>
  </div>
</template>
```

#### Angular

```typescript
import {Component, ViewChild} from '@angular/core';
import {CoinflowCardForm} from '@coinflowlabs/angular';

@Component({
  selector: 'app-checkout',
  standalone: true,
  imports: [CoinflowCardForm],
  template: `
    <lib-coinflow-card-form
      #cardForm
      [args]="{
        merchantId: 'your-merchant-id',
        env: 'sandbox',
        variant: 'card-form'
      }"
    />
    <button (click)="handleTokenize()">Pay</button>
  `,
})
export class CheckoutComponent {
  @ViewChild('cardForm') cardForm!: CoinflowCardForm;

  async handleTokenize() {
    try {
      const result = await this.cardForm.tokenize();
      console.log('Token:', result.token);
      console.log('Exp Month:', result.expMonth);
      console.log('Exp Year:', result.expYear);

      // Pass result.token, result.expMonth, result.expYear
      // to the Card Checkout API endpoint
    } catch (err) {
      console.error('Tokenization failed:', err);
    }
  }
}
```

#### React Native

```typescript
import {useRef} from 'react';
import {Button, View} from 'react-native';
import {
  CoinflowCardForm,
  CardFormNativeRef,
} from '@coinflowlabs/react-native';

function NewCardCheckout() {
  const cardFormRef = useRef<CardFormNativeRef>(null);

  const handleTokenize = async () => {
    try {
      const result = await cardFormRef.current?.tokenize();
      if (!result) return;

      console.log('Token:', result.token);
      console.log('Exp Month:', result.expMonth);
      console.log('Exp Year:', result.expYear);
    } catch (err) {
      console.error('Tokenization failed:', err);
    }
  };

  return (
    <View>
      <CoinflowCardForm
        ref={cardFormRef}
        merchantId="your-merchant-id"
        env="sandbox"
      />
      <Button title="Pay" onPress={handleTokenize} />
    </View>
  );
}
```

## Refreshing a Saved Card Token (CVV Only)

When a customer pays with a saved card, you need to re-collect the CVV and refresh the token. Use `CoinflowCvvForm` with the saved card's `token` from the [Get Customer](/api-reference/api-reference/customers/get-customer) endpoint.

#### React

```typescript
import {useRef} from 'react';
import {CoinflowCvvForm, CardFormRef} from '@coinflowlabs/react';

function SavedCardCheckout({savedCardToken}: {savedCardToken: string}) {
  const cvvFormRef = useRef<CardFormRef>(null);

  const handleTokenize = async () => {
    try {
      const result = await cvvFormRef.current?.tokenize();
      if (!result) return;

      // Pass result.token to the Saved Card Checkout API endpoint
      console.log('Refreshed token:', result.token);
    } catch (err) {
      console.error('CVV tokenization failed:', err);
    }
  };

  return (
    <div>
      <CoinflowCvvForm
        ref={cvvFormRef}
        merchantId="your-merchant-id"
        env="sandbox"
        token={savedCardToken}
      />
      <button onClick={handleTokenize}>Pay with Saved Card</button>
    </div>
  );
}
```

#### Vue

```vue
<script setup lang="ts">
import {ref} from 'vue';
import {CoinflowCardForm} from '@coinflowlabs/vue';

const props = defineProps<{savedCardToken: string}>();
const cvvFormRef = ref<InstanceType<typeof CoinflowCardForm> | null>(null);

async function handleTokenize() {
  try {
    const result = await cvvFormRef.value?.tokenize();
    if (!result) return;

    console.log('Refreshed token:', result.token);
  } catch (err) {
    console.error('CVV tokenization failed:', err);
  }
}
</script>

<template>
  <div>
    <CoinflowCardForm
      ref="cvvFormRef"
      :args="{
        merchantId: 'your-merchant-id',
        env: 'sandbox',
        variant: 'cvv-form',
        token: props.savedCardToken,
      }"
    />
    <button @click="handleTokenize">Pay with Saved Card</button>
  </div>
</template>
```

> **Info**
>
> In Vue, the `CoinflowCardForm` component accepts a `variant` prop to switch between `'card-form'`, `'card-number-form'`, and `'cvv-form'` modes.

> **Warning**
>
> If the saved card can't be used, the CVV form shows an error instead of a CVV input, calls `onInputError` with the error message, and `tokenize()` rejects with the same message. The message is one of:
>
> * `Card token is invalid. Please add a new card`: the `token` doesn't match a saved card, or the card was saved in a way Coinflow no longer supports.
> * `Card entry is unavailable. Please re-enter your card details.`: any other error.
>
> When this happens, ask the customer to enter their card again with `CoinflowCardForm`.

## Tokenizing Card Number Only (No CVV)

Use `CoinflowCardNumberForm` when you only need to collect the card number and expiration — for example, when saving a card for future use without an immediate charge.

```typescript
import {useRef} from 'react';
import {CoinflowCardNumberForm, CardFormRef} from '@coinflowlabs/react';

function SaveCardForLater() {
  const cardNumberRef = useRef<CardFormRef>(null);

  const handleTokenize = async () => {
    const result = await cardNumberRef.current?.tokenize();
    if (!result) return;

    // Store result.token, result.expMonth, result.expYear for later use
    console.log('Token:', result.token);
  };

  return (
    <div>
      <CoinflowCardNumberForm
        ref={cardNumberRef}
        merchantId="your-merchant-id"
        env="sandbox"
      />
      <button onClick={handleTokenize}>Save Card</button>
    </div>
  );
}
```

## `tokenize()` Response

The `tokenize()` method returns a `CardFormTokenResponse`:

| Field      | Type                | Description                                                                                             |
| ---------- | ------------------- | ------------------------------------------------------------------------------------------------------- |
| `token`    | `string`            | The PCI-compliant card token to pass to checkout APIs                                                   |
| `expMonth` | `string` (optional) | Two-digit expiration month (e.g., `"05"`) — returned by `CoinflowCardForm` and `CoinflowCardNumberForm` |
| `expYear`  | `string` (optional) | Two-digit expiration year (e.g., `"30"`) — returned by `CoinflowCardForm` and `CoinflowCardNumberForm`  |
| `bin`      | `string` (optional) | The card's BIN: the leading 6 or 8 digits of the card number                                            |
| `lastFour` | `string` (optional) | The last four digits of the card number                                                                 |
| `cardType` | `string` (optional) | The card network: `VISA`, `MSTR`, `AMEX`, or `DISC`                                                     |

> **Info**
>
> `CoinflowCvvForm` does not return `expMonth` or `expYear` since expiration details are already stored with the saved card. It does return `bin`, `lastFour`, and `cardType`.

> **Note**
>
> `bin`, `lastFour`, and `cardType` let you run fraud checks before creating a payment. They are omitted if Coinflow could not register the card at tokenize time, so treat their absence as "card details unavailable" rather than an error.

## Theming

All card form components accept a `theme` prop to customize the look and feel. Theme values are passed as a `MerchantTheme` object.

| Property                | Type            | Description                                                                                                        |
| ----------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------ |
| `font`                  | `string`        | Font family name — any [Google Font](https://fonts.google.com) is supported (e.g., `"Inter"`, `"Red Hat Display"`) |
| `fontSize`              | `string`        | Font size for input text (e.g., `"12px"`, `"14px"`)                                                                |
| `background`            | `string`        | Background color for input fields (e.g., `"#ffffff"`, `"#1a1a1a"`)                                                 |
| `textColor`             | `string`        | Input text color                                                                                                   |
| `style`                 | `MerchantStyle` | Border radius style — `MerchantStyle.Rounded`, `MerchantStyle.Pill`, or `MerchantStyle.Sharp`                      |
| `showCardIcon`          | `boolean`       | Show the detected card brand icon (Visa, Mastercard, etc.) to the left of the card number input                    |
| `cardNumberPlaceholder` | `string`        | Placeholder for the card number field                                                                              |
| `cvvPlaceholder`        | `string`        | Placeholder for the CVV field                                                                                      |
| `expirationPlaceholder` | `string`        | Placeholder for month/year fields (format: `"MM / YY"`)                                                            |

```typescript
<CoinflowCardForm
  ref={cardFormRef}
  merchantId="your-merchant-id"
  env="sandbox"
  theme={{
    font: 'Inter',
    fontSize: '14px',
    background: '#ffffff',
    textColor: '#111827',
    style: MerchantStyle.Rounded,
    showCardIcon: true,
    cardNumberPlaceholder: 'Card number',
    cvvPlaceholder: 'CVV',
    expirationPlaceholder: 'MM / YY',
  }}
/>
```

> **Warning**
>
> The theme values shown above are **examples** for illustration. Configure your branding in the [Merchant Dashboard](/guides/merchant-dashboard/login-account-access) or contact the Coinflow integrations team to set these values for your account.

## Responsive Layout

`CoinflowCardForm` automatically adapts its layout to the width of the container you place it in — no configuration is required.

* **Wide containers** — the card number, expiration, and CVV render together on a single row.
* **Narrow containers** — when there isn't enough horizontal space for all three fields, the form reflows to two rows: the card number on the first row, with the expiration and CVV on the second.

This is especially useful in mobile checkout UIs, where a single-row layout can clip the card number in narrow viewports. The form measures its own container and switches layouts on the fly, including when the viewport is resized or rotated.

Because the form's height changes when it reflows, the embedded iframe reports its content height back to the SDK, which resizes the iframe automatically so the fields are never clipped.

> **Tip**
>
> To let the form size itself correctly, place it in a container whose **width** you control and whose **height** is allowed to grow. Avoid setting a fixed height on the component or its wrapping element — doing so prevents the form from expanding to its two-row layout.

> **Info**
>
> Responsive reflow and automatic height adjustment are built in to `CoinflowCardForm`. To pick up this behavior, update to the latest version of your SDK (`@coinflowlabs/react`, `@coinflowlabs/vue`, `@coinflowlabs/angular`, or `@coinflowlabs/react-native`). No code changes are needed.

## Component Props Reference

### `CoinflowCardForm` / `CoinflowCardNumberForm`

| Prop         | Type            | Required | Description                                    |
| ------------ | --------------- | -------- | ---------------------------------------------- |
| `merchantId` | `string`        | Yes      | Your Coinflow merchant ID                      |
| `env`        | `CoinflowEnvs`  | No       | `"sandbox"` or `"prod"` (defaults to `"prod"`) |
| `theme`      | `MerchantTheme` | No       | Custom theming options                         |
| `onLoad`     | `() => void`    | No       | Callback when the form iframe has loaded       |

### `CoinflowCvvForm`

| Prop           | Type                                               | Required | Description                                                                                   |
| -------------- | -------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------- |
| `merchantId`   | `string`                                           | Yes      | Your Coinflow merchant ID                                                                     |
| `env`          | `CoinflowEnvs`                                     | No       | `"sandbox"` or `"prod"` (defaults to `"prod"`)                                                |
| `theme`        | `MerchantTheme`                                    | No       | Custom theming options                                                                        |
| `token`        | `string`                                           | Yes      | The saved card token from [Get Customer](/api-reference/api-reference/customers/get-customer) |
| `onLoad`       | `() => void`                                       | No       | Callback when the form iframe has loaded                                                      |
| `onInputError` | `(info: {field: string; message: string}) => void` | No       | Callback when the form shows an error, including when the saved card can no longer be used    |

## Token Expiration

Tokens expire if not used within **7 days** of creation. Once used, a token is valid for **5 minutes** in production. In the sandbox environment, tokens remain valid indefinitely. If a token expires, call `tokenize()` again to generate a new one.

## Migrating from Legacy Card Inputs

The legacy `CoinflowCardNumberInput`, `CoinflowCvvInput`, `CoinflowCvvOnlyInput`, and `CoinflowCardOnlyInput` components were removed in `@coinflowlabs/react` 6.0.0, `@coinflowlabs/vue` 2.0.0, and `@coinflowlabs/angular` 2.0.0. If you are upgrading from an earlier version, replace them with the Card Form components:

| Legacy                                                                   | New                                 |
| ------------------------------------------------------------------------ | ----------------------------------- |
| `CoinflowCardNumberInput` + `CoinflowCvvInput` + manual expiration input | `CoinflowCardForm` (all-in-one)     |
| `CoinflowCardNumberInput` + manual expiration input                      | `CoinflowCardNumberForm`            |
| `CoinflowCvvOnlyInput`                                                   | `CoinflowCvvForm`                   |
| `CoinflowCardOnlyInput`                                                  | `CoinflowCardNumberForm`            |
| `ref.current.getToken()`                                                 | `ref.current.tokenize()`            |
| CSS string-based styling (`css` prop)                                    | Object-based theming (`theme` prop) |

> **Warning**
>
> Upgrading to these versions is a breaking change if you still use the legacy components. Replace them before you upgrade.