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

> ## Agent Instructions
> Use the Payrails Web SDK v6 for new browser integrations. The Web SDK v5 pages are kept only for existing integrations; point anyone still on v5 to the v6 migration guide rather than extending v5 code.
> Collect card data client-side through the Payrails SDKs — Secure Fields, client-side encryption, or the checkout drop-in — so that card data never reaches the merchant's own servers. Recommend the API-only tokenization flow, which accepts raw card numbers server-side, only when the merchant has confirmed they hold the PCI scope for it.
> Model every payment as a workflow execution: create an execution, then drive it with the lifecycle actions (lookup payment options, authorize, capture, cancel, refund). Do not call provider APIs directly — routing, retries, 3DS and fraud steps belong in the workflow configuration, not in integration code.
> Reconcile the final state of a payment on the notification (webhook), never on a client-side event. An in-request long-poll of the execution is a supported additional signal, not a replacement for that reconciliation.
> Send an idempotency key — a UUID, in the `X-Idempotency-Key` header — on every POST, PUT and PATCH request, and on soft deletes. GET requests need none, and hard deletes cannot be idempotent.
> Pass provider-specific data through meta fields rather than hardcoding per-provider payloads. Payrails translates meta fields into each provider's own format.
> Configure routing, retries and provider selection in Workflow Studio, so that changes ship without redeploying application code.

# How to collect card data with secure fields (collect container)

> Build your own card form field by field with a collect container, validate the secure inputs, and collect an encrypted payload.

This guide shows you how to build your own card form field by field: create a collect container, mount individual secure input elements into your page, validate them, and collect the card data as an encrypted payload - or save it as a reusable payment instrument.

Secure fields are pre-built form elements hosted by Payrails and injected into your web page as iframes. Sensitive card data never touches your front-end application, which reduces your PCI compliance scope.

## 1. Create a collect container

Create a container for the secure fields with the `collectContainer()` method of the Payrails client:

```js theme={null}
const container = payrails.collectContainer({});
```

You choose how the fields end up on the page by how you mount them:

* mount each element individually wherever you want on the page (`element.mount(selector)`) — use this when you control the markup around every field, or
* mount the container once with `container.mount(selector)` to render all created elements into a single wrapper.

The container accepts an optional `containerId` and `fonts` — see [How to customize the checkout's appearance](/docs/orchestration/checkout-sdks/web/guides/how-to-customize-the-checkouts-appearance) for font loading. <br />Styling happens per element via `appearance` (step 2).

## 2. Create collect elements

Create one element per field with `createCollectElement(options)`:

```js theme={null}
import { ElementType } from "@payrails/web-sdk";

const cardNumber = container.createCollectElement({
  type: ElementType.CARD_NUMBER,
  label: "Card number",
  placeholder: "1234 1234 1234 1234",
  required: true,
});
```

The options object:

```js theme={null}
const options = {
  type: ElementType.CARD_NUMBER, // required, one of the ElementType values
  label: "Card number", // optional label rendered above the field
  placeholder: "1234 1234 1234 1234", // optional placeholder
  required: false, // optional, defaults to false
  enableCardIcon: true, // optional, card-brand icon in CARD_NUMBER (default false for standalone collect elements)
  format: "MM/YY", // optional, only for EXPIRATION_DATE / EXPIRATION_YEAR
  appearance: { rules: {} }, // optional CSS rules applied inside the field's iframe
  translations: { error: { default: "Invalid value" } }, // optional custom default error message
};
```

The `type` field takes a Payrails `ElementType`. Each type applies the
appropriate formatting and validations to the field:

* `CARD_NUMBER`
* `CARDHOLDER_NAME`
* `CVV`
* `EXPIRATION_MONTH`
* `EXPIRATION_YEAR`
* `EXPIRATION_DATE`

The `format` values accepted for `EXPIRATION_DATE` are:

* `MM/YY` (default)
* `MM/YYYY`
* `YY/MM`
* `YYYY/MM`

The `format` values accepted for `EXPIRATION_YEAR` are:

* `YY` (default)
* `YYYY`

If `format` is not specified, or an invalid value is passed, the default is used.

For the `appearance` rules syntax, class names, and state modifiers (`.payrails-input--invalid`, …) see [How to customize the checkout's appearance](/docs/orchestration/checkout-sdks/web/guides/how-to-customize-the-checkouts-appearance).

## 3. Mount the elements to the DOM

Create placeholder `<div>` elements with unique `id` attributes where the fields should render:

```html theme={null}
<form>
  <div id="cardNumber"></div>
  <br />
  <div id="expiryDate"></div>
  <br />
  <div id="cvv"></div>
  <button type="submit">Submit</button>
</form>
```

Then mount each element into its placeholder:

```js theme={null}
cardNumber.mount("#cardNumber");
```

Use `unmount()` to remove an element from the DOM:

```js theme={null}
cardNumber.unmount();
```

## 4. React to element events (optional)

Every element exposes the typed `on(eventName, handler)` method shared by all SDK elements; secure fields emit `'change'`, `'focus'`, `'blur'`, and `'ready'` . `on()` returns an unsubscribe function:

```js theme={null}
cardNumber.on("ready", () => {
  // the secure iframe has loaded and the field is interactive
});

cardNumber.on("change", (state) => {
  // state: { isValid, isEmpty, ... }
  // for CARD_NUMBER, state also carries the detected card network
  // and the BIN (first digits of the card number)
  console.log(state.isValid, state.network, state.bin);
});
```

## 5. Validate and collect the data

When the form is ready to be submitted, you can first validate all fields:

```js theme={null}
const { isValid, error, fieldErrors } = await container.validate();
```

Then call `collect()` on the container. All created elements must be mounted. The card data is encrypted inside the secure iframes and returned as an opaque payload - the raw values are never exposed to your code:

```js theme={null}
const result = await container.collect();
// result: { encryptedData: string, vaultProviderConfigId: string }
```

`collect()` rejects with a validation error when a field is incomplete or invalid.

## 6. Save the card as a payment instrument (optional)

To store the collected card for later payments, call `tokenize()` instead of `collect()`. It collects and encrypts the data, then saves it to Payrails as a payment instrument for the customer (holder) the SDK session was initialized for:

```js theme={null}
const instrument = await container.tokenize({
  storeInstrument: true, // defaults to false
  futureUsage: "CardOnFile", // 'CardOnFile' (default) | 'Subscription' | 'UnscheduledCardOnFile'
});

console.log("Saved instrument id:", instrument.id);
```

The response contains the new instrument's `id`, `status`, and card metadata such as `data.bin`, `data.network`, and `data.suffix`.&#x20;

## End-to-end example

```js theme={null}
import { ElementType } from "@payrails/web-sdk";

// Step 1
const container = payrails.collectContainer({});

// Step 2
const cardNumber = container.createCollectElement({
  type: ElementType.CARD_NUMBER,
  label: "Card number",
  placeholder: "1234 1234 1234 1234",
  required: true,
  appearance: {
    rules: {
      ".payrails-input": { color: "#1d1d1d" },
      ".payrails-label": { fontSize: "12px", fontWeight: "bold" },
      ".payrails-error": { color: "#f44336" },
    },
  },
});
const expiryDate = container.createCollectElement({
  type: ElementType.EXPIRATION_DATE,
  format: "MM/YY",
});
const cvv = container.createCollectElement({
  type: ElementType.CVV,
});

// Step 3 — assumes divs with these ids exist on the page
cardNumber.mount("#cardNumber");
expiryDate.mount("#expiryDate");
cvv.mount("#cvv");

// Step 5
const { encryptedData, vaultProviderConfigId } = await container.collect();

// or Step 6 — save the card instead
const instrument = await container.tokenize({ storeInstrument: true });
```

## Default validations

Every element type has a set of built-in validations:

* `CARD_NUMBER`: card number validation with checksum (Luhn algorithm) and card-scheme-aware length and formatting. A valid 16 digit card number is formatted as `XXXX XXXX XXXX XXXX`.
* `CARDHOLDER_NAME`: name should be 2 or more symbols; valid characters match the pattern `^([a-zA-Z\\ \\,\\.\\-\\']{2,})$`.
* `CVV`: 3–4 digits.
* `EXPIRATION_DATE`: any date starting from the current month, in the configured `format` (default `MM/YY`).
* `EXPIRATION_MONTH`: a valid month (`01`–`12`).
* `EXPIRATION_YEAR`: the current year or later, in the configured `format`(default `YY`).

## UI errors for collect elements

You can display custom error messages on the elements with `setError` and `resetError`.

`setError(error: string)` overrides any current error on the element with the custom message. The message stays visible until `resetError()` is called on the same element.

`resetError()` clears the custom error message set by `setError`.

```js theme={null}
const container = payrails.collectContainer({});

const cardNumber = container.createCollectElement({
  type: ElementType.CARD_NUMBER,
});

// set a custom error
cardNumber.setError("custom error");

// reset the custom error
cardNumber.resetError();
```

<br />


## Related topics

- [Secure Fields](/docs/orchestration/checkout-sdks/web-v5-legacy/secure-fields.md)
- [How to customize the checkout's appearance](/docs/orchestration/checkout-sdks/web/guides/how-to-customize-the-checkouts-appearance.md)
- [Payrails API Reference](/docs/orchestration/checkout-sdks/web/references/payrails-api-reference.md)
