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

# Tokenize card using TabaPay widget

> Instructions for how to use the TabaPay widget, and how to migrate to Synctera widget

# Overview

As of November 2025, Synctera switched to an own widget for tokenizing external cards. This widget is described [here](/v2/docs/link-external-cards).
For instructions on how to migrate to the Synctera widget, see [here](#-migration-guide:-tabapay-to-synctera-widget).

# Tokenize card

To enable customers to add card-on-file, you first need to tokenize the card through integration with the TabaPay Cards Tokenization widget. The widget allows for direct communication between the client, TabaPay, and Synctera, eliminating the need for the integrator to have PCI certification. This integration is described step-by-step below.

## Getting started

Before integrating with TabaPay Card PCI-Compliant iFrame, ensure that you have all the necessary components in place:

1. Ensure Synctera API keys are provisioned and working for your business;
2. Ensure external cards have been enabled for your program with your Synctera implementation and onboarding contact;
3. If integrating in a mobile application, you may need to use a web view such as a [WKWebView in a iOS application](https://developer.apple.com/documentation/webkit/wkwebview), [WebView in an android application](https://developer.android.com/guide/webapps/webview), or a [WebView in React Native](https://github.com/react-native-webview/react-native-webview);
4. Content security policies should be set in the application using the following directives:

```js theme={"system"}
{
  "connect-src": [
    'https://sso.tabapay.com',
    'https://sso.sandbox.tabapay.com:8443'
  ],
  "frame-src": [
    'https://sso.tabapay.com',
    'https://sso.sandbox.tabapay.com:8443'
  ]
}
```

## Display iFrame and tokenize an External Card

The steps below describe how to display TabaPay PCI-Compliant iFrame:

1. Add an iFrame tag to the desired webpage:

```html theme={"system"}
<div><iframe id="sso"></iframe></div>
```

2. Set the iFrame source for the appropriate environment:

* prod: "[https://sso.tabapay.com/SSOSynctera.html](https://sso.tabapay.com/SSOSynctera.html)"
* sandbox: "[https://sso.sandbox.tabapay.com:8443/SSOSynctera.html](https://sso.sandbox.tabapay.com:8443/SSOSynctera.html)"

```html theme={"system"}
<script>
  document.getElementById( "sso" ).src = "https://sso.sandbox.tabapay.com:8443/SSOSynctera.html";
</script>
```

3. Add an event listener and handler for TabaPay events:

```html theme={"system"}
<script>
  function pfReceivedMessage( event )
  {
    if ( event.data != "Close" )
    {
      if ( event.data.slice( 0, 7 ) == "Error: " )
      {
        // Error
      }
      else
      {
        var asData = event.data.split( "|" );
        if ( asData.length == 4 )
        {
          // asData[ 0 ] contains the Cardholder Name
          // asData[ 1 ] contains the Last 4
          // asData[ 2 ] contains the Expiration Date in YYYYMM Format
          // asData[ 3 ] contains the Card Token
        }
        else
        {
          // Data Error
        }
      }
    }
    else
    {
      // Close or Cancel
    }
  }
  window.addEventListener( "message", pfReceivedMessage, false);
</script>
```

Upon completing the preceding steps, the widget should be ready to display and capable of tokenizing External Cards.

> 💡
>
> For more information on card tokenization using TabaPay PCI-Compliant iFrame, refer to the [TabaPay documentation](https://developers.tabapay.com/docs/pci-compliant-iframe).

## Customization Options

The [TabaPay Browser SDK](https://developers.tabapay.com/reference/browser-sdk) provides a number of customization options for controlling the layout and appearance of the TabaPay Card PCI-Compliant iFrame. See [TabaPay Browser SDK](https://developers.tabapay.com/reference/browser-sdk) for additional details and specific customization options.

# Migration Guide: TabaPay to Synctera widget

This guide helps you migrate from the TabaPay Browser SDK widget to the Synctera External Card Creation Widget.

## Quick Comparison

| Feature             | TabaPay Widget                                 | Synctera Widget                               |
| ------------------- | ---------------------------------------------- | --------------------------------------------- |
| **Initialization**  | `tabaPaySdk.createIframe()`                    | `<external-card-creation>` web component      |
| **Script Loading**  | External TabaPay SDK script                    | External widget script                        |
| **Configuration**   | JavaScript object                              | HTML attributes                               |
| **DOM Integration** | Returns `{ element }` to append                | Direct web component usage                    |
| **Token Return**    | Pipe-separated string `"last4\|expiry\|token"` | Event detail with `{ token, card_data }`      |
| **Event Listeners** | Config object with functions                   | DOM event listeners (`success`/`failure`)     |
| **Styling**         | Inline config objects                          | Theme attribute + (CSS variables coming soon) |
| **Environment**     | `clientId` (sandbox/production)                | `env` attribute + `token`                     |

***

## Step-by-Step Migration

### Step 1: Update Script Tag

**Before (TabaPay):**

```html theme={"system"}
<script src="https://tabapay-sdk-url.com/script.js"></script>
```

**After (Synctera):**

```html theme={"system"}
<script 
  type="module" 
  src="https://widgets.synctera.com/external-card-creation/v1.0.0/index.js">
</script>
```

### Step 2: Replace Widget Initialization

**Before (TabaPay):**

```javascript theme={"system"}
const { element } = tabaPaySdk.createIframe({
  cardNumberInput: {},
  expirationDateInput: {},
  cscInput: {},
  buttons: {
    submit: { order: '1' },
    reset: { order: '2' },
    cancel: { order: '3' }
  },
  eventListeners: {
    focusChange: (focused) => {},
    submit: (encryptedCardData) => {},
    cancel: () => {}
  },
  clientId: "your-sandbox-client-id"
});

// Append to DOM
document.getElementById('target').appendChild(element);
```

**After (Synctera):**

```html theme={"system"}
<external-card-creation
  id="card-widget"
  token="your-widget-token"
  env="sandbox"
></external-card-creation>
```

### Step 3: Update Token Handling

**Before (TabaPay):**

```javascript theme={"system"}
// TabaPay returns: "card-last4|expiration-date|encrypted-card-token"
eventListeners: {
  submit: (encryptedCardData) => {
    // Extract only the token part (last segment after pipe)
    const token = encryptedCardData.split('|')[2];
    console.log('Token:', token);
    // Use token for API calls
  }
}
```

**After (Synctera):**

```javascript theme={"system"}
// Synctera returns: { token, card_data } in event.detail
document.getElementById('card-widget').addEventListener('success', (event) => {
  const { token, card_data } = event.detail;
  console.log('Token:', token);
  // token is ready to use - no parsing needed!
  // Use token for API calls
});
```

### Step 4: Update Event Listeners

**Before (TabaPay):**

```javascript theme={"system"}
eventListeners: {
  focusChange: (focused) => {
    console.log('Focus changed to:', focused);
  },
  submit: (encryptedCardData) => {
    handleSubmit(encryptedCardData);
  },
  cancel: () => {
    handleCancel();
  }
}
```

**After (Synctera):**

```javascript theme={"system"}
const widget = document.getElementById('card-widget');

// Note: focusChange is not available, but you can listen to input events if needed
widget.addEventListener('success', (event) => {
  handleSubmit(event.detail.token);
});

widget.addEventListener('failure', (event) => {
  handleError(event.detail.error);
});

// Clear functionality: Built-in Clear button automatically clears all fields
// Cancel functionality: Can be handled with your own cancel button if needed
```

***

## Feature Mapping

### Basic Configuration

| TabaPay                               | Synctera Widget                                   |
| ------------------------------------- | ------------------------------------------------- |
| `cardNumberInput.separator`           | theme options (css coming soon)                   |
| `expirationDateInput.placeholderText` | `custom-labels='{"cardExpPlaceholder": "mm/yy"}'` |
| `cscInput.labelText`                  | `custom-labels='{"securityCodeLabel": "CVV"}'`    |

### Styling

| TabaPay                      | Synctera Widget                                   |
| ---------------------------- | ------------------------------------------------- |
| `labelStyle.fontWeight`      | CSS styling or theme customization                |
| `inputStyle.backgroundColor` | `theme="night-shift"` or (custom CSS coming soon) |
| `inputStyle.borderStyle`     | Theme-based or (custom CSS coming soon)           |
| `invalidStyle.textColor`     | Theme-based or (custom CSS coming soon)           |

### Buttons

| TabaPay                                 | Synctera Widget                               |
| --------------------------------------- | --------------------------------------------- |
| `buttons.submit.backgroundColorEnabled` | Theme-based styling                           |
| `buttons.submit.backgroundColorHover`   | Theme-based styling                           |
| `buttons.reset`                         | Built-in **Clear** button (clears all fields) |
| `buttons.cancel`                        | Not included by default                       |

***

## Complete Migration Example

### Before: TabaPay Implementation

```html theme={"system"}
<!DOCTYPE html>
<html>
<head>
  <title>TabaPay Widget</title>
  <script src="https://tabapay-sdk-url.com/script.js"></script>
</head>
<body>
  <div id="target"></div>
  <pre id="result"></pre>

  <script>
    const preEl = document.getElementById('result');
    
    const { element } = tabaPaySdk.createIframe({
      cardNumberInput: {
        separator: "-",
      },
      expirationDateInput: {
        placeholderText: "mm/yy"
      },
      cscInput: {
        labelText: "CVV"
      },
      buttons: {
        submit: {
          backgroundColorEnabled: "#88f",
          backgroundColorHover: "#aaf",
          order: '1',
        },
        reset: {
          backgroundColorEnabled: "#3f3",
          backgroundColorHover: "#9f9",
          order: '2',
        },
        cancel: {
          backgroundColorEnabled: "#f55",
          backgroundColorHover: "#f88",
          order: '3',
        },
      },
      labelStyle: {
        fontWeight: "300",
      },
      inputStyle: {
        backgroundColor: "#ddd",
        borderStyle: "1.5px solid",
        borderColor: "#000"
      },
      invalidStyle: {
        textColor: "#000",
      },
      eventListeners: {
        focusChange: (focused) => {
          preEl.firstChild.nodeValue = 'focus: ' + String(focused);
        },
        submit: (encryptedCardData) => {
          // Extract token (last part after pipes)
          const token = encryptedCardData.split('|')[2];
          preEl.firstChild.nodeValue = token ?? ' ';
          // Use token for API call
          createTransaction(token);
        },
        cancel: () => {
          preEl.firstChild.nodeValue = 'cancel';
        },
      },
      clientId: "your-sandbox-client-id",
    });

    document.getElementById('target').appendChild(element);

    async function createTransaction(token) {
      // Make API call with token
      const response = await fetch('/api/create-transaction', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ card_token: token })
      });
      // Handle response
    }
  </script>
</body>
</html>
```

### After: Synctera Widget Implementation

```html theme={"system"}
<!DOCTYPE html>
<html>
<head>
  <title>Synctera Widget</title>
  <script 
    type="module" 
    src="https://widgets.synctera.com/external-card-creation/v1.0.0/index.js">
  </script>
</head>
<body>
  <div id="target">
    <external-card-creation
      id="card-widget"
      token="your-widget-token"
      env="sandbox"
      theme="night-shift"
      custom-labels='{
        "cardExpPlaceholder": "mm/yy",
        "securityCodeLabel": "CVV"
      }'
    ></external-card-creation>
  </div>
  <pre id="result"></pre>

  <script>
    const preEl = document.getElementById('result');
    const widget = document.getElementById('card-widget');

    // Get widget token (you'll need to fetch this from your backend)
    async function initializeWidget() {
      try {
        const response = await fetch('/api/widget-token', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ customer_id: 'customer-id' })
        });
        const { widget_token } = await response.json();
        widget.setAttribute('token', widget_token);
      } catch (error) {
        console.error('Failed to get widget token:', error);
      }
    }

    // Event listeners
    widget.addEventListener('success', (event) => {
      const { token, card_data } = event.detail;
      preEl.textContent = token ?? ' ';
      
      // Token is ready to use - no parsing needed!
      createTransaction(token);
    });

    widget.addEventListener('failure', (event) => {
      const { error } = event.detail;
      preEl.textContent = 'Error: ' + error;
      console.error('Tokenization failed:', error);
    });

    async function createTransaction(token) {
      // Make API call with token
      const response = await fetch('/api/create-transaction', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ card_token: token })
      });
      // Handle response
    }

    // Initialize on page load
    window.addEventListener('DOMContentLoaded', initializeWidget);
  </script>
</body>
</html>
```

***

## Key Differences to Remember

### 1. **Token Format**

* **TabaPay**: Returns pipe-separated string `"last4|expiry|token"` - you must extract the token
* **Synctera**: Returns structured object `{ token, card_data }` - token is ready to use

### 2. **Widget Token vs Client ID**

* **TabaPay**: Uses `clientId` directly in config
* **Synctera**: Requires fetching a `widget_token` from your backend API first, then passing it to the widget

### 3. **DOM Integration**

* **TabaPay**: Returns an element you must append to DOM
* **Synctera**: Web component is declaratively added to HTML

### 4. **Event Handling**

* **TabaPay**: Config object with function callbacks
* **Synctera**: Standard DOM event listeners (`addEventListener`)

### 5. **Styling**

* **TabaPay**: Inline configuration objects
* **Synctera**: Can swap themes to get closest to the look you want (custom css style coming soon)

### 6. **Buttons**

* **TabaPay**: Includes submit, reset, and cancel buttons
* **Synctera**: Includes **submit** and **clear** buttons (clear button clears all fields; cancel can be added via custom code)

***

## Migration Checklist

* [ ] Update script tag to load Synctera widget
* [ ] Replace `tabaPaySdk.createIframe()` with `<external-card-creation>` component
* [ ] Update backend to provide widget tokens (instead of using clientId directly)
* [ ] Replace event listener config with DOM event listeners
* [ ] Update token extraction logic (remove pipe-splitting, use `event.detail.token`)
* [ ] Map styling preferences to theme (custom CSS still to come)
* [ ] Test in sandbox environment
* [ ] Update API calls to use new token format
* [ ] Update reset button logic (use built-in Clear button; cancel can be added via custom code if needed)
* [ ] Test all payment flows

***

## Getting Your Widget Token

Unlike TabaPay's `clientId`, the Synctera widget requires a widget token from your backend:

```javascript theme={"system"}
// Backend endpoint example
POST /api/widget-token
Body: { customer_id: "customer-123" }

// Response
{
  "widget_token": "widget-token-abc123..."
}
```

Then pass this token to the widget:

```html theme={"system"}
<external-card-creation
  token="widget-token-abc123..."
  env="sandbox"
></external-card-creation>
```
