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

# Setting up Payment Method Tokenization

This explains how to use a checkout page for PCI SAQ A compliance with the Vindicia REST back end. We call this Payment Method Tokenization PMT.

1. Place a DIV tag in your form.

   Instead of an input field for sensitive data (credit card number, expiration date, cvn), which would look like this:

   ```

   <input type="text" id="vin_credit_card_account" 
   name="vin_credit_card_account" placeholder="Enter Credit Card Number" />
   ```

   add a DIV tag as follows:

   ```

   <div id="vin_credit_card_account"></div>
   ```

2. Include vindicia.js on your checkout page.

   For example:

   ```

   <script src="https://cdn2.vindicia.com/pmt/vindicia.js"></script>
   ```

   Although there is no requirement for JQuery or any other Javascript framework, Vindicia recommends that you include vindicia.js after you include the framework file.This instantiates an object called `vindicia`.

3. Call `vindicia.setup()` with options.

   Call the `vindicia.setup()` method to initialize the Hosted Fields on your checkout form.

   **Note:** `vindicia.setup()` will take over the submit event in your form. To validate, you will need to pass in some options to the `setup()` method.

   ```

    <script>
    vindicia.setup(options);
    </script>
   ```

## A vindicia.setup() example

This Javascript example uses all the optional features of the `vindicia.setup()` method. It initializes the Hosted Fields on the checkout form.

See [section below](#section_xlr_dd5_hsb) for explanations of the values to use.

```

<script>
vindicia.setup({
 // Options
 formId: "mainForm", // dom element of form
 vindiciaAuthId: "aXdjbF9vbmVfdGltZV9sb2dpbjpBTG5JWnRjZA==", 
// Authorization: Basic header"
 vindiciaServer: "secure.qa.vindicia.com", 
// Server to load the iframes from
 vindiciaRestServer: "api.qa.vindicia.com", 
// REST server to which to submit JSON
useCardinal3ds: false, 
// optional, if true (using cardinal 3DS) then loads songbird.js, default is false
useCardinalStaging: false, 
//optional, true means Cardinal Staging environment, default is false, which means use Cardinal Production environment
 hostedFields: {
 cardNumber: {
 selector: "#vin_credit_card_account",
 placeholder: "Enter Credit Card Number", // optional field
 autocomplete: "cc-number", // optional field
 formatinput: true, // optional field
 maskinput: false // optional field, show asterisks
 },
 expirationDate: {
 selector: "#vin_credit_card_expiration_date",
 placeholder: "Expiration Date MM/YY", // optional field
 format: "MM/YY", // optional, default is MM/YY. 
 Supported formats are: 
 MM/YY, MM-YY, MMYY, YY/MM, YY-MM, YYMM, YYYY/MM, YYYY-MM, YYYYMM, MM/YYYY, MM-YYYY, MMYYYY
 formatinput: true, // optional field
 autocomplete: "cc-exp" // optional field
 },
 cvn: { 
 selector: "#vin_credit_card_cvn",
 placeholder: "Enter CVV", // optional field
 autocomplete: "cc-csc", // optional field
 maskinput: false // optional field, show asterisks 
 },
 styles: { // optional
 "input": {
 "width": "100%",
 "font-family": "'Helvetica Neue',Helvetica,Arial,sans-serif",
 "font-size": "14px",
 "color": "#555",
 "height": "34px",
 "padding": "6px 12px",
 "margin": "5px 0px",
 "line-height": "1.42857",
 "border": "1px solid #ccc",
 "border-radius": "4px",
 "box-shadow": "0px 1px 1px rgba(0,0,0,0.075) inset",
 "-webkit-transition": "border-color 0.15s ease-in-out 0s, box-shadow 0.15s ease-in-out 0s",
 "transition": "border-color 0.15s ease-in-out 0s, box-shadow 0.15s ease-in-out 0s",
 },
 ":focus": { // optional
 "border-color": "#66afe9",
 "outline": "0",
 "-webkit-box-shadow": "inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6)",
 "box-shadow": "inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6)"
 },
 ".valid": { // optional
 "border-color": "#228B22",
 },
 ".notValid": { // optional
 "border-color": "#ff0000",
 },
 // custom width
 "@media (min-width: 600px) and (max-width: 800px)": {
 "input": {
 "font-size": "16pt"
 }
 }
 },
 },
 iframeHeightPadding: 2, // optional
 onSubmitEvent: function(myform) {
 return merchantValidate(myform); 
 // this method is for your use to interact with the user when the user 
    presses the Submit button. 
    It’s for when you want to validate the fields for yourself.
 },
 onSubmitCompleteEvent: function(data) {
 $('#message').html('submitted OK, vid ' + data.detail.vid);
 return;
 },
 onSubmitCompleteFailedEvent: function(data) {
 console.log(data.detail.status);
 if (data.detail.error) {
 console.log(data.detail.error);
 }
 return true; 
 // this method is for your use to interact with the user when the user 
    presses the Submit button and the payment method is not created.
 },
 onVindiciaFieldEvent: function(event) { // optional
 if (event.detail.fieldType == 'cardNumber') {
 setCardImage(event.detail.cardType); 
 // bright or dim the card logo. 
    This function is just an example of what you can do with this event. 
 }
 if (event.detail.fieldType == 'expirationDate') {
 if (!event.detail.isValid)
 {
 console.log('vindiciaEvent expirationDate not valid');
 }
 }
 if (event.detail.fieldType == 'cvn') {
 if (!event.detail.isValid)
 {
 console.log('vindiciaEvent cvn not valid');
 }
 console.log('cvn length ' + event.detail.dataLength);
 }
 }
});

// optional function shown being used above
function setCardImage(type)
{
 // use a sprite to display one credit card logo brightly with JQuery
 if (type == 'visa' || type == 'visa_electron') { 
 $('#ccTypeImage').css('background-position', '0 -23px'); 
 } 
 else if (type == 'mastercard' || type == 'maestro') {
 $('#ccTypeImage').css('background-position', '0 -46px'); 
 } 
 else if (type == 'amex') {
 $('#ccTypeImage').css('background-position', '0 -69px'); 
 } 
 else if (type == 'discover') {
 $('#ccTypeImage').css('background-position', '0 -92px'); 
 } 
 else {
 $('#ccTypeImage').css('background-position', '0 0'); 
 } 
}
</script>
```

## vindicia.setup() options explained

This setup method takes one argument.

```

vindicia.setup(options);
```

| `formId:` | string | **Required**. This is the id of the form as shown below in this sample HTML: \`\` would look like this: `formId: mainform,` |
| - | - | - |
| `vindiciaAuthId:` | object | **Required**. This option is for your login. We use it for “Basic Authentication” `vindiciaAuthId: "aXdjbF9vbmVfdGltZV9sb2dpbjpBTG5JWnRjZA==", // Authorization: Basic header"` |
| `vindiciaServer:` | object | **Required**. This option specifies the location of vindicia REST server to create the payment method on. If not provided it will default to "api.vindicia.com" our production server. `vindiciaServer: "api.vindicia.com",` |
| `vindiciaRestServer:` | object | **Required**. This option specifies the location of vindicia REST server to create the payment method on. If not provided it will default to "api.vindicia.com" our production server. `vindiciaServer: "api.vindicia.com",` |
| useCardinal3ds: | boolean | **Optional**3DS is the Strong Customer Authentication payment standard for the European Economic Area. If you are using Cardinal Commerce 3DS, set this boolean to **true**. Default is false. |
| useCardinalStaging: | boolean | **Optional** This option is relevant only if the **useCardinal3ds** option is set to True. Set this to true if you are using the Cardinal Commerce Staging environment. If you are using the Cardinal Commerce Production environment leave as false. Default is false. |
| `hostedFields:` | object | **Required**. This option has many subobjects, one for each input field that you want protected and a "styles" sub object. Not all input fields need to be protected. `hostedFields: &#123; cardnumber: &#123; selector: "#vin_credit_card_account", // required placeholder: "Enter Credit Card Number" // optional field &#125;, expirationDate: &#123; selector: "#vin_credit_card_expiration_date", // required placeholder: "Expiration Date MM/YY", // optional field format: "MM/YY" // optional, defaults to MM/YY &#125;, cvn: &#123; selector: "#vin_credit_card_cvn", placeholder: "Enter CVV" // optional field &#125;, styles: &#123; "input": &#123; "width": "100%", "font-family": "'Helvetica Neue',Helvetica,Arial,sans-serif", "font-size": "14px" &#125; &#125; &#125;,` Or if you want the month and year separated but still show as input boxes: `hostedFields: &#123; cardnumber: &#123; selector: "#vin_credit_card_account", // required placeholder: "Enter Credit Card Number" // optional field &#125;, expirationMonthInput: &#123; selector: "#vin_credit_card_expiration_month", placeholder: 'Month', format: "MM" // only "MM" is supported &#125;, expirationYearInput: &#123; selector: "#vin_credit_card_expiration_year", placeholder: 'Year', format: "YYYY" // "YY" or "YYYY", defaults to YYYY &#125;, cvn: &#123; selector: "#vin_credit_card_cvn", placeholder: "Enter CVV" // optional field` Or if you want month and year as dropdown boxes: `hostedFields: &#123; cardnumber: &#123; selector: "#vin_credit_card_account", // required placeholder: "Enter Credit Card Number" // optional field &#125;, expirationMonth: &#123; // optional dropdown field or use expirationDate selector: "#vin_credit_card_expiration_month", format: "N - A" // "N", "A", "N A", "N-A", "N - A" where (N=numeric month, A=Alphabetic Month) &#125;, expirationYear: &#123; selector: "#vin_credit_card_expiration_year"format: "YYYY" // "YY" or "YYYY", defaults to YYYY &#125;, cvn: &#123; selector: "#vin_credit_card_cvn", placeholder: "Enter CVV" // optional field&#125;, styles: &#123; "input": &#123; "width": "100%", "font-family": "'Helvetica Neue',Helvetica,Arial,sans-serif", "font-size": "14px" &#125; &#125;&#125;,` Multi-language support for month and year dropdown boxes: `hostedFields: &#123; cardNumber: &#123; // required selector: "#vin_credit_card_account", placeholder: "Entrez le numéro de carte de crédit" // optional field &#125;, expirationMonth: &#123; // optional dropdown field or use expiration_date selector: "#vin_credit_card_expiration_month", language: "fr", // en, es, de, pt, fr, zh, ja only allowed format: "N - A" // "N", "A", "N A", "N-A", "N - A" where (N=numeric month, A=Alphabetic Month) &#125;, expirationYear: &#123; // optional dropdown field or use expiration_date selector: "#vin_credit_card_expiration_year", language: "fr", // en, es, de, pt, fr, zh, ja only allowed format: "YYYY" // or "YYYY", defaults to YYYY &#125;, cvn: &#123; selector: "#vin_credit_card_cvn", placeholder: "Entrez CVV" // optional field &#125;, styles: &#123; "input": &#123; "width": "100%", "font-family": "'Helvetica Neue',Helvetica,Arial,sans-serif", "font-size": "14px" &#125; &#125; &#125;,` |
| `iframeHeightPadding:` | Optional, integer | During vindicia.setup() the iframe height is adjusted to be the size of the input field inside of it. If any styles require some extra padding around the input box, this is the way to get that. It defaults to zero. iframeHeightPadding: 3, |
| `onSubmitEvent:` | Optional, function | This is where you will define a function to be executed when the submit button is pressed. It can be anonymous or a call to an existing function. `onSubmitEvent: function(myform) &#123; return merchantValidate(myform); &#125;,` |
| `onSubmitCompleteEvent:` | Optional, function | Us this callback function when the REST server successfully creates a payment method. Status returned is 200. `onSubmitCompleteEvent: function(data) &#123; $('#message').html('submitted OK'); return true; &#125;,` |
| `onSubmitCompleteFailedEvent:` | Optional, function | This is where you will define a function to be executed when the submit to the REST server fails. It can be anonymous or a call to an existing function. The most common reasons for failure are:- OneTimeLogin token is being reused and rejected - The vin\_id specified in the form is already in our system and cannot be reused `onSubmitEvent: function(myform) &#123; return merchantValidate(myform); &#125;,` |
| `onVindiciaFieldEvent:` | Optional, function | To have access to real time validation of iframe blur events, use this optional handler `onVindiciaFieldEvent: function(event) &#123; // optional if (event.detail.fieldType == 'expirationDate') &#123; if (!event.detail.isValid) &#123; console.log('vindiciaEvent expirationDate not valid'); &#125; &#125;` |
| **The following properties are supported in hosted fields:** | | |
| `cardNumber` | | A required field. `cardNumber: &#123; selector: "#vin_credit_card_account", placeholder: "Enter Credit Card Number" // optional field &#125;,` |
| `expirationDate` | | A required field unless you use the optional fields below for separating the month and year as dropdowns or input boxes.Supported formats include the following: - MM/YY' (the default) - 'MM-YY' - 'MMYY - YY/MM'' - 'YY-MM' - 'YYMM' - 'YYYY/MM' - 'YYYY-MM' - 'YYYYMM' - 'MM/YYYY' - 'MM-YYYY' - 'MMYYYY' `expirationDate: &#123; selector: "#vin_credit_card_expiration_date", placeholder: "Expiration Date MM/YY", // optional field format: "MM/YY" // defaults to MM/YY &#125;,` |
| `cvn` | | `cvn: &#123; selector: "#vin_credit_card_cvn", placeholder: "Enter CVV" // optional field &#125;,` |
| `expirationMonth` | | Optional field to show a dropdown box for the month. `expirationMonth: &#123; selector: "#vin_credit_card_expiration_month", language: "fr", // en, es, de, fr, ja, pt, zh only allowed format: "N - A" // "N", "A", "N A", "N-A", "N - A" where (N=numeric month, A=Alphabetic Month) &#125;,` The `language` is optional and defaults to “en” English. All the month names will display in the chosen language. Currently only en (English),es (Spanish), de (German), fr, (French),ja (Japanese), pt (Portuguese), zh (Chinese) are supported . |
| `expirationYear` | | Optional field to show a dropdown box for the year. `expirationYear: &#123; selector: "#vin_credit_card_expiration_year", language: "fr", // en, es, de, fr, ja, pt, zh only allowed format: "YYYY" // or "YYYY", defaults to YYYY &#125;,` The `language` is optional and defaults to “en” English. All the month names will display in the chosen language. Currently only en (English), es (Spanish), de (German), fr , (French), ja (Japanese), pt (Portuguese), zh (Chinese) are supported. |
| `expirationMonthInput` | | Optional field to show an input box for the month. `expirationMonthInput: &#123; selector: "#vin_credit_card_expiration_month", placeholder: 'Month', format: "MM" // only "MM" is supported &#125;` |
| `expirationYearInput` | | Optional field to show an input box for the year. `expirationYearInput: &#123; selector: "#vin_credit_card_expiration_year", placeholder: 'Year', format: "YYYY" // "YY" or "YYYY", defaults to YYYY &#125;,` |
| Notes on some parameters | | |
| `autocomplete` | | An `optional` parameter describing the following fields for `cardNumber`, `expirationDate`, `cvn`. `cardNumber: &#123; selector: "#vin_credit_card_account", autocomplete: "cc-number"&#125;,expirationDate: &#123; selector: "#vin_credit_card_expiration_date", // required autocomplete: "cc-exp" // optional field &#125;,cvn: &#123; selector: "#vin_credit_card_cvn", autocomplete: ‘"cc-csc" // optional field&#125;,` Set this parameter to `off` or `cc-number`. When you set it to `cc-number` you take advantage of the Chrome Autofill feature. If you store credit cards in your browser, this feature will assist the user when entering the stored credit card number. |
| `formatinput` | | An optional parameter describing the following fields for cardNumber or expirationDate. `cardNumber: &#123; selector: "#vin_credit_card_account", formatinput: true // optional field, force spacing &#125;, expirationDate: &#123; selector: "#vin_credit_card_expiration_date", // required format: "MM/YYYY", // optional, defaults to MM/YY formatinput: true // optional field, force slash in proper location &#125;,` This parameter can be either `true` or `false`. If you set it to true, the browser will add spaces, slashes or dashes when the numbers are typed in. This makes for fewer mistakes and is easier to read. For `expirationDate`, above, a slash is automatically inserted when the user types the third number--if the user did not type a slash. |
| `maskinput` | | An `optional` parameter describing the following fields for cardNumber or cvn. `cardNumber: &#123; selector: "#vin_credit_card_account", maskinput: false // optional password like field &#125;, expirationDate: &#123; selector: "#vin_credit_card_expiration_date", // required maskinput: false // optional password like field &#125;,` This parameter can be `true` or `false`. Set it to true to have the browser show asterisks (as in a password field) as the user types. |

## Understanding onVindiciaFieldEvent

Events happen inside the iframes that the parent window will not be allowed to respond to. The onVindiciaFieldEvent property can be used to call your function when validation events are happening inside the iframes.

```
onVindiciaFieldEvent: function(event) {
 if (event.detail.fieldType == 'cardNumber') {
 setCardImage(event.detail.cardType); // bright or dim the card logo
 }
 if (event.detail.fieldType == 'expirationDate') {
 if (!event.detail.isValid)
 {
 alert('vindiciaFieldEvent expirationDate not valid');
 }
 }
 if (event.detail.fieldType == 'cvn') {
 if (!event.detail.isValid && event.detail.dataLength > 0)
 {
 alert('vindiciaFieldEvent cvn not valid');
 }
 }
 }
```

| `event.detail.fieldType` | | `cardNumber, expirationDate, cvn.` `cardNumber: validation by luhn check, forces all numeric characters, returns a cardType if detectable.` `expirationDate: Defaults to mm/yy format for validation. But you may specify another format.` `If separate date fields validation is configured for month and year (expirationMonthInput expirationYearInput), validation is performed on the individual fields as well as the combined field values (month and year) for expirationDate validation.` `cvn: Forces all numeric characters, is valid if character count is 3 or 4.` |
| - | - | - |
| `event.detail.cardType` | | A string property that will hold the short name of the card type that was detected. Only on events with fieldType `cardNumber`. The following values are supported: - amex - dankort - diners\_club\_carte\_blanche - diners\_club\_international - discover - jcb - laser - visa\_electron - visa - mastercard - maestro |
| `event.detail.isValid` | | `A boolean value. True means the data is acceptable and can be submitted. False means that it is illegal or incomplete.` |
| `event.detail.dataLength` | | `This method will return an integer that is a count of characters inside the input box in the iframe. An blank field returns zero.` |

## Merchant FormValidation

When the user clicks the submit button on the form, you will want to validate the form fields before allowing the submit action to complete.

Since the vindicia object took over the submit event when you called `vindicia.setup(),` you cannot call a validation function directly. Instead your validation function will be called for you. Specify the validation function in vindicia.setup like this:

```

 hostedFields: {
 onSubmitEvent: function(event) {
 return merchantValidate();
 },
},
```

Your validation function should look like this:

```

function merchantValidate(theform)
{
 if (!vindicia.isValid('vin_account_holder'))
 {
 alert('Please enter card holders name before submitting');
 return false;
 }
 if (!vindicia.isValid('vin_credit_card_account'))
 {
 alert('Please enter a valid credit card before submitting');
 return false;
 }
 else
 {
 if (!vindicia.isValid('vin_credit_card_expiration_date'))
 {
 alert('Please enter a valid expiration date YYYYMM before submitting');
 return false;
 }
 }

 if (theform.vin_account_holder.value.length == 0)
 {
 // non sensitive field
 alert('Please enter the Account Name');
 return false;
 }

 if (vindicia.cardType == "amex" && vindicia.dataLength('vin_credit_card_cvn') != 4)
 {
 alert("American Express CVV must be 4 digits");
 return false
 }
 return true;
}
```

You must return true or false.

## Iframe Validation

Even though access to iframe field data is restricted, you can validate these iframe fields in the following ways:

* Inside your own merchant validation function at submit
* As blur events happen when the user tabs (moves) from field to field in the iframes
* As a call to vindicia.isValid() for all iframes or for a single iframe
* Just let the submit button block naturally if the iframe fields are not filled in with valid data

Methods to help you validate iframe fields

| `vindicia.cardType` | `A string property that will hold the short name of the card type that was detected.` `Supported values include the following:` - amex - dankort - diners\_club\_carte\_blanche - diners\_club\_international - discover - jcb - laser - visa\_electron - visa - mastercard - maestro |
| - | - |
| `vindicia.dataLength(selector)` | This method will return a positive integer that is a count of characters inside the input box in the iframe. If no selector is specified or if the selector cannot find the iframe it will return zero. This is very useful when detecting if the card is an "amex" and in the CVV field four numbers were entered `if (vindicia.cardType == "amex" && vindicia.dataLength('vin_credit_card_cvn') != 4) &#123; alert("American Express CVV must be 4 numbers"); return false &#125;` |
| `vindicia.isValid(selector)` | This method will return true or false if the input box in the iframe is valid. If no selector is specified it will return true if all iframes are valid and false if at least one iframe is not valid. `if (!vindicia.isValid('vin_credit_card_account')) &#123; alert('Please enter a valid credit card before submitting'); return false &#125;` |

```

Accessing onblur events with vindicia.setup()
onVindiciaFieldEvent: function(event) {
 if (event.detail.fieldType == 'cardNumber') {
 setCardImage(event.detail.cardType); // bright or dim the card logo
 }
 if (event.detail.fieldType == 'expirationDate') {
 if (!event.detail.isValid)
 {
 console.log('vindiciaFieldEvent expirationDate not valid');
 }
 }
 },
```

## Credit Card Validation

PMT automatically attempts to validate the credit card on submit. To turn off this functionality, place a hidden field in the form as follows:

```

<input name="vin_validate" value="0" type="hidden" />
```

As of version 24.0.0, PMT accepts the following additional form fields for validation:

* `<input name="vin_ignore_avs_policy" value="1" type="hidden" />`
* `<input name="vin_ignore_avs_policy" value="1" type="hidden" />`
* `<input name="vin_ignore_cvn_policy" value="1" type="hidden" />`
* `<input name="vin_min_chargeback_probability" value="100" type="hidden" />`
* `<input name="vin_source_ip" value="12.243.28.45" type="hidden" />`
* `<input name="vin_currency" value="EUR" type="hidden" />`

**Note:** `vin_min_chargeback_probability` is a percentage from 0 to 100. Must be integers, or it will be ignored.

The preferred currency to use for validation is `vin_currency`. If not specified, the default merchant currency will be used. If a merchant currency is not set, defaults to USD.

**Behind The Scenes**

When the PMT form submits, the REST endpoint POST to [https://api.vindicia.com/payment\\\_methods](https://api.vindicia.com/payment\\_methods) sends this JSON snippet:

```

policy: {
 validate: 0
}
```

The default value is 1. Therefore if you want validation, do not include the hidden field `vin_validate`.

## The vindicia object: properties and methods

The following properties are supported

| vindicia.setup | | The heart of this Hosted Fields system. It is described in more detail on its own page. But basically calling this function will take over the submit event and create iframes on your checkout page. Each iframe represents a single input field such as credit card number or expiration date. |
| - | - | - |
| vindicia.cardType | | A string property that will hold the short name of the card type that was detected. Supported values: - amex - dankort - diners\_club\_carte\_blanche - diners\_club\_international - discover - jcb - laser - visa\_electron - visa - mastercard - maestro |
| vindicia.dataLength(selector) | | This method will return an integer that is a count of characters inside the input box in the iframe. If no selector is specified or if the selector cannot find the iframe it will return zero. The following is very useful when detecting if the card is an AMEX and if four numbers were entered in the CVV field: `if (vindicia.cardType == "amex" && vindicia.dataLength('vin_credit_card_cvn') != 4) &#123; alert("American Express CVV must be 4 numbers"); return false &#125;` |
| vindicia.isValid(selector) | | This method will return a boolean value that confirms whether the input box in the iframe is valid. If no selector is specified, it checks all the iframe input boxes and returns true if all are valid and false if at least one is not valid. `if (!vindicia.isValid('vin_credit_card_account')) &#123; alert('Please enter a valid credit card before submitting'); &#125;` |
| vindicia.isSynced() | | This method will return true if all the iframes have reported their data to the master iframe and the notified the parent window its validity status. This method will return false if at least one iframe has not reported its data. The reporting happens normally during an onblur or onchange event. This method was added to help work around a Mobile Safari bug where blur events were not consistently being triggered. |
| vindicia.clearSyncFlags() | | This method will set all iframe sync values to false. It is usually called right before `syncData()`. This method is also used on submit to ensure any changes in the form fields are captured. |
| vindicia.syncData() | | This method will send a message to each iframe to simulate a blur event. This will cause the iframes to validate their form data, send the results to the master iframe, and notify the parent window for validation purposes. This method was added to help work around a Mobile Safari bug where blur events were not consistently being triggered. See also `isSynced()` and `clearSyncFlags().` This method is also used on submit to ensure all changes in the form fields are captured. `setInterval(function() &#123; vindicia.syncData(); &#125;,300);` |
| vindicia.clearData() | | This method clear the data in the iframes input boxes and dropdown boxes It will also clear the sync flags and isValid flags. It will also set isComplete() to false |
| vindicia.isLoaded() | | This method returns true if all the iframes have reported to the parent window that they are loaded. We use this when dealing with a Chrome bug that allows random focus and blur events occur during iframe loading. |
| vindicia.resetCompleteStatus() | | This method will set `isComplete()` to false. This allows the submit button to initiate a POST again. This is useful in single page apps where you do not want to call `clearData()`. |
| vindicia.submit() | | This method will cause the form to begin its three phase submit directly to vindicia.com, as long as the data is valid. Not required for normal operation. |
| `vindicia.isComplete()` | | This method will return true if the form has already been submitted to Vindicia. Not required for normal operation. |
| `vindicia.onSubmitComplete(event)` | | This method will indicate that you would like to use AJAX to submit the fields. This method will be called when the POST is finished. Optional. `onSubmitCompleteEvent: function(event) &#123; $('#message').html('submitted OK, vid ' + event.detail.vid); return; &#125;,` |

## Allowed Expiration Date Format

When passing in format via the `setup()` method, we only allow these specific month and year formats:

* MM/YY
* MM-YY
* MMYY
* YY/MM
* YY-MM
* YYMM
* YYYY/MM
* YYYY-MM
* YYYYMM
* MM/YYYY
* MM-YYYY
* MMYYYY

The default format is MM/YY. Any other format will cause a browser error.

## Allowed Styles in vindicia.setup() iframes

When passing in styles via the `setup()` method, we only allow these specific tags:

* color
* width
* height
* border
* border-color
* border-radius
* font
* font-family
* font-size
* font-size-adjust
* font-stretch
* font-style
* font-variant
* font-variant-alternates
* font-variant-caps
* font-variant-east-asian
* font-variant-ligatures
* font-variant-numeric
* font-weight
* line-height
* opacity
* outline
* padding
* margin
* text-shadow
* box-shadow
* -webkit-box-shadow
* -moz-osx-font-smoothing
* -moz-transition
* -webkit-font-smoothing
* -webkit-transition
* transition

All other styles will be ignored. Attempts to send script tags in values will also be ignored.

## PCI Compliance Notes

SAQ A compliance: “Card-not-present merchants (e-commerce or mail/telephone-order) that have fully outsourced all cardholder data functions to PCI DSS validated third-party service providers, with no electronic storage, processing, or transmission of any cardholder data on the merchant’s systems or premises.”

Vindicia Hosted Fields satisfies the above definition of PCI SAQ A compliance through the use of iframes and browser standard security known as same origin policy.

## Browser Security Notes

Same Origin Policy dictates that browser windows and iframes cannot share data unless they are loaded from the same domain (origin). This standard security policy in web browsers allows Vindicia to leverage SAQ A PCI compliance for the merchant checkout page.

Sensitive credit card fields are loaded in iframes from a Vindicia web server while the other non-sensitive checkout form fields are loaded in the parent window (from the merchant web server). Each iframe represents one secure input box.

This will cause the browser to block direct access to the data in each of these iframes. However messages (events) are allowed to be passed back and forth between them. This is how Vindicia coordinates the submit button on the parent window to allow iframes to submit their data.

## Internal Notes

Custom field Luhn check

If the merchant has set up any custom fields (custom iframes) using the `vindicia.setup()` method, we must ensure that no credit cards have been entered there. Therefore at each blur event on a custom field, we perform a Luhn check. If the field passes the Luhn check, the field is not valid.

**When the Submit button Is pressed**

A three phase submit has been implemented to coordinate the parent window submit event with the iframes.

Phase 1: Collect iframe data for the last time.

* If the form fields pass validation, let the submit process begin.
* Set `submitFormRequest` flag to true to trigger phase two when ready.
* Call `clearSyncFlags()` which resets all iframe sync flags to false.
* Call `syncData()` which sends events to each iframe to simulate a blur event.
* When the last iframe sends its data to the master iframe, go to phase 2.

Phase 2: AJAX POST to Vindicia REST server from master iframe.

* If all the data in all the iframes is valid, the parent window collects all the non iframe form fields.
* This data is sent to the master iframe with a `submitForm` event
* The master iframe performs an AJAX POST, with all its collected data in JSON format, back to the Vindicia REST server.

Phase 3: After form submission activity.

* Master iframe sends `submitComplete` event to parent window after the AJAX POST finishes.
* This triggers the event `onSubmitCompleteEvent` to invoke merchant application Javascript.

**Methods of interest**

**The vindicia object has a some interesting and useful methods.**

| vindicia.isValid(tagId) | Programmer Ok | Return boolean value of last reported onblur validation state. If tagId is not provided it returns true if all iframes are holding valid data, false otherwise. |
| - | - | - |
| vindicia.isSynced(tagId) | Programmer Ok | Return boolean value about if iframe(s) ever reported in on its data. If tagId is not provided it returns true if all iframes have reported in, false otherwise. Note: if isValid is true then isSynced is true but not vice versa. |
| vindicia.clearSyncFlags() | Programmer Ok | Set all sync flags to false for every iframe, master iframe is ignored. Use this with syncData() |
| vindicia.syncData() | Programmer Ok | Send an event (`"syncData"`) to all iframes to simulate a blur event. This causes each iframe to do 3 things: validate its data, send it to the master iframe and notify the parent window (`"validationReport"` event) with its validation boolean state. The parent window is also given the iframe's data length but not its value. |
| vindicia.clearData() | Programmer Ok | This method clear the data in the iframes input boxes and dropdown boxes It will also clear the sync flags and isValid flags. It will also set `isComplete()` to false |
| vindicia.resetCompleteStatus() | Programmer Ok | This method will set `isComplete()` to false. This allows the submit button to initiate a POST again. This is useful in single page apps where you do not want to call `clearData().` |
| vindicia.dataLength(tagId) | Programmer Ok | Return an integer count of the length of the input field in the tagId iframe. |
| vindicia.getForm() | Programmer Ok | Return string value of the name of the form from parent window that this object has taken over the submit event on. |
| vindicia.isComplete(tagId) | Programmer Ok | Return boolean value of last reported state of tagId iframe submission. If tagId is not provided it returns true if all iframes have completed submission of their forms, false otherwise. Not typically needed by merchant Javascript programmer. |
| vindicia.submit() | Internal use only | Begin the three phase submit of the form back to Vindicia. All iframes send data to the master iframe one last time. If not all iframe data is currently valid then just return false. |
| vindicia.submit2() | Internal use only | If all iframe data is valid, send master iframe the rest of the form fields from parent window and master iframe will perform a POST back to Vindicia with entire set of data. |

## **Using AJAX with PMT, the easy way**

If you want AJAX to be used, include the following element onSubmitCompleteEvent in your setup method. Control will return to you after the POST.

```
onSubmitCompleteEvent: function(data) {
 $('#message').html('submitted OK, vid ' + data.detail.vid);
 return;
},
```

If this element is missing, the URL returned from the POST will cause the browser to redirect there.

## **Using AJAX with PMT, the single page app**

If you want a single page app, keep in mind that after the POST the One time login string will be invalid. You are allowed only one POST per one-time login. You will have to refresh the one-time login value and the signed value in the hidden fields in the form.

You must then call `vindicia.clearData()` to notify the iframes to reset their data. This also unblocks the submit button.

```
var newOTL = getNewOTL();
$('#vin_session_id').value = newOTL.unsignedValue;
$('#vin_session_hash').value = newOTL.signedValue;
vindicia.clearData();
```

**Note:** `getNewOTL()` is a Javascript method you will have to write that makes an ajax request back to your merchant server.

This is useful if you want to allow the user to enter more than one payment method. You do not have to refresh the OTL value on failed POSTs to the REST server.

## Basic PMT setup

Use the following procedure to set up PMT.

1. **Include the vindicia javascript file in your page near the bottom.**`<script src="https://cdn2.vindicia.com/ws/vindicia.js"></script>`

2. **Generate a new one time login string.**

   Choose a unique string we call a `session id` and place it into the form as a hidden field. This string will be used in the payment method object that gets created on form submission. This is your unique identifier.

   Using your OTL HMAC key that Vindicia provided for you, create a hash using HMAC256 or HMAC512. The format of this string is:

   ```

   session_id#POST#/payment_methods
   #! /usr/bin/perl
   use Digest::SHA qw(hmac_sha256_hex hmac_sha512_hex);

   my $otl_hmac_key = 'Zb7xZDtJ4OEHNZ9-Y7IjvPYn4N4';
   my $session_id = "CC4930533";
   my $session = "$session_id#POST#/payment_methods";
   my $session_hash = hmac_sha256_hex($session, $otl_hmac_key);
   print "session_id: $session_id\n";
   print "session_hash: $session_hash\n";
   Results of the above Perl program run would be:

   session_id: CC4930533
   session_hash: 511e8c48bc8b1caa9def400fc11975391440e0c72fc9c630edafb8d46691aec5

   Vindicia Customer Service runs util/one_time_login_admin.pl to setup the secret key
   ```

3. **Create a form with some hidden fields.**

   Two forms are required.

   ```

   <h3>Credit/Debit Card Information</h3>
   <form id="mainForm" name="mainForm" class="form-horizontal" method="post">
   <input name="vin_session_id" value="CC403930495" type="hidden" />
   <input name="vin_session_hash" value="4812242031705a2413c6363067298bb3023b474084d11614c89a089f6ba35ec9" type="hidden" />
   ```

4. **Create some input fields not requiring protection.**

   ```

   <input type="text" id="vin_account_holder" name="vin_account_holder" placeholder="Enter Cardholder Name" value=""/>
   <input type="text" id="vin_billing_address_line1" name="vin_billing_address_line1" placeholder="Enter Address Line 1" value=""/>
   <input type="text" id="vin_billing_address_line2" name="vin_billing_address_line2" placeholder="Enter Address Line 2" value=""/>
   <input type="text" id="vin_billing_address_line3" name="vin_billing_address_line3" placeholder="Enter Address Line 3" value=""/>
   <input type="text" id="vin_billing_address_city" name="vin_billing_address_city" placeholder="Enter City" value=""/>
   <input type="text" id="vin_billing_address_district" name="vin_billing_address_district" placeholder="Enter State" value=""/>
   <input type="text" id="vin_billing_address_postal_code" name="vin_billing_address_postal_code" placeholder="Enter Cardholder Name" value=""/>
   <input type="text" id="vin_billing_address_country" name="vin_billing_address_country" placeholder="Enter Country" value=""/>
   <input type="text" id="vin_billing_address_phone" name="vin_billing_address_phone" placeholder="Enter Phone Number" value=""/>
   ```

5. **Create some input fields requiring protection as div tags.**

   ```

   <div id="vin_credit_card_account"></div>
   <div id="vin_credit_card_expiration_date"></div>
   <div id="vin_credit_card_cvn"></div>
   ```

6. **Call vindicia.setup to establish the protected fields inside iframes.**

   ```

   <script type="text/javascript">
    vindicia.setup(options);
   </script>
   ```

**Using Javascript for HMAC hash**

See [Brix Github repository](https://github.com/brix/crypto-js) for more information.

```
<script>
 var otl_hmac_key = "A39485F85039D394059B948390";
 var vin_session_id = "CC403930495";
 var vin_session_hash = CryptoJS.HmacSHA512(vin_session_id + "#POST#/payment_methods", otl_hmac_key);
</script>
```

**vindiciaAuthId: basic auth generation**

Script

Get your login user and password from Vindicia. It must be a special, low privileged user.

```

vindicia.setup({
 formId: "mainForm",
 vindiciaAuthId: "cWEtZ1RsLTIwMTgtcTJfc19hcDpaR2FqNUl0azAwWHM2dVBJTEVoSGRqYVMzMHJjYWtLdQ==", // Authorization: Basic header"
```

Log in to the Basic Auth Generator, at [https://www.blitter.se/utils/basic-authentication-header-generator/](https://www.blitter.se/utils/basic-authentication-header-generator/)

## PMT Fields list

Allowed form field names for Payment Method Tokenization

| **Name in form** | **Type** | **Notes** |
| - | - | - |
| vin\_session\_id | input | Required and must be unique on each successful submit. |
| vin\_session\_hash | hidden | Required and must be unique on each successful submit. HMAC256 or HMAC512. |
| vin\_customer\_specified\_type | hidden | Optional |
| vin\_primary | hidden | Optional: true or false. |
| vin\_active | hidden | `Optional: true or false.` |
| vin\_billing\_address\_line1 | input | |
| vin\_billing\_address\_line2 | input | |
| vin\_billing\_address\_line3 | input | |
| vin\_billing\_address\_city | input | |
| vin\_billing\_address\_district | input | Also known as state in the US. |
| vin\_billing\_address\_postal\_code | input | |
| vin\_billing\_address\_country | input | |
| vin\_billing\_address\_phone | input | |
| vin\_credit\_card\_account | div | Required. The credit card number. |
| vin\_credit\_card\_expiration\_date | div | Not needed if month and year specified. |
| vin\_credit\_card\_expiration\_month | div | Not needed if date specified. Dropdown and input boxes are supported. |
| vin\_credit\_card\_expiration\_year | div | Not needed if date specified. Dropdown and input boxes are supported. |
| vin\_credit\_card\_cvn | div | |

## Notes on onSubmitCompleteFailedEvent

The callback function `onSubmitCompleteFailedEvent` is called when the Submit button does not return 200 (success) from the REST server.

```

 onSubmitCompleteFailedEvent: function(data) {
 $('#message').html('submitted Failed');
 console.log(data.detail.status); // server return code
 if (data.detail.error) {
 console.log(data.detail.error);
 }return true;
 },
```

If the `vindiciaAuthId` used in the `vindicia.setup()` call is not valid, a `403 Forbidden` is returned from the REST server. This might appear as a status code of zero in Javascript, depending on the browser.

You might receive a status code of 400 if:

* The id POSTed is already in use
* The onetime login is reused from a previous successful POST

For example:

400 PaymentMethod already exists: id="paymeth\_100"

would be returned from the REST server if you try to create a payment method with an existing payment method id.

## PMT Support for Single Page Application using REACT

Vindicia has created its own open source React.js library for merchant developers to use when integrating PMT into a React.js application. This library is available via GitHub. You can use the package managers yarn and npm.

Get more information and detailed instructions at [PMT Github repository](https://github.com/Vindicia/vindicia-pmt-react) .

**Parent topic:**[Payment Method Tokenization](../topics/ProgGuide_PMT)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.