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

# CVC Element

> Re-collect the security code for a stored card in an isolated iframe, without re-entering the full card.

The CVC Element renders a single secure CVC/CVV input inside an iframe and applies the value to a card you already tokenized. The buyer re-enters three or four digits; the card number and expiry stay where they are and are never re-collected.

You keep the same `cardToken` throughout - the element patches the stored card in place rather than creating a new one.

## Why this is needed

A card verification code can't be stored, so the CVC captured when you tokenized a card expires while the rest of the card stays valid. Charging a stored card after that point requires a fresh CVC from the buyer. This element is how you collect it.

<Warning>
  The CVC is captured and applied inside the iframe, so it never reaches your
  servers - this keeps you out of PCI DSS scope. A server-side `POST
    	/card/{cardToken}/cvv` REST endpoint also exists (it is not exposed through
  the SDK) for backends that can't run an iframe, but passing the raw CVV
  through your own backend brings your systems into PCI scope. Prefer this
  element unless you already run a PCI-compliant environment.
</Warning>

***

## Usage

```js theme={null}
import Henry from '@henrylabs/js';

const cvcEl = Henry.createCvcElement('#cvc-container', {
	cardToken: 'card_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
});
```

Add a container in your HTML:

```html theme={null}
<div id="cvc-container"></div>
```

The `cardToken` is the token you received from a previous `cardEl.submit()` call - see the [Card Element](/v1/sdk/client/elements/card-element).

To apply the new code, call `validate()` then `submit()`:

```js theme={null}
const isValid = await cvcEl.validate();

if (isValid) {
	await cvcEl.submit();
	// the existing cardToken is now chargeable again
}
```

***

## Options

### `cardToken` `string`

Required. The card token identifying the stored card to update.

### `styles` `CvcStyles`

Optional. Customize the appearance of the CVC input.

```js theme={null}
const cvcEl = Henry.createCvcElement('#cvc-container', {
	cardToken: 'card_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
	styles: {
		cardVerification: {
			base: {
				color: '#111',
				fontSize: '16px',
				fontFamily: 'Inter, sans-serif',
			},
			'::placeholder': {
				color: '#aaa',
			},
			invalid: {
				color: '#e53e3e',
			},
		},
	},
});
```

#### `CvcStyles` reference

| Field              | Type               | Description                                   |
| ------------------ | ------------------ | --------------------------------------------- |
| `cardVerification` | `CardElementStyle` | Styles for the CVV/CVC field                  |
| `container`        | `CSSProperties`    | Styles applied to the outer container element |
| `inputWrappers`    | `CSSProperties`    | Styles applied to the field wrapper           |
| `fonts`            | `string[]`         | Font URLs to load inside the iframe           |

`cardVerification` accepts the same `CardElementStyle` shape documented under the [Card Element](/v1/sdk/client/elements/card-element#cardelementstyle-reference) - `base`, `complete`, `invalid`, `empty`, `container`, `placeholder`, `label`, `labelContainer`, `fonts`, and the pseudo-class overrides.

***

## Methods

### `validate()` → `Promise<boolean>`

Returns `true` when the field contains a valid code. Use this to gate your submit button.

```js theme={null}
const isValid = await cvcEl.validate();
if (!isValid) {
	// show your own error message
}
```

### `submit()` → `Promise<void>`

Applies the entered code to the stored card. Resolves on success and rejects with the failure reason otherwise. The `cardToken` is unchanged - after this resolves you can charge it again.

```js theme={null}
await cvcEl.submit();
```

### `destroy()`

Removes the iframe and cleans up all event listeners. Call this when navigating away or unmounting a component.

```js theme={null}
cvcEl.destroy();
```

***

## Full example

```html theme={null}
<form id="confirm-form">
	<div id="cvc-container"></div>
	<button type="submit">Confirm & pay</button>
</form>

<script type="module">
	import Henry from '@henrylabs/js';

	const cvcEl = Henry.createCvcElement('#cvc-container', {
		cardToken: 'card_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
		styles: {
			cardVerification: {
				base: { color: '#111', fontSize: '16px' },
				'::placeholder': { color: '#aaa' },
				invalid: { color: '#e53e3e' },
			},
			fonts: ['https://fonts.googleapis.com/css2?family=Inter&display=swap'],
		},
	});

	document.getElementById('confirm-form').addEventListener('submit', async (e) => {
		e.preventDefault();

		const isValid = await cvcEl.validate();
		if (!isValid) return;

		await cvcEl.submit();

		await fetch('/api/pay', {
			method: 'POST',
			body: JSON.stringify({ cardToken: 'card_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' }),
			headers: { 'Content-Type': 'application/json' },
		});
	});
</script>
```

***

## React

```tsx theme={null}
import { useEffect, useRef } from 'react';
import Henry from '@henrylabs/js';

function CvcConfirm({ cardToken }: { cardToken: string }) {
	const cvcElRef = useRef<ReturnType<typeof Henry.createCvcElement> | null>(null);
	const initialized = useRef(false);

	useEffect(() => {
		if (initialized.current) return;
		initialized.current = true;

		cvcElRef.current = Henry.createCvcElement('#cvc-container', {
			cardToken,
			styles: {
				cardVerification: {
					base: { color: '#111', fontSize: '16px' },
				},
			},
		});

		return () => cvcElRef.current?.destroy();
	}, [cardToken]);

	async function handleSubmit(e: React.FormEvent) {
		e.preventDefault();
		if (!cvcElRef.current) return;

		const isValid = await cvcElRef.current.validate();
		if (!isValid) return;

		await cvcElRef.current.submit();
		// charge the same cardToken from your server
	}

	return (
		<form onSubmit={handleSubmit}>
			<div id='cvc-container' />
			<button type='submit'>Confirm & pay</button>
		</form>
	);
}
```

***

## Related

<CardGroup cols={2}>
  <Card title="Card Element" icon="credit-card" href="/v1/sdk/client/elements/card-element">
    Collect and tokenize a full card
  </Card>

  <Card title="Server SDK - Checkout" icon="server" href="/v1/sdk/server/guides/checkout#headless-checkout">
    Build headless checkout flows server-side
  </Card>
</CardGroup>
