PCI Compliance

Access Rules for Sensitive Data

To improve data security and align with compliance standards, we have updated how sensitive card information can be accessed. Depending on your PCI compliance status, the available access method differs as follows:

  • Clients with a valid PCI DSS Level 1 Certification

    You can continue to use the Get Card Details API to retrieve full card details, including PAN, expiration date, and CVV2.

  • Clients without PCI DSS Level 1 Certification

    You cannot obtain sensitive card data through the endpoint. Instead, you must use the Widget.js iframe to securely display card information in your application or webpage.

    • The iframe URL must be pre-registered with Interlace.

    • The registered domain must exactly match the domain where the iframe is loaded. Otherwise, the card information will not be displayed.

    • Card data displayed via iframe cannot be exported in bulk (PAN, CVV2, expiration date, etc.). It can only be viewed individually on pages under the registered domain.


Display Sensitive Card Data Using Widget.js

Interlace provides a JavaScript library, Widget.js, that enables you to display sensitive card data in your application or webpage while minimizing your data security compliance burden.

Widget.js injects a set of configurable iframes into your HTML, allowing you to display a virtual card or PIN without handling sensitive card data on your servers. Interlace securely hosts the card data on compliant servers. You can present either a complete card—including the PAN, expiration date, and CVV2—or individual elements such as the cardholder’s PIN. Widget.js can also add buttons to your application or webpage that copy a card’s PAN, expiration date, or CVV2 to the clipboard.

You can view a live demo to see Widget.js in action:

Sandbox Environment Demo | Production Environment Demo

ℹ️

Note

While the Widget.js iframe feature reduces your data security compliance burden, it does not eliminate it entirely and is only appropriate for certain use cases.


At the end of this guide, you will understand:

  • How to integrate Widget.js into your HTML.
  • How to obtain a client access token.
  • How to inject the Widget.js iframes.
  • How to customize iframe styles for your site.

If you have questions, please contact the Interlace Technical Support team for assistance.


Concept

Data security compliance

Companies that store, transmit, or process sensitive card data, including the primary account number (PAN), card verification value (CVV2), expiration date must comply with the Payment Card Industry Data Security Standard (PCI DSS). Achieving PCI DSS certification is both time consuming and expensive.

Widget.js offloads some of the PCI compliance burden (for certain use cases) by enabling the encrypted transmission of sensitive card data. Interlace is fully PCI-Service Provider Level 1 compliant and handles the unencrypted sensitive card data for you. Your servers never store, transmit, or process the card data.

⚠️

Warning

Card data is less secure when copied to the clipboard than when left in this PCI-compliant widget. Cardholders should adopt data security best practices and take precautions to keep their sensitive data safe.

Dynamic card data iframes

The iframes injected by Widget.js enable you to control the styling and layout of the HTML pages you serve to client applications, while delegating secure handling of sensitive data to Interlace servers. They also provide the transparent button UI elements used for copying sensitive data to the clipboard. These elements are transparent so you can overlay them over your custom user interface to intercept click events. They require no customization. You create and style pages in whatever manner you desire, and Widget.js inserts the card data and button UI elements into the page locations specified in the HTML.

📘

Tip

To display virtual cards within a mobile application, you can embed the iframes using a webview.

Encryption and card data flow

The following process describes how Widget.js injects the iframes into your application. See the tutorials on this page for more details.

  1. Your application’s backend requests a client access token.
  2. The Interlace API creates a Base64-encoded client access token. This token enables access to the specified card and will expire after 5 minutes.
  3. Your backend passes the client access token to your JavaScript.
  4. Your JavaScript injects the client access token into the widget.bootstrap() method through a configuration object.
  5. Your JavaScript executes widget.bootstrap(), which initializes and configures Widget.js.
  6. Widget.js injects the card data and buttons into the HTML container:
  • If you are displaying card data, separate iframes are created for the card’s PAN, CVV2, and expiration date.
  • If you enable the "Copy to Clipboard" feature, buttons will also be added for each field.
  1. The HTML page displays the requested information.

The configuration object

The .bootstrap() method of Widget.js requires a configuration object, which defines the attributes and behaviors of the iframes. Nested under the configuration object is the component object, which accepts a showPan object. You must include the ID of the "div" element into which Widget.js injects the individual iframes of card data.

More information on the objects is given the section below.

The configuration object

Field

Description

configuration.clientAccessToken string Required

Client access token obtained from the Interlace API.

Allowable Values: Client access token

configuration.component.showPan object Required

Specifies the card data to display and applies typographical styling to the data.

Include the cardPan object to display the PAN, the cardCvv object to display the CVV2 number, and the cardExp object to display the expiration date.

Allowable Values: Any combination of cardPan, cardCvv, and cardExp objects

configuration.callbackEvents object Optional

Defines customizable event handlers that are executed upon success or failure of iframe rendering.

Allowable Values: Existing object

The showPan object

You must include the showPan object under the component object, which is itself nested under the configuration object. The showPan object specifies which card attributes to display. It can contain any of the following optional objects:

  • cardPan — The card’s PAN.
  • cardCvv — The card’s CVV2.
  • cardExp — The card’s expiration date.
  • copyCardPan — A transparent button used to copy the card’s PAN to the clipboard.
  • copyCardCvv — A transparent button used to copy the card’s CVV2 to the clipboard.
  • copyCardExp — A transparent button used to copy the card’s expiration date to the clipboard.
📘

Tip

If you include a copyCard* object, you should also include its associated card* object. For example, if you include the copyCardPan object in the showPan object, you should also include the cardPan object.

Any cardPan, cardCvv, or cardExpobjects included within the showPan object have their associated values injected as iframes within the div container you specified in the component object.

For each included object, you must specify the div element that contains the iframe.

You can also configure the appearance of the card data using the styles object within each of the cardPan, cardExp, and cardCvv objects. The copyCardPan, copyCardCvv, and copyCardExp objects do not contain a styles object or a span.

ℹ️

Note

Widget.js only supports web-safe/system fonts that can be displayed on modern web browsers without a specific download.

ObjectSupported SelectorsSupported Attributes
cardPanspan, span:hovercolor, font-family, font-size, background, font-weight, letter-spacing
cardCvvspan, span:hovercolor, font-family, font-size, background
cardExpspan, span:hovercolor, font-family, font-size, background, font-weight, letter-spacing

The showPan.cardPan and showPan.cardExp objects

Fields

Description

domId string Required

Associates the cardPan and cardExp elements with their corresponding div elements.

Allowable Values: Must match the DOM ID of the corresponding "div" element.

For example, cardPan.domId must match the ID attribute of the div element that will contain the card PAN iframe.

format boolean Optional

Set to true to format the element’s content.

The content of cardPan is formatted as: "XXXX XXXX XXXX XXXX"

The content of cardExp is formatted as: "XX/XX"

Allowable Values: true, false

Default value: false

styles object Optional

A CSS-like style object applied to the iframe holding the card data.

Allowable Values: Existing object

styles. object Optional

Replace \ with one of the supported selector values.

Supported selectors: span, span:hover

Supported CSS attributes: color, font-family, font-size, background, font-weight, letter-spacing

  • NOTE*: CSS importing schemes such as @import and @url are not supported. Widget.js only supports web-safe/system fonts that can be displayed on modern web browsers without a specific download.

Allowable Values: Your custom styles object

The showPan.cardCvv object

Fields

Description

domId string Required

Associates the cardCvv element with its corresponding div element.

Allowable Values: Must match the DOM ID of the corresponding "div" element. That is, cardCvv.domId must match the ID attribute of the div element that will contain the card CVV2 iframe.

styles object Optional

A CSS-like style object applied to the iframe holding the card data.

Allowable Values: CSS-like style object

styles. object Optional

Replace \ with one of the supported selector values.

Supported selectors: span, span:hover

Supported CSS attributes: color, font-family, font-size, background

  • NOTE*: CSS importing schemes such as @import and @url are not supported. Widget.js only supports web-safe/system fonts that can be displayed on modern web browsers without a specific download.

Allowable Values: Your custom styles object

The showPan.copyCardPan, showPan.copyCardCvv, and showPan.copyCardExp objects

Fields

Description

domId string Required

Associates the copyCardPan, copyCardCvv, and copyCardExp elements with their corresponding div elements.

Allowable Values: Must match the DOM ID of the corresponding div element. That is, copyCardPan.domId must match the ID attribute of the div element that will contain the card PAN iframe’s Copy to Clipboard button.

onCopySuccess event handler Optional

Called when the element’s text is successfully copied to the clipboard.

You can use this callback to update your user interface in response to a click.

Allowable Values: A function with no parameters

onCopyFailure event handler Optional

Called when an error occurred while attempting to copy the element’s text to the clipboard.

Allowable Values: A function with the error object as its parameter

The callbackEvents object

Fields

Description

onSuccess event handler Optional

Executed upon success of iframe rendering.

You can customize the method within this event handler to perform whatever action you want.

Allowable Values: A method

onFailure event handler Optional

Executed upon failure of iframe rendering.

You can customize the method within this event handler to perform whatever action you want.

Allowable Values: A method

The callbackEvents object specifies the methods executed upon the success and failure events when rendering the iframes.

Copying card data to the clipboard

You can add transparent iframes to your application or webpage that capture clicks to copy a card’s PAN, CVV2, or expiration date to the clipboard of the cardholder’s device. For example, in a POS financing scenario, your cardholders can click the buttons provided by the .copyToClipboard() method to avoid manually copying card data from a newly issued virtual card to the vendor’s application where they are making a purchase.

Each iframe is associated with a parent div element. Your application or webpage requires a separate parent div element for each card data element you want to copy to the clipboard.

The iframe containing the transparent Copy to Clipboard button is positioned precisely to the bounds of the parent div, so that it can intercept all click events. If your button is not responding to clicks, ensure that the iframe is positioned correctly. By handling the clicks within an iframe, your application avoids entering PCI scope.

You must supply your own visible button UI element inside each parent div that contains the card data to copy.

To respond to clicks within the parent div, you must specify a callback function for the onCopySuccess field. This event handler is triggered when data is successfully copied to the clipboard in response to a click event, enabling you to perform actions such as updating your application’s user interface or webpage.

The following HTML example displays a Copy PAN to Clipboard button as an icon:

<div id='pan-container'></div>
<div id='copy-pan-container' style="position: relative;">
  <img src="CopyPanIcon.jpg" alt="Copy PAN" id="copy-card-pan">
</div>

<script>
widget.bootstrap({
   "clientAccessToken": "**CLIENT_ACCESS_TOKEN**",
   "component": {
     "showPan": {
       "cardPan": {
         "domId": "card-pan",
         "format": true,
         "styles": styles.cardPan.styles // A custom styles object
       },
       "copyCardPan": {
         "domId": "copy-card-pan",
         "onCopySuccess": () => alert("Copied PAN!"),
         "onCopyFailure": error => console.error(error)
       }
     }
   },
   "callbackEvents": {
     "onSuccess": () => console.log("Success!"),
     "onFailure": error => console.error(error)
   }
 });
</script>

Resetting Widget.js

Widget.js also provides a .destroy() method. Run widget.destroy() to reset Widget.js and any existing configurations, by clearing all the listeners.

In some situations, you might prefer to call widget.bootstrap() instead of calling widget.destroy(). One example would be if you wanted to display the data from another card. If you use this approach, however, your client access token will expire. Don’t forget to obtain a new one-time client access token first, as explained in Step 6 of the card data tutorial.


Card data tutorial

This tutorial shows you how to display a virtual card’s sensitive data using iframes on your application or webpage. In this scenario, you will display a virtual card’s PAN, expiration date, and CVV2 on an otherwise blank webpage, enabling you to display a virtual card’s data to your customer. You will also add three buttons to facilitate copying card data to the clipboard.

ℹ️

Note

This tutorial assumes you have already worked with Interlace to enable Widget.js for your program. You cannot complete these steps in the public sandbox.

When you are finished, your HTML code should look like the following:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Widget.js Card Data Tutorial</title>
    <style type="text/css">
      #copy-pan-container:hover > img /* styles */
      #copy-exp-container:hover > img /* styles */
      #copy-cvv-container:hover > img /* styles */
    </style>
  </head>
  <body>
    <!-- div elements for displaying and copying PAN, expiration date, and CVV -->
    <div>
      <div id="card-pan"></div>
      <div id="card-exp"></div>
      <div id="card-cvv"></div>
      <div id="copy-pan-container" style="position: relative">
        <img src="copyPanIcon.jpg" alt="Copy card number" id="copy-card-pan" />
      </div>
      <div id="copy-exp-container" style="position: relative">
        <img src="copyExpIcon.jpg" alt="Copy expiration date" id="copy-card-exp" />
      </div>
      <div id="copy-cvv-container" style="position: relative">
        <img src="copyCvvIcon.jpg" alt="Copy CVV" id="copy-card-cvv" />
      </div>
    </div>

    <!-- Reference Widget.js -->
    <script src="https://static.univisioncard.com/api/sdk/card/1.0.0/index.min.js" type="text/javascript"></script>
    <script>
      // Call the bootstrap method
      widget.bootstrap({
        clientAccessToken: "**CLIENT ACCESS TOKEN**",
        component: {
          showPan: {
            cardPan: {
              domId: "card-pan",
              format: true,
              styles: styles.cardPan.styles, // A custom styles object
            },
            copyCardPan: {
              domId: "copy-card-pan",
              onCopySuccess: () => alert("Copied PAN!"),
              onCopyFailure: (error) => console.error(error),
            },
            cardExp: {
              domId: "card-exp",
              format: true,
              styles: styles.cardExp.styles, // A custom styles object
            },
            copyCardExp: {
              domId: "copy-card-exp",
              onCopySuccess: () => alert("Copied expiration date!"),
              onCopyFailure: (error) => console.error(error),
            },
            cardCvv: {
              domId: "card-cvv",
              styles: styles.cardCvv.styles, // A custom styles object
            },
            copyCardCvv: {
              domId: "copy-card-cvv",
              onCopySuccess: () => alert("Copied CVV2!"),
              onCopyFailure: (error) => console.error(error),
            },
          },
        },
        callbackEvents: {
          onSuccess: () => console.log("Success!"),
          onFailure: (error) => console.error(error),
        },
      });
    </script>
  </body>
</html>

Step 1 — Prepare a blank HTML page

Before integrating with Widget.js to display card data, prepare an otherwise blank HTML page.

Create a new HTML file and add the following code:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Widget.js Card Data Tutorial</title>
  </head>
  <body>
  </body>
</html>

Step 2 — Reference Widget.js

If you are working in your private sandbox environment, add the following script tag to your HTML:

<script src="https://staging-static.univisioncard.com/api/sdk/card/1.0.0/index.min.js" type="text/javascript"></script>

If you are working in a production environment, add the following script tag to your HTML:

<script src="https://static.univisioncard.com/api/sdk/card/1.0.0/index.min.js" type="text/javascript"></script>

Step 3 — Add a widget.bootstrap() method call

To inject the iframes into your HTML, call the widget.bootstrap() method of Widget.js from your HTML.

Add the following script tag to your HTML after the reference to Widget.js. You’ll need to pass an object to the method, but leave it empty for now.

<script>
  widget.bootstrap();
</script>

Step 4 — Add target div elements

To present all three pieces of card data, create three div elements—one each for the PAN, expiration date, and CVV2. Assign a unique ID to each div; you will use these IDs in the widget.bootstrap() configuration object.

Add the following code to your HTML:

<div>
  <div id='card-pan'></div>
  <div id='card-exp'></div>
  <div id='card-cvv'></div>
</div>

You can use CSS to define the width of the iframes created by Widget.js if you need to resize them. You have the option of setting the iframe’s width to a fixed value such as 300 px or to a relative value such as 75%.

If you set the iframe’s width to 100%, it will expand to fill the size of the parent container. In terms of the span, its text will stop wrapping once it is wide enough. In many cases, it is recommended to set the iframe width to 100%, and then style the parent container to the width you actually need.

Step 5 — Add the bootstrap configuration object

Use the configuration object to define how the iframes appear on your webpage. Make sure to include the required data. You can configure Widget.js to display any combination of the card’s PAN, CVV2, and expiration date.

In this scenario, you will display the PAN, CVV2, and expiration date, providing the necessary card data for your customer.

You have the option of supplying style-related information inline (see card-pan below) or as a custom style object you create (see the card-exp style object styles.cardExp.styles below).

Add the following object as an argument for the bootstrap() method. Later steps in this tutorial will explain how to retrieve the data that replaces the placeholder text.

{
  "clientAccessToken": "**CLIENT ACCESS TOKEN**",
  "component": {
    "showPan": {
      "cardPan": {
        "domId": "card-pan",
        "format": true,
        "styles": {
          "span": {
            "background": "green",
            "color": "white",
            "font-family": "monospace",
            "letter-spacing": "2px",
            "font-weight": "bold"
          }
        }
      },
     "cardExp": {
       "domId": "card-exp",
       "format": true,
       "styles": styles.cardExp.styles // A custom styles object
     },
     "cardCvv": {
       "domId": "card-cvv",
       "styles": styles.cardCvv.styles // A custom styles object
      }
    }
  },
  "callbackEvents": {
    "onSuccess": () => console.log("Success!"),
    "onFailure": error => console.error(error)
  }
}

Step 6 — Request a client access token

Each time you want to display a virtual card’s sensitive data, you must request a new one-time client access token. The client access token expires after five minutes.

Request a client access token from the Interlace platform by sending a GET request to the /open-api/v3/cards/{card-id}/assess-token endpoint.

Use the following cURL to request an access token. Replace the following placeholder text:

  • YOUR ACCESS TOKEN
  • CARD ID

Query Parameters:

  • ACCOUNT ID
curl -X GET 'https://api-sandbox.interlace.money/open-api/v3/cards/{card-id}/access-token?accountId={account-id}' \
  -H 'x-access-token: **ACCESS TOKEN**' \
  -H 'Content-Type: application/json'

The following is a sample response to a request for a client access token:

{
    "code": 200,
    "message": "success",
    "data": "37d1a36d3583bed8d0cd13fe4c074aeaefdc6bbbd0aeb66d12ffb5325031fa9b"
}

After obtaining the client access token, embed it into your JavaScript code in place of the CLIENT ACCESS TOKEN placeholder text. For this tutorial, you can manually insert the token into your HTML code. In a production situation, however, you should programmatically insert the token before calling the bootstrap() method.

Step 7 — Add "Copy to Clipboard" buttons

Use the following set of HTML buttons to enable cardholders to copy card data elements to the clipboard of their device.

Add the following HTML to the page, at the top of the <body> tag, after the line <div id='card-cvv'></div>:

<div id='copy-pan-container' style="position: relative;">
  <img src="copyPanIcon.jpg" alt="Copy card number" id="copy-card-pan">
</div>
<div id='copy-exp-container' style="position: relative;">
  <img src="copyExpIcon.jpg" alt="Copy expiration date" id="copy-card-exp">
</div>
<div id='copy-cvv-container' style="position: relative;">
  <img src="copyCvvIcon.jpg" alt="Copy CVV" id="copy-card-cvv">
</div>
⚠️

Warning

The images referenced in the above example will not load because this is not an actual resource as-is.

Now modify the widget.bootstrap() method to configure these iframes:

widget.bootstrap({
 "clientAccessToken": "**CLIENT ACCESS TOKEN**",
   "component": {
     "showPan": {
       "cardPan": {
         "domId": "card-pan",
         "format": true,
         "styles": styles.cardPan.styles // A custom styles object
       },
       "copyCardPan": {
         "domId": "copy-card-pan",
         "onCopySuccess": () => alert("Copied PAN!"),
         "onCopyFailure": error => console.error(error)
       },
       "cardExp": {
         "domId": "card-exp",
         "format": true,
         "styles": styles.cardExp.styles // A custom styles object
       },
       "copyCardExp": {
         "domId": "copy-card-exp",
         "onCopySuccess": () => alert("Copied expiration date!"),
         "onCopyFailure": error => console.error(error)
       },
       "cardCvv": {
         "domId": "card-cvv",
         "styles": styles.cardCvv.styles // A custom styles object
       },
       "copyCardCvv": {
         "domId": "copy-card-cvv",
         "onCopySuccess": () => alert("Copied CVV2!"),
         "onCopyFailure": error => console.error(error)
       }
     }
   },
});

Step 8 — Add a hover state

A hover state is a style configuration that you pass to the widget.bootstrap() method. The parent div receives hover events from its associated iframe.

To implement a hover state, add the following CSS statements to the HTML page inside the <head> tags:

<style type="text/css">
  #copy-pan-container:hover > img /* styles */
  #copy-exp-container:hover > img /* styles */
  #copy-cvv-container:hover > img /* styles */
</style>

Step 9 — Run widget.bootstrap()

When the page loads, widget.bootstrap() injects the iframe populated with card data and styles into the inner container div elements card-pan, card-exp, and card-cvv, as specified in the configuration object. The Copy to Clipboard iframes are also injected if you choose to integrate the Copy to Clipboard functionality.

📘

Tip

You can check the console of your browser for the success or failure message, as defined in the callbackEvents object.