# Card Collection

_Collect and encrypt cardholder data in a PCI-compliant iframe, then safely use it with your APIs and processors via Relay._

You can collect card information in a few ways with Evervault, but our prebuilt UI components are the most common. Cardholder data that's encrypted with Evervault can be safely stored in your database and then shared with third-party payment processors using [Relay](/relay). This guide walks you through the entire process using our Card component for collection.

<CornerFrame>
  <CardCollection />
</CornerFrame>

## The Card component

The Card component is a secure iframe, hosted by Evervault, that you embed in your product to collect card data. When customers input their card information, it's encrypted within the iframe before being returned to you for storage. This approach reduces your [PCI DSS](https://www.pcisecuritystandards.org/standards/pci-dss/) scope because Evervault hosts the iframe, and you never handle plaintext card data.

## Get started with the Card component
### Install the Evervault SDK

#### Browser

Our [JavaScript SDK](/sdks/javascript) is distributed from our CDN, and can be installed by placing this script tag in the head of your HTML file. The SDK must be loaded directly from our CDN and cannot be bundled with your application or self hosted.

```html
<script src="https://js.evervault.com/v2"></script>
```

Once the SDK is installed, initialize it using your Team ID and App ID. You can find these in the [Evervault Dashboard](https://app.evervault.com).

```javascript
const evervault = new Evervault("<TEAM_ID>", "<APP_ID>");
```

You can also install Evervault via the `@evervault/js` package on npm. This package is a light wrapper which handles loading the SDK from our CDN and also provides TypeScript definitions.

```javascript
import { loadEvervault } from "@evervault/js";

const evervault = loadEvervault("<TEAM_ID>", "<APP_ID>");
```

#### Frequently Asked Questions

##### Why does the SDK need to be loaded from the CDN?

The SDK must be loaded directly from our CDN in order to be PCI Compliant.

##### How do I get my Team ID and App ID?

You can find your Team ID and App ID in the [Evervault Dashboard](https://app.evervault.com).

##### Can I load the SDK asynchronously?

You can load the SDK asynchronously using the `async` attribute on the script tag to prevent blocking the loading of your page. However, it is important to note that you will need to wait for the SDK to load before making any API calls.

    ```html
    <!DOCTYPE html>
    <html>
      <head>
        <script src="https://js.evervault.com/v2" onload="onEvervaultReady()" async></script>
        <script>
          function onEvervaultReady() {
            const evervault = new Evervault('<TEAM_ID>', '<APP_ID>');
          }
        </script>
      </head>
    </html>
    ```

#### React

Our React SDK is distributed via npm and can be installed using your preferred package manager.

Once installed, Initialize the SDK by wrapping your application with the `EvervaultProvider` component.

```jsx
import { EvervaultProvider } from "@evervault/react";

export default function App() {
  return (
    <EvervaultProvider teamId="<TEAM_ID>" appId="<APP_ID>">
      ...
    </EvervaultProvider>
  );
}
```

#### Browser

### Create a Card component

Initialize the Card component and mount it to a DOM element. See the [SDK documentation](/sdks/javascript#uicard) for available parameters.

```javascript
const card = evervault.card({
  theme: evervault.ui.themes.clean(),
});
card.mount("#card-details");
```

#### React

### Render the Card component

The `<Card />` component can be mounted in any child of the `EvervaultProvider`. The `onChange` prop is called every time the customer updates their card data, and includes the encrypted card data as well as validation information. You can use this to store the card data in state and pass it to your backend to process a payment. See the [SDK documentation](/sdks/react#card) for other available props.

```jsx
import { Card } from "@evervault/react";

export function Checkout() {
  const [cardData, setCardData] = useState(null);

  const handleCheckout = () => {
    if (!cardData.isValid) {
      // The user has entered an invalid card.
      return;
    }

    // Pass the encrypted card data to your backend
    fetch("my-api.com/checkout", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        number: cardData.card.number,
        expiry: cardData.card.expiry,
        cvc: cardData.card.cvc,
      }),
    });
  };

  return (
    <>
      <Card onChange={setCardData} />
      <button onClick={handleCheckout}>Checkout</button>
    </>
  );
}
```

You can customize the appearance of the Card component by passing a custom theme or extending one of our prebuilt themes. Read more about [customization](/cards/card-collection#customization).

#### Browser

### Submit the encrypted card data to your API

The encrypted card values can be accessed from the `card.values` object or by subscribing to the `change` event with the `card.on` method.

Inside your submit handler, you can access the encrypted card data and pass it to your backend.

```javascript
function handleSubmit() {
  // Pass the encrypted card data to your backend
  fetch("my-api.com/checkout", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      number: card.values.card.number,
      expiry: card.values.card.expiry,
      cvc: card.values.card.cvc,
    }),
  });
}
```

#### React Native

Our React Native SDK provides a set of premade components for collecting and encrypting card data in a safe and secure way.

### Install the Evervault SDK

Our React Native SDK is distributed via `npm` and can be installed using your preferred package manager. The [react-native-webview](https://www.npmjs.com/package/react-native-webview) package is a peer dependency and will need to be installed as well.

Once installed, Initialize the SDK by wrapping your application with the `EvervaultProvider` component.

```jsx
import { EvervaultProvider } from "@evervault/react-native";

export default function App() {
  return (
    <EvervaultProvider teamId="<TEAM_ID>" appId="<APP_ID>">
      ...
    </EvervaultProvider>
  );
}
```

### Render the Card component

The `<Card />` component can be mounted in any child of the `EvervaultProvider`. The `onChange` prop is called every time your customer updates their card data, and includes the encrypted card data as well as validation information. You can use this to store the card data in state and pass it to your backend to process a payment.

```tsx
import { Card, type CardPayload } from '@evervault/react-native';
import { Button, Form } from '@your/ui";

type CardFormProps = {
  onSubmit: () => void;
};

function CardForm({ onSubmit }: CardFormProps) {
  const [data, setData] = useState<CardPayload | null>(null);

  return (
    <Form>
      <Card onChange={setData}>
        <Card.Number />
        <Card.Expiry />
        <Card.Cvc />
      </Card>

      <Button disabled={!data?.isValid || !data?.isComplete} onPress={onSubmit}>
        Pay
      </Button>
    </Form>
  );
}
```

Learn more about the Card component in the [React Native SDK documentation](/sdks/react-native#card).

### Submit the encrypted card data to your API

The last step is to submit the encrypted card details to your API so they can be forwarded on to your payment processor.

```tsx
function Checkout() {
  const handleSubmit = (data: CardPayload) => {
    // Pass the encrypted card data to your backend
    fetch("my-api.com/checkout", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        number: data.card.number,
        expiry: data.card.expiry,
        cvc: data.card.cvc,
      }),
    });
  };

  return (
    <>
      <CardForm onSubmit={handleSubmit} />
    </>
  );
}
```

#### Swift

The Evervault iOS SDK includes an `EvervaultInputs` module, which provides a SwiftUI view called `PaymentCardInput`. This view is designed for capturing credit card information and automatically encrypts the credit card number and CVC without exposing the unencrypted data.

### Install the Evervault SDK

The Evervault iOS SDK can be installed using the Swift Package Manager.

1. Open your Xcode project.
2. Navigate to _File > Swift Packages > Add Package Dependency_.
3. Enter the repository URL for the Evervault iOS SDK: `https://github.com/evervault/evervault-ios.git`
4. Choose the latest available version or specify a version rule.

After you install the SDK, you can initialize it with your Team ID and App ID.

```swift
import EvervaultCore

Evervault.shared.configure(teamId: "<TEAM_ID>", appId: "<APP_ID>")
```

### Render the Card component

To use `PaymentCardInput`, make sure you import the `EvervaultInputs` module in your file, and then add the view to your SwiftUI hierarchy. See the [SDK documentation](/sdks/ios#inputs) for available parameters.

```swift
import EvervaultInputs

struct ContentView: View {

    @State private var cardData = PaymentCardData()

    var body: some View {
        VStack {
            PaymentCardInput(cardData: $cardData)

            // Data captured:
            Text("Encrypted credit card number: \(cardData.card.number)")
        }
    }
}
```

#### Android

The Android SDK provides a Compose view called `PaymentCard`. This view is designed for capturing credit card information and automatically encrypts the credit card number and CVC without exposing the unencrypted data.

### Install the Evervault SDK

Our Android SDK distributed via [maven](https://central.sonatype.com/search?q=com.evervault.sdk), and can be installed using your preferred build tool.

#### Android

Before using the Evervault Android SDK, you need to configure it with your Evervault Team ID and App ID. This step is essential for establishing a connection with the Evervault encryption service. You can find these in the [Evervault Dashboard](https://app.evervault.com).

```java
Evervault.shared.configure("<TEAM_ID>", "<APP_ID>")
```

### Render the PaymentCard view

Once the SDK is installed and configured, you can use the `PaymentCard` view to capture encrypted card data.

```java
UserParentLayout {
    val onDataChange: (PaymentCardData) -> Unit = {} // Handle card data
    // ...
    PaymentCard(onDataChange = onDataChange) {
        // User custom payment card layout using the [PaymentCardInputScope] components with a combination of user own components and modifiers
    }
}
```

You can learn more about the `PaymentCard` view in the [Android SDK documentation](/sdks/android#inputs).

## Forwarding encrypted card data

Now that you have the encrypted card data, you need to pass it to a third-party payment processor (Stripe, Adyen, etc.). To do this, you use [Relay](/relay) to decrypt the data after it leaves your infrastructure, and before it reaches the third-party API.

Relay is a network proxy that can be configured to decrypt data during a request. When you proxy a request through Relay, the encrypted card holder data is decrypted, allowing the request to be processed as normal when it reaches the third-party. This means the encrypted card data you collect reaches the PSP as plaintext data, without you ever handling the raw form.

![An illustration of a credit card being decrypted by a relay.]()

### Create a Relay

To simplify this example, we'll use [PutsReq](https://putsreq/) to simulate a third-party API. PutsReq provides a temporary endpoint that we can send requests to, however, in practice, this would be an endpoint from a third-party service.

To create a relay for your PutsReq endpoint, navigate to the **Relays** tab in the [Evervault Dashboard](https://app.evervault.com), click **Create Relay**, and add the PutsReq endpoint to the destination field.

![Screenshot of the Relay creation form in the Evervault Dashboard.]()

### Configure the relay

By default, relays are just transparent proxies that don't decrypt any data. They can be configured to perform encrypt and decrypt operations on request or response. For this example, the relay needs to decrypt encrypted card data on request as it's proxied to the third-party endpoint.

In the Dashboard, click the **Add Route** button to configure a new route. We want to decrypt any data being sent to this endpoint, so we can enter `/**` in the path field to match all requests sent to the relay.

Next, we can add a request action to decrypt any encrypted data in the request body. Select **Add Request Action** -> **Decrypt** -> **JSON**, and enter `$..*` in the fields to decrypt. This configures the relay to match any encrypted JSON fields in the request body and decrypts them.

You can learn more about field selection in the [Relay documentation](/relay#selecting-fields).

![Screenshot of a Relay configured to decrypt all data in the request body.]()

### Integrate the relay

Finally, we can implement our API endpoint to pass encrypted card data to the third-party payment processor using the relay you just created. When requests are sent, card data is automaitcally detected and decrypted before it reaches the third-party API.

#### Node

```javascript
// Replace with Relay URL
const relayURL = "<PUTSREQ_RELAY_URL>";

// POST /checkout
router.post("/checkout", async (req, res) => {
  try {
    const response = await fetch(relayURL, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Evervault-App-Id": "<APP_ID>",
        "X-Evervault-Api-Key": "<API_KEY>",
      },
      body: JSON.stringify({
        number: req.body.number,
        expiry: req.body.expiry,
        cvc: req.body.cvc,
      }),
    });

    const data = await response.json();
    res.json(data);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});
```

#### Python

```python
# Replace with Relay URL
relay_url = "<PUTSREQ_RELAY_URL>"

# Route handler for /checkout
@app.route("/checkout", methods=["POST"])
def checkout():
    try:
        payload = {
            "number": request.json["number"],
            "expiry": request.json["expiry"],
            "cvc": request.json["cvc"]
        }

        headers = {
            "Content-Type": "application/json",
            "X-Evervault-App-Id": "<APP_ID>",
            "X-Evervault-Api-Key": "<API_KEY>"
        }

        response = requests.post(relay_url, json=payload, headers=headers)
        return jsonify(response.json())
    except Exception as e:
        return jsonify({"error": str(e)}), 500
```

#### Ruby

```ruby
# Replace with Relay URL
relay_url = "<PUTSREQ_RELAY_URL>"

# POST /checkout
post "/checkout" do
  begin
    payload = {
      number: params[:number],
      expiry: params[:expiry],
      cvc: params[:cvc]
    }

    headers = {
      "Content-Type" => "application/json",
      "X-Evervault-App-Id" => "<APP_ID>",
      "X-Evervault-Api-Key" => "<API_KEY>"
    }

    response = HTTParty.post(
      relay_url,
      body: payload.to_json,
      headers: headers
    )

    content_type :json
    response.body
  rescue => e
    status 500
    { error: e.message }.to_json
  end
end
```

#### Java

```java
// Replace with Relay URL
private static final String RELAY_URL = "<PUTSREQ_RELAY_URL>";

@PostMapping("/checkout")
public ResponseEntity<?> checkout(@RequestBody Map<String, String> requestBody) {
    try {
        // Create request payload
        Map<String, String> payload = new HashMap<>();
        payload.put("number", requestBody.get("number"));
        payload.put("expiry", requestBody.get("expiry"));
        payload.put("cvc", requestBody.get("cvc"));

        // Set up HTTP client
        HttpClient client = HttpClient.newHttpClient();

        // Create request with headers
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(RELAY_URL))
            .header("Content-Type", "application/json")
            .header("X-Evervault-App-Id", "<APP_ID>")
            .header("X-Evervault-Api-Key", "<API_KEY>")
            .POST(HttpRequest.BodyPublishers.ofString(new ObjectMapper().writeValueAsString(payload)))
            .build();

        // Send request and get response
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        return ResponseEntity.ok(response.body());
    } catch (Exception e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body(Map.of("error", e.getMessage()));
    }
}
```

#### PHP

```php
// Replace with Relay URL
$relayUrl = "<PUTSREQ_RELAY_URL>";

// POST /checkout
app->post('/checkout', function ($request, $response) use ($relayUrl) {
    try {
        $data = $request->getParsedBody();

        $payload = json_encode([
            'number' => $data['number'],
            'expiry' => $data['expiry'],
            'cvc' => $data['cvc']
        ]);

        $ch = curl_init($relayUrl);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'X-Evervault-App-Id: <APP_ID>',
            'X-Evervault-Api-Key: <API_KEY>'
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $result = curl_exec($ch);
        curl_close($ch);

        $response->getBody()->write($result);
        return $response->withHeader('Content-Type', 'application/json');
    } catch (Exception $e) {
        $response->getBody()->write(json_encode(['error' => $e->getMessage()]));
        return $response->withStatus(500)->withHeader('Content-Type', 'application/json');
    }
});
```

#### Go

```go
// Replace with Relay URL
var relayURL = "<PUTSREQ_RELAY_URL>"

// POST /checkout
func checkoutHandler(w http.ResponseWriter, r *http.Request) {
	var requestBody map[string]string
	if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	payload := map[string]string{
		"number": requestBody["number"],
		"expiry": requestBody["expiry"],
		"cvc":    requestBody["cvc"],
	}

	payloadBytes, err := json.Marshal(payload)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	req, err := http.NewRequest("POST", relayURL, bytes.NewBuffer(payloadBytes))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Evervault-App-Id", "<APP_ID>")
	req.Header.Set("X-Evervault-Api-Key", "<API_KEY>")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(resp.StatusCode)
	io.Copy(w, resp.Body)
}
```

> **Relay authentication**
>
> Notice we are sending the `X-Evervault-App-Id` and `X-Evervault-Api-Key`
>   headers to the Relay. These headers are used to authenticate the request to
>   the Relay. We recommend enabling [Relay
>   authentication](/relay#authentication) when sending requests to third-party
>   APIs.

## Customization

The Card component can be fully customized to match the design of your application. By default, the Card component has no styling applied. You can use one of our prebuilt themes to get up and running or build your own theme from scratch.

### Prebuilt themes

The SDK has three themes to get you started: `clean`, `minimal`, and `material`.

#### Browser

```javascript
evervault.card({
  theme: evervault.ui.themes.clean(),
});
```

#### React

```jsx
import { Card, themes } from "@evervault/react";

function Checkout() {
  return <Card theme={themes.clean()} />;
}
```

### Custom themes

A theme is just an object with a `styles` property. The `styles` property uses a CSS-as-JS format to define CSS rules for the component. These rules are compiled to CSS and injected into the iframe.

<CornerFrame>
  <CardTheme theme="custom" />
</CornerFrame>

```javascript
const theme = {
  styles: {
    ":root": {
      "color-scheme": "light",
    },
    label: {
      fontSize: 11,
      fontWeight: 500,
      textTransform: "uppercase",
    },
    ".error": {
      color: "red",
      fontSize: 12,
      marginTop: 5,
    },
    ".field input": {
      marginTop: 5,
      padding: "8px 12px",
      border: "2px solid #dddddd",
    },
    ".field:focus-within input": {
      borderColor: "#6633ee",
    },
  },
};
```

You can pass a custom theme as an argument to any of the prebuilt themes to extend them.

```javascript
evervault.ui.themes.clean({
  styles: {
    label: {
      color: "#6633ee",
    },
  },
});
```

#### Element attributes

Although you can customize the CSS inside of the iframe, you can't modify the HTML. We know that layout can have a big impact on style definitions and so to help with this, we apply custom attributes to various elements within the iframe. These attributes are prefixed with `ev-`.

You can see our premade [UI component themes](https://github.com/evervault/evervault-js/tree/master/packages/themes) for an example of how these attributes can be used when creating themes.

**fieldset attributes**

The following attributes are added to the `<fieldset />` tag that wraps the entire component.

- `ev-component` `Card` — The name of the component.
- `ev-valid` `true | false` — Whether or not all of the fields within the component are valid.

**.field Attributes**

Each field in the component is wrapped in a <div /> with a '.field' class and the following attributes.

- `ev-name` `name | number | expiry | cvc` — The name for the individual field within the component.
- `ev-valid` `true | false` — Whether or not the field is valid.
- `ev-has-value` `true | false` — Whether or not the input has a value.

### Custom fonts

You can load additional fonts from Google Fonts by providing a `fonts` array in the theme definition. Currently, we only support custom fonts via Google Fonts.

```javascript
const theme = {
  fonts: ["https://fonts.googleapis.com/css2?family=Comic+Neue"],
  styles: {
    input: {
      fontFamily: "'Comic Neue'",
    },
  },
};
```

### Responsive styling

You can define media queries inside of the themes `styles` object, however, this may lead to unexpected behaviour as the media queries are matched against the iframe document, not the parent document. To get around this, we provide a `media` utility which allows you to define styles based on media queries that match the parent document.

To access the media utility, you need to define your theme as a function that returns a theme object. This function is passed a utilities object as an argument, which contains the `media` utility. The media utility should be spread into the styles object of the returned theme.

```javascript
const theme = (utilities) => {
  return {
    styles: {
      label: {
        fontSize: 16,
      },
      ...utilities.media("(max-width: 400px)", {
        label: {
          fontSize: 20,
        },
      }),
    },
  };
};
```

### Custom card brands

By default, the card component recognizes a fixed set of card networks but you can extend it with your own brands using `evervault.brands.create`. This creates a brand which you can then pass into the card component with the `customBrands` option.

#### Browser

```javascript
const acmeBrand = evervault.brands.create("acme-card", {
  numberValidationRules: {
    ranges: [[88000, 88999]],
    lengths: [16],
  },
  securityCodeValidationRules: {
    lengths: [3],
  },
  iconSrc: "https://your-cdn.com/acme-icon.svg",
});

const card = evervault.ui.card({
  customBrands: [acmeBrand],
});

card.on("change", (data) => {
  // "acme-card" appears in data.card.localBrands when a matching number is entered
  console.log(data.card.localBrands);
});
```

#### React

```jsx
import { useMemo } from "react";
import { Card, useEvervault, BrandOptions } from "@evervault/react";

const acmeBrand: BrandOptions = {
  numberValidationRules: {
    luhnCheck: true,
    ranges: [9900, [88000, 88999]],
    lengths: [16],
  },
  securityCodeValidationRules: {
    lengths: [4],
  },
  iconSrc: "https://example.com/logo.png",
};

export function Checkout() {
  const evervault = useEvervault();

  const customBrands = useMemo(() => {
    if (!evervault) return;

    return [evervault.brands.create("acme-card", acmeBrand)];
  }, [evervault]);

  return (
    <Card
      customBrands={customBrands}
      onChange={(data) => {
        // "acme-card" appears in data.card.localBrands when a matching number is entered
        console.log(data.card.localBrands);
      }}
    />
  );
}
```

The `ranges` argument accepts a BIN prefix (e.g., `9900`) or an inclusive range of prefixes (e.g., `[88000, 88999]`). The card component always accepts custom brands, even if `acceptedBrands` is set to restrict standard networks.

#### Swift

## Customization

Internally, the `PaymentCardInput` view uses SwiftUI `TextField`. These can be customized using SwiftUI modifiers like any other `TextField` in your application:

```swift
PaymentCardInput(cardData: $cardData)
    .font(.footnote)
    .foregroundColor(.blue)
```

### Built-in styles

To provide more customization options, the `PaymentCardInput` can be styled using a `PaymentCardInputStyle`. There are two built-in styles:

#### Inline

![Inline card input]()

The inline style renders the credit card number, expiry and cvc fields all on a single row.

```swift
PaymentCardInput(cardData: $cardData)
  .paymentCardInputStyle(.inline)
```

#### Rows

![Rows card input]()

The rows style renders the credit card number on a single line with the expiry and cvc fields next to each other below.

```swift
PaymentCardInput(cardData: $cardData)
  .paymentCardInputStyle(.rows)
```

### Custom styles

If these two styles do not match your use case, you can create your own style:

```swift
struct CustomPaymentCardInputStyle: PaymentCardInputStyle {
    func makeBody(configuration: Configuration) -> some View {
        VStack(alignment: .center) {
            configuration.cardImage

            Text("CC Number").font(.title3)
            configuration.cardNumberField

            Divider()

            Text("Expiry").font(.title3)
            configuration.expiryField

            Divider()

            Text("CVC").font(.title3)
            configuration.cvcField
        }
    }
}
```

#### Android

## Customization

The `PaymentCard` and its components can be customized to fit your application's design. The view accepts a number of parameters that enable you to customize the modifier, text, and placeholder styles. The content parameter accepts a `@Composable PaymentCardInputScope.()` function, which can be used to customize the view's layout. The `PaymentCardInputScope` components can be customized with a modifier (Modifier), label (String or `@Composable`), placeholder (String or `@Composable`), text style (TextStyle), and input field colors (TextFieldColors).

### Prebuilt themes

The SDK has two prebuilt styles to get you started.

#### Inline

![Inline card input]()

The inline style renders the credit card number, expiry, and cvc fields on a single row.

```kotlin
@Composable
fun InlinePaymentCard(
    modifier: Modifier = Modifier,
    textStyle: TextStyle = TextStyle.Default,
    placeholderTexts: PlaceholderTexts = PlaceholderDefaults.texts(),
    placeholderTextStyle: TextStyle = textStyle.copy(color = MaterialTheme.colorScheme.secondary),
    onDataChange: (PaymentCardData) -> Unit = {}
)
```

#### Rows

![Rows card input]()

The rows style renders the credit card number on a single line with the expiry and cvc fields next to each other below.

```kotlin
@Composable
fun RowsPaymentCard(
    modifier: Modifier = Modifier,
    textStyle: TextStyle = TextStyle.Default,
    placeholderTexts: PlaceholderTexts = PlaceholderDefaults.texts(),
    placeholderTextStyle: TextStyle = textStyle.copy(color = MaterialTheme.colorScheme.secondary),
    onDataChange: (PaymentCardData) -> Unit = {}
)
```

### Custom styles

If these two layouts do not fit your application's design, you can create your own layout by passing a `@Composable` function to the content parameter. The `@Composable` function receives a `PaymentCardInputScope` (together with the `PaymentCard` Modifier) object, which contains the `CardImage`, `CardNumberField`, `ExpiryField` and `CVCField` fields. You can use these fields to create your own layout.

```kotlin
UserParentLayout {
    val onDataChange: (PaymentCardData) -> Unit = {} // Handle card data
    // ...
    PaymentCard(onDataChange = onDataChange) { modifier ->
        Column(
            modifier = modifier
                .border(BorderStroke(1.dp, Color.LightGray), RoundedCornerShape(8.dp))
                .padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.spacedBy(8.dp),
        ) {
            CardImage()

            CardNumberField(
                modifier = Modifier.fillMaxWidth(),
                label = {
                    Text(
                        text = LabelTextsDefaults.CreditCardText,
                        color = Color.Blue
                    )
                },
                placeholder = {
                    Text(
                        text = PlaceholderTextsDefaults.CreditCardText,
                        color = Color(0x75757575),
                        fontSize = 12.sp
                    )
                },
                textStyle = MaterialTheme.typography.titleLarge,
                textFieldColors = customTextFieldColors()
            )

            ExpiryField(
                modifier = Modifier.fillMaxWidth(),
                label = {
                    Text(
                        text = LabelTextsDefaults.ExpirationDateText,
                        color = Color.Blue
                    )
                },
                placeholder = {
                    Text(
                        text = PlaceholderTextsDefaults.ExpirationDateText,
                        color = Color(0x9E9E9E9E),
                        fontSize = 10.sp
                    )
                },
                textStyle = MaterialTheme.typography.titleMedium,
                textFieldColors = customTextFieldColors()
            )

            CVCField(
                modifier = Modifier.fillMaxWidth(),
                label = {
                    Text(
                        text = LabelTextsDefaults.CvcText,
                        color = Color.Blue
                    )
                },
                placeholder = {
                    Text(
                        text = PlaceholderTextsDefaults.CvcText,
                        color = Color(0xBDBDBDBD),
                        fontSize = 8.sp
                    )
                },
                textStyle = MaterialTheme.typography.bodyMedium,
                textFieldColors = customTextFieldColors()
            )
        }
    }
}

@Composable
private fun customTextFieldColors(): TextFieldColors = TextFieldDefaults.colors(
    focusedIndicatorColor = Color.Transparent,
    unfocusedIndicatorColor = Color.Transparent,
    disabledIndicatorColor = Color.Transparent,
    focusedContainerColor = Color.Transparent,
    unfocusedContainerColor = Color.Transparent,
    disabledContainerColor = Color.Transparent,
)
```

- [3D Secure](/cards/3d-secure): Learn how to use 3D Secure to authenticate your customers and reduce fraud.
- [Network Tokens](/cards/network-tokens): Learn how to use Network Tokens to protect your customers' cards and improve authorization rates.

## Other collection methods

For card collection, we generally recommend using our UI components, but if those don't work for your use case, you can also collect card information server to server and with our SDKs (each SDK has an `encrypt` method). These options can impact your compliance scope depending on what you implement. If the flow you build exposes you to plaintext card data, it can increase your PCI DSS scope.

For server to server, create a relay that encrypts data for inbound requests to your server. The external server making the call needs to send the request to your relay, which encrypts the sensitive data and then forwards the request to your server. For client side encryption with the SDK, use the `encrypt` method.

