MENU
PHP CSharp Perl Ruby

Secure Overlay API

The Secure Overlay is a way for PDCflow customers/partners to easily encrypt and tokenize credit card and bank account numbers from their own sites/software without PCI Compliance concerns.

It provides a tight integration between account management software and PDCflow’s Payment Processing Products and takes the core software platform out of PCI Scope. Enter credit card or bank account data from within your software system using our secure overlay form. The sensitive data field resides on our secure, PCI Level 1 Certified servers, and overlays the data entry field on the core software’s payment page.

Sensitive Data is encrypted, tokenized, and stored in a secure vault. The token is then returned to your software system through the HTML MessageChannel api, eliminating your transmission of any PCI relevant data.

Implementation

Your HTML page

Iframe src
Test url:
https://wsdemo.pdc4u.com/SecureOverlay/v2_0?iframeId=myIframe
Live url:
https://ws.pdc4u.com/SecureOverlay/v2_0?iframeId=myIframe

<script type="text/javascript">
    ... 
    window.addEventListener('message', receiveMessage, false);
    const remoteDomain = 'https://wsdemo.pdc4u.com';
    let preType = 'CCN';

    //These are used to set up and communicate through the dedicated channel
    const channel1 = new MessageChannel();
    let port1 = channel1.port1;

    /*
     * This function will be used once, to handle the PRELOAD function
     * On the PRELOAD function, we will set up a dedicated MessageChannel to converse between iFrame and parent page
     */
    function receiveMessage(event) {
      if(event.origin !== remoteDomain) {
        console.log('remote origin: ' + event.origin);
        return;
      }

      if (event.data.function === 'PRELOAD') {
          //Establish the MessageChannel and handle any preload data (if you already have a token, pass it here)
      }
    }

    function receivedPortMessage(e) {
      //Once the MessageChannel is established, any communication from the overlay will be received here
    }

    function sendMessage(data) {
      port1.postMessage(data);
    }

    function tokenizeData() {
      var requiredData = {};
      ...
      sendMessage(data);
    }
    ...
</script>

The secure overlay uses the HTML MessageChannel() protocol. This allows messages to be posted from the parent website (your page) to the iframe and back. Thus, the parent page can initiate the tokenization and when the Credit Card or Bank Account number, entered on our page, is tokenized, the token can be returned to the parent page. The iframeId in the URL is not required; however, it is useful in identifying which iframe has loaded in the case the multiple SecureOverlay iframes are loaded on the same page.

API Functions

The API can be called with these functions. Each represents a different function to be executed on the Secure Overlay field.

Attribute Description
PRELOAD
JSONN/A
This function can be used to preload the field with an existing token, style the field, and define some functionality. See object definition below.
PROCESS
JSONN/A
The command to tokenize the value in the Secure Overlay field. See object definition below.
PATCH
JSONN/A
The command to make changes to an existing token. See object definition below.
BULK_UPLOAD
JSONN/A
The command to upload a bulk file. This is to be used with the file upload page. The endpoint expects a csv file with all columns used for processing a transaction, and will return a JSON object with a list of the data, including tokens, suitable for processing the transactions. See object definition below.
BILLING
JSONN/A
The command to tokenize for PDCFlow billing. This will tokenize the card or bank account to be used for billing purposes. The token returned can only be used by PDCFlow for monthly invoice payment. The required data is the same as the processData. See object definition below.
CLEAR
JSONN/A
Will clear all characters in the input field. See object definition below.

Preload Data

Preload Data Object

{
  "function": "PRELOAD",
  "backgroundColor": "#fff",
  "fontSize": "12",
  "token": "thisIsAToken1234",
  "focus": "false",
  "shouldNotify": "true",
  "clearFieldOnFocus": "true",
  "dataType": "CCN",
  "placeholder": "Card Number"
}
Attribute Description
function
Alpha7
The function to be performed by the remote javascript.
Valid value(s): PRELOAD
backgroundColor
AlphanumericN/A
Set the background color of the input field using a CSS color.
Format: Text (red, blue), Hexadecimal (#RRGGBB), RGB (rgb(255,0,0))
fontSize
AlphanumericN/A
Set the size of the font in the input box. Append the unit to the font size (12px, 2em, 12pt, etc).
Format: 12px
token
Alphanumeric16
An existing token to prepopulate the field with.
focus
Boolean5
Whether or not to set focus to the input field on page load. If you preload the token, it is not recommended that you focus on the field, as that may clear the preloaded token, based on whether clearFieldOnClick is true or not.
Valid value(s): true, false
shouldNotify
Boolean5
Define whether change events on the data field should send a NOTIFY post message. See object definition below for potential notifications.
Valid value(s): true, false
Default: false
clearFieldOnClick
Boolean5
Use this to define whether the data field should automatically clear its contents when focus is put on the field.
Valid value(s): true, false
Default: true
dataType
Alpha3
Use this to define whether the field is being used for card or bank account data. This only affects the real-time validation as handled by the shouldNotify events. This will be passed in again in the Process Data object to define the final dataType to tokenize.
Valid value(s):
CCN - Credit card,
BAN - Bank account number
Default: CCN
placeholder
Alpha32
This will set a placeholder value in the data entry field.

Process Data

Process Data Object

{
  "function": "PROCESS",
  "companyId": 1234,
  "apiKey": "yourApiKeyHere",
  "dataType": "CCN",
  "ccExpMonth": "12",
  "ccExpYear": "2024",
  "routingNumber": "",
  "bankAccountType": "",
  "authorizedForFutureUse": "true",
  "secondName": ""
}
Attribute Description
function
Alpha7
The function to be performed by the remote javascript. PROCESS is used for general data tokenization. BILLING is used to create a token that will be used by PDCflow to process your company's invoice.
Valid value(s): PROCESS, BILLING
Note: If a token has already been created and a PROCESS request is sent, and we detect no changes in the secure overlay field, we will treat the request as a PATCH request
apiKey
AlphanumericN/A
Your PDCFlow APIKey for tokenizing data.
companyId
Numeric8
Your PDCFlow Company Id.
dataType
Alpha3
The type of data you are tokenizing.
Valid value(s):
CCN - Credit card,
BAN - Bank account number
ccExpMonth
Numeric2
Conditional
The expiration month of the card.
Required for dataType=CCN
Format: MM
ccExpYear
Numeric4
Conditional
The expiration year of the card.
Required for dataType=CCN
Format: YYYY
routingNumber
Numeric9
Conditional
The routing number for the bank that the bank account number is associated with.
Required for dataType=BAN
bankAccountType
Alpha8
Conditional
The type of account the bank account number is.
Required for dataType=BAN
Valid value(s):
CHECKING, SAVINGS
authorizedForFutureUse
Boolean5
Required
Whether the card number or bank data can be used for future transactions. Setting this to false will result in the token being usable for one transaction (including CREDIT and VOID of that transaction) but any future transaction with this token will be blocked.
secondName
AlphanumericN/A
This parameter can be used to validate the values are equal between two Secure Overlay fields. If you have a second Secure Overlay iframe on the page and you want to validate that the user has entered the same value in both Secure Overlay fields, you can pass in the name of the second iframe and SecureOverlay will validate that the values in the fields are equal.

Patch Data

Patch Data Object

{
  "function": "PATCH",
  "companyId": 1234,
  "apiKey": "yourApiKeyHere",
  "dataType": "CCN",
  "ccExpMonth": "12",
  "ccExpYear": "2024",
  "routingNumber": "",
  "bankAccountType": "",
  "existingToken": "ThisIsAToken1234",
  "authorizedForFutureUse": "true",
  "secondName": ""
}
Attribute Description
function
Alpha7
The function to be performed by the remote javascript. A PATCH request will update an existing token with the provided data.
Valid value(s): PATCH
apiKey
AlphanumericN/A
Your PDCFlow APIKey for tokenizing data.
companyId
Numeric8
Your PDCFlow Company Id.
dataType
Alpha3
The type of data you are tokenizing.
Valid value(s):
CCN - Credit card,
BAN - Bank account number
ccExpMonth
Numeric2
Conditional
The expiration month of the card.
Required for dataType=CCN if ccExpYear is present.
Format: MM
ccExpYear
Numeric4
Conditional
The expiration year of the card.
Required for dataType=CCN if ccExpMonth is present.
Format: YYYY
routingNumber
Numeric9
The routing number for the bank that the bank account number is associated with.
bankAccountType
Alpha8
The type of account the bank account number is.
Valid value(s):
CHECKING, SAVINGS
authorizedForFutureUse
Boolean5
Whether the card number or bank data can be used for future transactions. Setting this to false will result in the token being usable for one transaction (including CREDIT and VOID of that transaction) but any future transaction with this token will be blocked.
secondName
AlphanumericN/A
This parameter can be used to validate the values are equal between two Secure Overlay fields. If you have a second Secure Overlay iframe on the page and you want to validate that the user has entered the same value in both Secure Overlay fields, you can pass in the name of the second iframe and SecureOverlay will validate that the values in the fields are equal.

Bulk Upload

Bulk Upload Request

{
  "function": "BULK_UPLOAD",
  "companyId": 1234,
  "apiKey": "yourApiKeyHere",
  "dataType": "CCN",
  "bulkType": "TRANSACTION"
}
Attribute Description
function
Alpha7
The function to be performed by the remote javascript. BULK_UPLOAD is used for uploading the bulk csv file.
Valid value(s): BULK_UPLOAD
apiKey
AlphanumericN/A
Your PDCFlow APIKey for tokenizing data.
companyId
Numeric8
Your PDCFlow Company Id.
dataType
Alpha3
The type of data you are tokenizing.
Valid value(s):
CCN - Credit card,
BAN - Bank account number
bulkType
Alpha11
Which type of bulk/validation/response is expected?
Valid value(s):
TRANSACTION - Bulk file includes all details for transactions.

Bulk Upload CSV

Header row (card):
FIRST_NAME, LAST_NAME, STREET_ADDRESS_ONE, CITY, STATE, ZIP, COUNTRY, PAYMENT_AMOUNT, FEE_AMOUNT, ACCOUNT_NUMBER, EMAIL_ADDRESS, PHONE_NUMBER, MEMO, CARD_NUMBER, CARD_MONTH, CARD_YEAR, CARD_CODE, POST_AUTHORIZATION

Header row (check):
FIRST_NAME, LAST_NAME, STREET_ADDRESS_ONE, CITY, STATE, ZIP, COUNTRY, PAYMENT_AMOUNT, FEE_AMOUNT, ACCOUNT_NUMBER, EMAIL_ADDRESS, PHONE_NUMBER, MEMO, BANK_ACCOUNT_NUMBER, BANK_ROUTING_NUMBER, BANK_ACCOUNT_TYPE

Required card fields: FIRST_NAME, LAST_NAME, PAYMENT_AMOUNT, ACCOUNT_NUMBER, CARD_NUMBER, CARD_MONTH, CARD_YEAR

Required check fields: FIRST_NAME, LAST_NAME, PAYMENT_AMOUNT, ACCOUNT_NUMBER, BANK_ACCOUNT_NUMBER, BANK_ROUTING_NUMBER, BANK_ACCOUNT_TYPE

The columns can be in any order and any combination of the non-required fields may be present, as long as they are specified in the header row and each row has a value (or empty value) for them. See the Object Definition below.

Clear

Clear Data Object

{
  "function": "CLEAR"
}
Attribute Description
function
Alpha5
The function to be performed by the remote javascript.
Valid value(s): CLEAR

Post Message Handling

These are the definitions of the objects the integrating website can expect to receive in a posted message.

Attribute Description
PRELOAD
The iframe will post a message when it is loaded and ready to establish a MessageChannel and set any preload data. See object definition below.
NOTIFY
The iframe will post messages pertaining to changes in the data field. See object definition below.
RESPONSE
The iframe will post a message with the response to the tokenization request. This will include both success and/or error messages. See object definition below.

Preload Handling

Preload Object

{
  "function": "PRELOAD",
  "id": "secondIframeId"
}
Attribute Description
function
Alpha7
The function that should be handled by the integrating website. When this message is received, the iframe is loaded and ready to handle a postMessage with the preloadData.
Valid value(s): PRELOAD
id
AlphaNumericN/A
The id of the iframe containing the Secure Overlay field. This is particularly useful for determining which field is ready if 2 Secure Overlay iframes are loaded on a single page.

Notify Data

Notify Data Object

{
  "function": "NOTIFY",
  "code": "001",
  "message": "Data in field changed to valid.",
  "cardType": "VISA"
}
Attribute Description
function
Alpha7
The function that identifies this post message.
Valid value(s): NOTIFY
code
Alpha6
A code to define which event has happened to the data field.
Valid value(s): 001, 002, 003, 004, 005, 006, 007, 008, 009, 010
Definitions:
001 - Enter button pressed on secure overlay field. SecureOverlay prevents any action happening when the user pushes Enter while focused on the Secure Overlay field. This notification event allows the integrator to handle this event if they desire.
002 - Secure overlay field is no longer empty.
003 - Secure overlay field changed to empty.
004 - Value in field is no longer valid. - The validation here will be defined by the dataType in the PRELOAD message.
005 - Value in field changed to valid. - The validation here will be defined by the dataType in the PRELOAD message.
006 - Card type. - As a card is entered, the card type will be passed once it can be determined.
007 - Hostname mismatch. - When initially establishing the port, if the postMessage does not match the hostname of the parent page, this notification will be triggered. In most cases, this can be ignored, as browser extensions and other browser plugins may trigger postMessages that are not from the parent page.
008 - Connection ports established. - This event is sent when the initial port connections have been established. Any communication after this event will/must be through the established port.
009 - Focus out of secure overlay field. - This notification event will be triggered when the user clicks into a secure overlay field.
010 - Focus is on secure overlay field. - This notification event will be triggered when the user clicks outside of the secure overlay field
message
Alphanumeric48
The message related to the NOTIFY event. Defined in the code definition above.
cardType
Alphanumeric48
The cardType for the entered data. This is only returned with code value of 6.
Valid value(s): VISA, MASTERCARD, AMERICAN_EXPRESS, DISCOVER, UNKNOWN
More types may be detected in the future.

Response Handling

Single Token Response Object

{
  "function": "RESULT",
  "result": "ERROR",
  "token": "",
  "cardType": "VISA",
  "mask": "4444441111",
  "errorList": [
    {
      "code": "10003",
      "description": "Field invalid",
      "fieldName": "ccExpYear"
    }
  ]
}
Attribute Description
function
Alpha8
The function to be performed by the remote javascript.
Valid value(s): RESPONSE
result
Alpha7
The result of the tokenization request. PRELOAD will be returned only if an existing token was preloaded and was not overwritten with new data.
Valid value(s): OK, ERROR, PRELOAD
token
Alphanumeric16
The token that represents the encrypted data in the PDCFlow system.
cardType
Alpha15
The type of card number that was tokenized, if the data was a card number.
Valid value(s): VISA, MASTERCARD, DISCOVERY, AMERICAN_EXPRESS, UNKNOWN
mask
Numeric10
The mask for the data that was tokenized.
For a card, this consists of the first 6 numbers and the last 4 numbers of the card.
For a bank account number, this consists of the last 3 numbers of the bank account number.
errorList
ListN/A
A list of error objects. See object definition below

Bulk Response Handling

Bulk Response Object

{
  "function": "RESPONSE",
  "result": "OK",
  "bulk": [
    {
      "firstName": "Monster",
      "lastName": "Mash",
      "paymentAmount": "125",
      "feeAmount": "3.00",
      "accountNumber": "123451",
      "emailAddress": "test@test.com",
      "memo": "Part of bulk transactions",
      "cardMonth": "12",
      "cardYear": "2025",
      "token": "thisIsAToken1234",
      "mask": "4000111234",
      "cardType": "VISA"
    },
    {
      "firstName": "November",
      "lastName": "Rain",
      "paymentAmount": "125",
      "feeAmount": "3.00",
      "accountNumber": "11111",
      "emailAddress": "test2@test.com",
      "memo": "Part of bulk transactions",
      "cardMonth": "4",
      "cardYear": "2023",
      "token": "thisIsAToken2222",
      "mask": "4000112222",
      "cardType": "VISA"
    }
  ]
}
Attribute Description
function
Alpha8
The function to be performed by the remote javascript.
Valid value(s): RESPONSE
result
Alpha7
The result of the tokenization request. PRELOAD will be returned only if an existing token was preloaded and was not overwritten with new data.
Valid value(s): OK, ERROR, PRELOAD
bulk
List
The list of objects from the bulk upload. See object definition below.
errorList
ListN/A
A list of error objects. See object definition below

Bulk Object

Bulk Object

Bank csv
FIRST_NAME *, LAST_NAME *, PAYMENT_AMOUNT *, FEE_AMOUNT *, ACCOUNT_NUMBER *, BANK_ACCOUNT_NUMBER, BANK_ROUTING_NUMBER, BANK_ACCOUNT_TYPE
Crazy, Bob, 125, 3.5, 123444, 123456789, 124000054, CHECKING
Lard, McGoo, 125, 3.5, 123444, 987654321, 124000054, SAVINGS

Card csv
FIRST_NAME *, LAST_NAME *, STREET_ADDRESS_ONE, CITY, STATE, ZIP, COUNTRY, PAYMENT_AMOUNT *, FEE_AMOUNT *, ACCOUNT_NUMBER *, EMAIL_ADDRESS, PHONE_NUMBER, MEMO, CARD_NUMBER *, CARD_MONTH *, CARD_YEAR *, CARD_CODE, POST_AUTHORIZATION
Crazy, Bob, here, there, UT, 84067, US, 125, 3.5, 123444, , , memo, 4000100011112224, 12, 2025, 444, postAuth
{
  "firstName": "Monster",
  "lastName": "Mash",
  "city": "here",
  "state": "CA",
  "zip": "90210",
  "country": "US",
  "paymentAmount": "125",
  "feeAmount": "3.00",
  "accountNumber": "123451",
  "emailAddress": "test@test.com",
  "memo": "Part of bulk transactions",
  "cardMonth": "12",
  "cardYear": "2025",
  "token": "thisIsAToken1234",
  "mask": "4000111234",
  "cardType": "VISA",
  "errorList": []
}
Attribute Description
firstName
AlphaNumeric45
Required
The first name of the person on the transaction.
lastName
AlphaNumeric45
Required
The last name of the person on the transaction.
city
AlphaNumeric
The city of the person on the transaction.
state
Alpha2
The state of the person on the transaction.
zip
Numeric5
The zip of the person on the transaction.
country
Alpha2
The country of the person on the transaction.
paymentAmount
NumericDecimal
Required
The amount for the transaction payment.
feeAmount
NumericDecimal
The fee amount for the transaction.
accountNumber
AlphaNumeric
Required
The account number for the person making the payment.
token
AlphaNumeric
The token for the card or bank account number.
mask
Numeric
The mask of the card number or bank account number. For a card, this is the first 6 and last 4 of the card number. For a bank account, this is the last 4 of the bank account.
emailAddress
AlphaNumeric
The email address for the person making the payment.
memo
AlphaNumeric
A memo on the payment.
cardMonth
Numeric
The expiration month for the card, if cards.
cardYear
Numeric
The expiration year for the card, if cards.
cardType
Alphanumeric
The cardType for the card.
Valid value(s): VISA, MASTERCARD, AMERICAN_EXPRESS, DISCOVER, UNKNOWN
More types may be detected in the future.
bankRoutingNumber
Numeric
The routing number for the bank account.
bankAccountType
Alpha
The type of bank account.
Valid value(s): SAVINGS, CHECKING0
errorList
ListN/A
A list of error objects. See object definition below

Response Error

Response Error Object

{
  "code": "10003",
  "description": "Field invalid",
  "fieldName": "ccExpYear"
}
Attribute Description
code
Numeric5
A code to define the error.
Valid value(s): 10001, 10002, 10003, 10004, 10005
Definitions:
10001 - Generic error. An unexpected error occurred.
10002 - Invalid security credentials. Either the companyId or apiKey was not present in the request, or the apiKey was invalid.
10003 - Field invalid. The value in the field was invalid. The fieldName parameter will have the name of the field.
10004 - Missing required field. - A required field is empty. The fieldName parameter will have the name of the field.
10005 - Duplicate field mismatch. - The value in the second SecureOverlay field does not match the first field. The fieldName parameter will be secondName.
description
Alphanumeric48
The user-friendly description of the error.
fieldName
Alpha48
The name of the field which had the error. This will only be present if there was an error on a specific field; for example, a validation error. Other than secureData, which represents the card/bank account number field, these field names will match the values required in the processData object. The secondName fieldName is returned if that name is not found in the dom or if the value in the second field does not match the value in the first field.
Valid value(s): secureData, apiKey, companyId, dataType, ccExpMonth, ccExpYear, routingNumber, bankAccountType, authorizedForFutureUse, secondName.

Example Workflow

Here is an example workflow for how your integration may happen.

Sample Code

Sample Card tokenization

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
  <title>Payment</title>
  <meta charset="utf-8" />
</head>
<body>
<div class="container">
  <div class="ccPayment">
    <iframe id="iframeId" src="https://wsdemo.pdc4u.com/SecureOverlay/v2_0"></iframe><br />
    <input type="tel" id="expMonth" placeholder="Expiration Month" /><br />
    <input type="tel" id="expYear" placeholder="Expiration Year" /><br />
    <p>Authorized for Future Use:</p>
    <div>
      <label for="authYes">Yes</label>
      <input type="radio" id="authYes" name="authorizedForFutureUse" value="Yes" checked>
    </div>
    <div>
      <label for="authNo">No</label>
      <input type="radio" id="authNo" name="authorizedForFutureUse" value="No">
    </div>
    <br/>
    <button id="nextButton" onclick="tokenizeData()">Tokenize</button>
  </div>
</div>

<script type="text/javascript">

  window.addEventListener('message', receiveMessage, false);
  const remoteDomain = 'https://wsdemo.pdc4u.com';
  let preType = 'CCN';

  //These are used to set up and communicate through the dedicated channel
  const channel1 = new MessageChannel();
  let port1 = channel1.port1;
  //We will use this to prevent submitting a PRELOAD request a second time in the case that a second PRELOAD message is received from SecureOverlay
  let alreadyPreloading = false; 

  /*
   * This function will be used once, to handle the PRELOAD function
   * On the PRELOAD function, we will set up a dedicated MessageChannel to converse between iFrame and parent page
   */
  function receiveMessage(event) {
    if(event.origin !== remoteDomain) {
      console.log('remote origin: ' + event.origin);
      return;
    }

    switch(event.data.function) {
      case 'PRELOAD':
        if (alreadyPreloading) {
          return;
        }
        alreadyPreloading = true;
        //First we set up the dedicated channel
        initiatePort();
        break;
    }
  }


  function initiatePort() {
    //receivedMessage function will be called when a postMessage from the iframe through this port comes through
    port1.onmessage = receivedMessage;
    //Each MessageChannel has 2 ports. One is used on the parent side (channel1.port1) and one is passed to the iframe to establish the connection (channel1.port2)
    document.getElementById("iframeId").contentWindow.postMessage('init', '*', [channel1.port2]);
  }


  function receivedMessage(e) {
    //Notice there is no needs to validate the event.origin when receiving a message as this is a dedicated channel directly from the parent page to the iframe
    console.log(e.data);
    //It is recommended here to listen for the NOTIFY 008 message, which signifies the port connection is established. Then we can remove our generic message listener and also send our PRELOAD data through the port
    if (e.data.function === 'NOTIFY' && e.data.code === '008') {
      window.removeEventListener('message', receiveMessage);
      const preloadData = {};
      preloadData.function = 'PRELOAD';
      // styleOptions.token = 'thisIsAToken1234';
      preloadData.focus = false;
      preloadData.clearFieldOnClick = false;
      preloadData.backgroundColor = "RED";
      preloadData.shouldNotify = true;
      preloadData.dataType = preType;
      sendMessage(preloadData);
    }
    else {
      //handle other communication here (notifications/tokenization response, etc.)
    }
  }

  function sendMessage(data) {
    //All that is required to do to send a message is use the port1.postMessage();
    port1.postMessage(data);
  }

  function tokenizeData() {
    const data = {};
    data.dataType = 'CCN';
    data.ccExpMonth = document.getElementById('expMonth').value;
    data.ccExpYear = document.getElementById('expYear').value;
    data.apiKey = 'replaceWithRealApiKeyForTest';
    data.companyId = 'replaceWithRealCompanyId';
    data.function = 'PROCESS';

    let radio = document.getElementsByName('authorizedForFutureUse');

    for(let i = 0; i < radio.length; i++) {
      if(radio[i].checked) {
        data.authorizedForFutureUse = radio[i].value === 'Yes';
      }
    }

    sendMessage(data);
  }

</script>

</body>
</html>

Here is a sample HTML page that has all the code required to interact with the Secure Overlay API. Here a credit card number will be tokenized. It contains the complete Javascript code as well as a test form that includes the Secure Overlay field and additional field required for tokenizing a card.

Sample Bank Account Tokenization

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
  <title>Payment</title>
  <meta charset="utf-8" />
</head>
<body>
<div class="container">
  <div class="achPayment">
    <iframe id="iframeId" src="https://wsdemo.pdc4u.com/SecureOverlay/v2_0"></iframe>
    <br/>
    <input type="tel" id="routingNumber" placeholder="Routing Number" />
    <br/>
    <label for="bankAccountType">Bank account type</label>
    <select name="bankAccountType" id="bankAccountType" >
      <option value="CHECKING" selected="selected">Checking</option>
      <option value="SAVINGS">Savings</option>
    </select>
    <br/>
    <p>Authorized for Future Use:</p>
    <div>
      <label for="authYes">Yes</label>
      <input type="radio" id="authYes" name="authorizedForFutureUse" value="Yes" checked>
    </div>
    <div>
      <label for="authNo">No</label>
      <input type="radio" id="authNo" name="authorizedForFutureUse" value="No">
    </div>
    <br/>
    <button id="nextButton" onclick="tokenizeData()">Tokenize</button>
  </div>
</div>


<script type="text/javascript">
  window.addEventListener('message', receiveMessage, false);
  const remoteDomain = 'https://wsdemo.pdc4u.com';
  let preType = 'BAN';

  //These are used to set up and communicate through the dedicated channel
  const channel1 = new MessageChannel();
  let port1 = channel1.port1;
  //We will use this to prevent submitting a PRELOAD request a second time in the case that a second PRELOAD message is received from SecureOverlay
  let alreadyPreloading = false;

  /*
   * This function will be used once, to handle the PRELOAD function
   * On the PRELOAD function, we will set up a dedicated MessageChannel to converse between iFrame and parent page
   */
  function receiveMessage(event) {
    if(event.origin !== remoteDomain) {
      console.log('remote origin: ' + event.origin);
      return;
    }

    switch(event.data.function) {
      case 'PRELOAD':
        if (alreadyPreloading) {
          return;
        }
        alreadyPreloading = true;
        //First we set up the dedicated channel
        initiatePort();
        break;
    }
  }


  function initiatePort() {
    //receivedMessage function will be called when a postMessage from the iframe through this port comes through
    port1.onmessage = receivedMessage;
    //Each MessageChannel has 2 ports. One is used on the parent side (channel1.port1) and one is passed to the iframe to establish the connection (channel1.port2)
    document.getElementById("iframeId").contentWindow.postMessage('init', '*', [channel1.port2]);
  }


  function receivedMessage(e) {
    //Notice there is no needs to validate the event.origin when receiving a message as this is a dedicated channel directly from the parent page to the iframe
    console.log(e.data);
    //It is recommended here to listen for the NOTIFY 008 message, which signifies the port connection is established. Then we can remove our generic message listener and also send our PRELOAD data through the port
    if (e.data.function === 'NOTIFY' && e.data.code === '008') {
      window.removeEventListener('message', receiveMessage);
      const preloadData = {};
      preloadData.function = 'PRELOAD';
      // styleOptions.token = 'thisIsAToken1234';
      preloadData.focus = false;
      preloadData.clearFieldOnClick = false;
      preloadData.backgroundColor = "RED";
      preloadData.shouldNotify = true;
      preloadData.dataType = preType;
      sendMessage(preloadData);
    }
    else {
      //handle other communication here (notifications/tokenization response, etc.)
    }
  }

  function sendMessage(data) {
    //All that is required to do to send a message is use the port1.postMessage();
    port1.postMessage(data);
  }

  function tokenizeData() {
    console.log("tokenizing");
    const data = {};
    data.dataType = 'BAN';
    data.routingNumber = document.getElementById('routingNumber').value;
    data.bankAccountType = document.getElementById('bankAccountType').value;

    let radio = document.getElementsByName('authorizedForFutureUse');

    for(let i = 0; i < radio.length; i++) {
      if(radio[i].checked) {
        data.authorizedForFutureUse = radio[i].value === 'Yes';
      }
    }

    data.apiKey = 'replaceWithRealApiKeyForTest';
    data.companyId = 'replaceWithRealCompanyId';
    data.function = 'PROCESS';

    sendMessage(data);
  }

</script>

</body>
</html>

Here is a sample HTML page that has all the code required to interact with the Secure Overlay API. Here a bank account number will be tokenized. It contains the complete Javascript code as well as a test form that includes the Secure Overlay field and additional field required for tokenizing a bank account number.

Sample Multiple Fields Bank Account Tokenization

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
  <title>Payment</title>
  <meta charset="utf-8" />
</head>
<body>
<div class="container">
  <div class="achPayment">
    <iframe id="myIframeId" src="https://wsdemo.pdc4u.com/SecureOverlay/v2_0?iframeId=myIframeId"></iframe>
    <iframe id="iframe2Id" name="iframe2Id" src="https://wsdemo.pdc4u.com/SecureOverlay/v2_0?iframeId=iframe2Id"></iframe>
    <br/>
    <input type="tel" id="routingNumber" placeholder="Routing Number" />
    <br/>
    <label for="bankAccountType">Bank account type</label>
    <select name="bankAccountType" id="bankAccountType" >
      <option value="CHECKING" selected="selected">Checking</option>
      <option value="SAVINGS">Savings</option>
    </select>
    <br/>
    <p>Authorized for Future Use:</p>
    <div>
      <label for="authYes">Yes</label>
      <input type="radio" id="authYes" name="authorizedForFutureUse" value="Yes" checked>
    </div>
    <div>
      <label for="authNo">No</label>
      <input type="radio" id="authNo" name="authorizedForFutureUse" value="No">
    </div>
    <br/>
    <button id="nextButton" onclick="tokenizeData()">Tokenize</button>
  </div>
</div>


<script type="text/javascript">
  window.addEventListener('message', receiveMessage, false);
  //mark that we have set a listener
  let listenerExists = true;

  const remoteDomain = 'https://wsdemo.pdc4u.com';
  let preType = 'BAN';

  //These are used to set up and communicate through the dedicated channel
  const channel1 = new MessageChannel();
  const channel2 = new MessageChannel();
  let port1 = channel1.port1;
  let port2 = channel2.port1;

  //We will use this to prevent submitting a PRELOAD request a second time in the case that a second PRELOAD message is received from SecureOverlay
  let alreadyPreloading = false;

  /*
   * This function will be used once, to handle the PRELOAD function
   * On the PRELOAD function, we will set up a dedicated MessageChannel to converse between iFrame and parent page
   * Because 2 Secure Overlay fields have been loaded, you will receive messages from each field, thus getting multiple PRELOAD messages.
   * In order to distinguish which iframe is sending the message, the PRELOAD message will include the id of the iframe if it is provided in the src URL, 
   * in this case "myIframeId" or "iframe2Id"
   * In most workflows, you will only need to tokenize one field. The second field would be used to validate that the user entered their information correctly
   * by having them re-enter it in the second field.
   * If the parent page has need to communicate with both SecureOverlay fields (for styling, for example), a second MessageChannel can be established, same
   * as the first is.
   */
  function receiveMessage(event) {
    if(event.origin !== remoteDomain) {
      console.log('remote origin: ' + event.origin);
      return;
    }

    switch(event.data.function) {
      case 'PRELOAD':
        initiatePort(event.data.id);
        break;
      case 'RESPONSE':
        if(event.data.errorMsg) {
          console.log('error: ' + event.data.errorMsg);
        }
        console.log('data:');
        console.log(event.data);
        break;
    }

    switch(event.data.function) {
      case 'PRELOAD':
        if (alreadyPreloading) {
          return;
        }
        //First we set up the dedicated channel for both iframe fields. This will allow us to style them both. We will only tokenize the first field, however.
        //This id is only returned if it is provided in the iframe src URL (?iframeId=myIframeId)
        alreadyPreloading = true;
        initiatePort(event.data.id);

        if (event.data.id === 'myIframeId') {
          const styleOptions = {};
          styleOptions.backgroundColor = '#ccc';
          styleOptions.fontSize = '14px';
          styleOptions.function = 'PRELOAD';
          styleOptions.dataType = preType;
          sendMessage(styleOptions);
        }
        break;
    }
  }


  function initiatePort(id) {
    //receivedMessage function will be called when a postMessage from the iframe through this port comes through
    //Each MessageChannel has 2 ports. One is used on the parent side (channel1.port1) and one is passed to the iframe to establish the connection (channel1.port2)
    if(id === 'firstIframe') {
      port11.onmessage = channel1Message;
      document.getElementById(id).contentWindow.postMessage('init', '*', [channel1.port2]);
    }
    else if(id === 'secondIframe') {
      port12.onmessage = channel2Message;
      document.getElementById('secondIframe').contentWindow.postMessage('init', '*', [channel2.port2]);
    }
  }

  function channel1Message(e) {
    console.log(e.data);
    if (listenerExists) {
      listenerExists = false;
      window.removeEventListener('message', receiveMessage);
    }
    //We get a notification that the port is established so we can send the PRELOAD
    if (e.data.function === 'NOTIFY' && e.data.code === '008') {
      const preloadData = {};
      preloadData.function = 'PRELOAD';
      preloadData.focus = false;
      preloadData.clearFieldOnClick = false;
      preloadData.backgroundColor = "RED";
      preloadData.shouldNotify = true;
      preloadData.dataType = preType;
      sendMessage(preloadData, '1');
    }
  }

  function channel2Message(e) {
    console.log(e.data);
    if (listenerExists) {
      listenerExists = false;
      window.removeEventListener('message', receiveMessage);
    }
    //We get a notification that the port is established so we can send the PRELOAD
    if (e.data.function === 'NOTIFY' && e.data.code === '008') {
      const preloadData = {};
      preloadData.function = 'PRELOAD';
      preloadData.focus = false;
      preloadData.clearFieldOnClick = false;
      preloadData.backgroundColor = "BLUE";
      preloadData.shouldNotify = false;   //On the second field, we probably don't need NOTIFY events
      preloadData.dataType = preType;
      sendMessage(preloadData, '2');
    }
  }


  function sendMessage(data, channel) {
    //All that is required to do to send a message is using the correct port;
    if (channel === '1') {
      port1.postMessage(data);
    }
    else if (channel === '2') {
      port2.postMessage(data);
    }
  }

  function tokenizeData() {
    console.log("tokenizing");
    const data = {};
    data.dataType = 'BAN';
    data.routingNumber = document.getElementById('routingNumber').value;
    data.bankAccountType = document.getElementById('bankAccountType').value;

    let radio = document.getElementsByName('authorizedForFutureUse');

    for(let i = 0; i < radio.length; i++) {
      if(radio[i].checked) {
        data.authorizedForFutureUse = radio[i].value === 'Yes';
      }
    }

    data.apiKey = 'replaceWithRealApiKeyForTest';
    data.companyId = 'replaceWithRealCompanyId';
    data.function = 'PROCESS';
    //We want SecureOverlay to validate that the data in each field is the same, thus send the id of the second iframe
    data.secondName = 'iframe2Id';

    //We only need to tokenize field 1; we will get the token back for it and use that token for processing
    sendMessage(data, '1');
  }

</script>

</body>
</html>

Here is a sample HTML page that has all the code required to interact with the Secure Overlay API. Here a bank account number will be tokenized. It contains the complete Javascript code as well as a test form that includes the Secure Overlay field and additional field required for tokenizing a bank account number. Notice the iframeId value in the URL. Providing this value allows SecureOverlay to identify in the PreloadData which iframe is loaded. The second Secure Overlay field also expects input, and the data.secondName in the tokenizeData() method will force SecureOverlay to validate that the values in the two Secure Overlay fields are identical.

Sample Bulk Card Tokenization

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
  <title>Payment</title>
  <meta charset="utf-8" />
</head>
<body>
<div class="container">
  <div class="bulkPayments">
    <iframe id="iframeId" src="https://wsdemo.pdc4u.com/SecureOverlay/v2_0/bulk"></iframe>
    <button id="nextButton" onclick="uploadBulk()">Upload</button>
  </div>
</div>


<script type="text/javascript">
  window.addEventListener('message', receiveMessage, false);
  const remoteDomain = 'https://wsdemo.pdc4u.com';

  //These are used to set up and communicate through the dedicated channel
  const channel1 = new MessageChannel();
  let port1 = channel1.port1;
  //We will use this to prevent submitting a PRELOAD request a second time in the case that a second PRELOAD message is received from SecureOverlay
  let alreadyPreloading = false;

  /*
   * This function will be used once, to handle the PRELOAD function
   * On the PRELOAD function, we will set up a dedicated MessageChannel to converse between iFrame and parent page
   */
  function receiveMessage(event) {
    if(event.origin !== remoteDomain) {
      console.log('remote origin: ' + event.origin);
      return;
    }

    switch(event.data.function) {
      case 'PRELOAD':
        if (alreadyPreloading) {
          return;
        }
        alreadyPreloading = true;
        //First we set up the dedicated channel
        initiatePort();
        break;
    }
  }


  function initiatePort() {
    //receivedMessage function will be called when a postMessage from the iframe through this port comes through
    port1.onmessage = receivedMessage;
    //Each MessageChannel has 2 ports. One is used on the parent side (channel1.port1) and one is passed to the iframe to establish the connection (channel1.port2)
    document.getElementById("iframeId").contentWindow.postMessage('init', '*', [channel1.port2]);
  }


  function receivedMessage(e) {
    //Notice there is no needs to validate the event.origin when receiving a message as this is a dedicated channel directly from the parent page to the iframe
    console.log(e.data);
    //It is recommended here to listen for the NOTIFY 008 message, which signifies the port connection is established. Then we can remove our generic message listener and also send our PRELOAD data through the port
    if (e.data.function === 'NOTIFY' && e.data.code === '008') {
      window.removeEventListener('message', receiveMessage);
      const preloadData = {};
      preloadData.function = 'PRELOAD';
      preloadData.backgroundColor = "RED";
      preloadData.shouldNotify = true;
      sendMessage(preloadData);
    }
    else {
      //handle other communication here (notifications/tokenization response, etc.)
    }
  }

  function sendMessage(data) {
    //All that is required to do to send a message is use the port1.postMessage();
    port1.postMessage(data);
  }

  function uploadBulk() {
    console.log("Uploading");
    const data = {};
    data.dataType = 'CCN';
    data.apiKey = 'replaceWithRealApiKeyForTest';
    data.companyId = 'replaceWithRealCompanyId';
    data.function = 'BULK_UPLOAD';
    data.bulkType = 'TRANSACTION';

    sendMessage(data);
  }

</script>

</body>
</html>

Here is a sample HTML page that has all the code required to interact with the Secure Overlay API. Here a bulk file will be uploaded. We specify the dataType as CCN, and as such, the file must contain credit card numbers. Also, specifying the bulkType as TRANSACTION, the service will validate all required fields for transactions. This will NOT process said transactions, only tokenize the card numbers. It contains the complete Javascript code as well as a test form that includes the Secure Overlay field and additional field required for tokenizing a bank account number.