Skip to main content

Overview

As of November 2025, Synctera switched to an own widget for tokenizing external cards. This widget is described here. For instructions on how to migrate to the Synctera widget, see here.

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, WebView in an android application, or a WebView in React Native;
  4. Content security policies should be set in the application using the following directives:
{
  "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:
<div><iframe id="sso"></iframe></div>
  1. Set the iFrame source for the appropriate environment:
<script>
  document.getElementById( "sso" ).src = "https://sso.sandbox.tabapay.com:8443/SSOSynctera.html";
</script>
  1. Add an event listener and handler for TabaPay events:
<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.

Customization Options

The TabaPay 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 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

FeatureTabaPay WidgetSynctera Widget
InitializationtabaPaySdk.createIframe()<external-card-creation> web component
Script LoadingExternal TabaPay SDK scriptExternal widget script
ConfigurationJavaScript objectHTML attributes
DOM IntegrationReturns { element } to appendDirect web component usage
Token ReturnPipe-separated string "last4|expiry|token"Event detail with { token, card_data }
Event ListenersConfig object with functionsDOM event listeners (success/failure)
StylingInline config objectsTheme attribute + (CSS variables coming soon)
EnvironmentclientId (sandbox/production)env attribute + token

Step-by-Step Migration

Step 1: Update Script Tag

Before (TabaPay):
<script src="https://tabapay-sdk-url.com/script.js"></script>
After (Synctera):
<script 
  type="module" 
  src="https://widgets.synctera.com/external-card-creation/v1.0.0/index.js">
</script>

Step 2: Replace Widget Initialization

Before (TabaPay):
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):
<external-card-creation
  id="card-widget"
  token="your-widget-token"
  env="sandbox"
></external-card-creation>

Step 3: Update Token Handling

Before (TabaPay):
// 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):
// 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):
eventListeners: {
  focusChange: (focused) => {
    console.log('Focus changed to:', focused);
  },
  submit: (encryptedCardData) => {
    handleSubmit(encryptedCardData);
  },
  cancel: () => {
    handleCancel();
  }
}
After (Synctera):
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

TabaPaySynctera Widget
cardNumberInput.separatortheme options (css coming soon)
expirationDateInput.placeholderTextcustom-labels='{"cardExpPlaceholder": "mm/yy"}'
cscInput.labelTextcustom-labels='{"securityCodeLabel": "CVV"}'

Styling

TabaPaySynctera Widget
labelStyle.fontWeightCSS styling or theme customization
inputStyle.backgroundColortheme="night-shift" or (custom CSS coming soon)
inputStyle.borderStyleTheme-based or (custom CSS coming soon)
invalidStyle.textColorTheme-based or (custom CSS coming soon)

Buttons

TabaPaySynctera Widget
buttons.submit.backgroundColorEnabledTheme-based styling
buttons.submit.backgroundColorHoverTheme-based styling
buttons.resetBuilt-in Clear button (clears all fields)
buttons.cancelNot included by default

Complete Migration Example

Before: TabaPay Implementation

<!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

<!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:
// 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:
<external-card-creation
  token="widget-token-abc123..."
  env="sandbox"
></external-card-creation>