# Credit Card and 3D Secure

> Tokenize credit card details and handle 3D Secure verification using the Bolt React Native SDK for Embeddable Checkout.

Source: https://help.boltapp.com/developers/sdks/react-native/credit-card

## Credit Card Payment [#credit-card-payment]

This guide covers credit card tokenization and 3D Secure for [Bolt Embeddable Checkout](/products/embeddable-checkout). For the full backend API, see the [Embeddable Checkout API Reference](https://help.boltapp.com/api-embedded/).

The Bolt React Native SDK tokenizes card data securely in a WebView loaded from `connect.boltapp.com`. Card details never touch your app, keeping your integration PCI compliant.

### Basic Flow [#basic-flow]

```typescript title="TypeScript"
import { CreditCard, useThreeDSecure } from '@boltpay/react-native/payments';

function CheckoutScreen() {
  const cc = CreditCard.useController();
  const threeDSecure = useThreeDSecure();

  // Listen for field events
  cc.on('valid', () => setCanSubmit(true));
  cc.on('error', (msg) => setFieldError(msg));

  const handlePayment = async () => {
    // 1. Tokenize: returns TokenResult | Error (never throws)
    const result = await cc.tokenize();
    if (result instanceof Error) {
      console.error(result.message);
      return;
    }
    // result: { token?, last4?, bin?, network?, expiration?, postal_code? }

    // 2. Fetch 3DS reference ID: throws ThreeDSError on failure
    const referenceID = await threeDSecure.fetchReferenceID({
      token: result.token,
      bin: result.bin,
      last4: result.last4,
    });

    // 3. Send token to your backend to create the payment
    const paymentResponse = await yourApi.createPayment(result);

    // 4. Handle 3DS challenge if required: returns ThreeDSResult (never throws)
    if (paymentResponse['.tag'] === 'three_ds_required') {
      const challengeResult = await threeDSecure.challengeWithConfig(
        paymentResponse.id,
        {
          referenceID,
          jwtPayload: paymentResponse.jwt_payload,
          stepUpUrl: paymentResponse.step_up_url,
        }
      );
      if (!challengeResult.success) {
        console.error(challengeResult.error?.message);
      }
    }
  };

  return (
    <>
      <CreditCard.Component controller={cc} />
      <threeDSecure.Component />
      <Button onPress={handlePayment} title="Pay" />
    </>
  );
}
```

### Step-by-Step Breakdown [#step-by-step-breakdown]

1. **Create a controller**: `CreditCard.useController()` returns a controller for the secure card input fields.
2. **Listen for events**: Use `cc.on()` to react to `valid`, `error`, `blur`, and `focus` events on the card fields.
3. **Tokenize**: Call `cc.tokenize()` to securely tokenize the card. This returns a `TokenResult` or an `Error` (it never throws).
4. **Fetch 3DS reference**: Pass the token result to `threeDSecure.fetchReferenceID()` to get a reference ID for 3D Secure verification.
5. **Create payment**: Send the token to your backend to initiate the payment via the Bolt API.
6. **Handle 3DS challenge**: If the payment response indicates `three_ds_required`, call `threeDSecure.challengeWithConfig()` to present the challenge UI.

### Credit Card Controller Methods [#credit-card-controller-methods]

| Method                | Description                                                           |
| --------------------- | --------------------------------------------------------------------- |
| `tokenize()`          | Returns `Promise<TokenResult \| Error>`. Never throws.                |
| `on(event, callback)` | Register an event listener. Events: `valid`, `error`, `blur`, `focus` |
| `setStyles(styles)`   | Update input field styles after creation                              |

## 3D Secure with a Stored Card [#3d-secure-with-a-stored-card]

If you've already added a card via [Bolt's Add Card API](https://help.boltapp.com/api-embedded/) and have a `creditCardID`, you can perform 3D Secure verification without re-tokenizing:

```typescript title="TypeScript"
import { useThreeDSecure } from '@boltpay/react-native/payments';

function StoredCardPayment() {
  const threeDSecure = useThreeDSecure();

  const handlePayment = async (creditCardId: string, expiration: string) => {
    // Fetch 3DS reference using stored card ID
    const referenceID = await threeDSecure.fetchReferenceID({
      id: creditCardId,
      expiration,
    });

    // Create payment on your backend with the 3DS reference
    const paymentResponse = await yourApi.createPayment({
      creditCardId,
      referenceID,
    });

    // Handle 3DS challenge if required
    if (paymentResponse['.tag'] === 'three_ds_required') {
      const result = await threeDSecure.challengeWithConfig(
        paymentResponse.id,
        {
          referenceID,
          jwtPayload: paymentResponse.jwt_payload,
          stepUpUrl: paymentResponse.step_up_url,
        }
      );
      if (!result.success) {
        console.error(result.error?.message);
      }
    }
  };

  return <threeDSecure.Component />;
}
```

## 3D Secure Methods [#3d-secure-methods]

| Method                                    | Description                                                                                             |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `fetchReferenceID(creditCardInfo)`        | Accepts a `TokenResult` or `CreditCardId`. Returns `Promise<string>`. Throws `ThreeDSError` on failure. |
| `challengeWithConfig(orderToken, config)` | Presents the 3DS challenge UI. Returns `Promise<ThreeDSResult>`. Never throws: check `result.success`.  |

## 3D Secure Error Codes [#3d-secure-error-codes]

| Code | Description                                      |
| ---- | ------------------------------------------------ |
| 1001 | Credit card id or token must be supplied         |
| 1002 | Credit card id and token cannot both be supplied |
| 1003 | Malformed credit card token                      |
| 1004 | Order token does not exist                       |
| 1005 | API response error during verification           |
| 1006 | Verification not required                        |
| 1007 | Setup error during verification                  |
| 1008 | Authentication failed                            |
| 1009 | Failed to create challenge or challenge failed   |
| 1010 | Failed to get device data collection jwt         |
