Using the Secure Fields Form

Go to API V4 docs

Summary

  1. Introduction
  2. Embedding the Secure Fields form in a Checkout Page
    1. 2.1 Include Javascript SDK
    2. 2.2 Add a form with input elements
    3. 2.3 Create an instance of Secure Fields
    4. 2.4 Create instance for each field
    5. 2.5 Create a token
    6. 2.6 HTML and Javascript code example
  3. Secure Field instantiation options
  4. Secure Field types and options
  5. Options for instantiating fields
  6. Validating user input
  7. Additional methods
  8. Styling the Secure Fields form
    1. 8.1 Applying styles
    2. 8.2 Applying custom classes
  9. What you should know about tokenization?
  10. Installments. Receiving card information
  11. What’s next?

1. Introduction

The Secure Fields Form is an HTML form that you can include in your site to collect a user’s card information.

When using the Secure Fields Form, PayU generates the card details input fields and handles the logic of grabbing the card information submitted by the user. This option reduces your PCI scope, requiring you to be SAQ A compliant.

You can style the form as you desire. Here’s an example:

When card information is submitted through the Secure Fields Form, PayU returns a token representation of the card to your site. You must use the token when create and authorize a payment.

2. Embedding the Secure Fields Form in a Checkout Page

The steps for embedding the Secure Fields Form in your checkout page are the following:

2.1. Include the Secure Fields Javascript SDK in your checkout page

You can find the SDK at https://secure.payu.ro/pay/secure-fields/v1.js

<body>
    ...
    <script src="https://secure.payu.ro/pay/secure-fields/v1.js"></script>
    ...
</body>

2.2. Add a form with the input elements you want to show

The example below shows just three input fields: the credit card number, the credit card expiration date and the credit card cvv. Once you are familiar with the basic implementation steps, proceed to review the additional secure field types you can add.

<body>
    ...
    <form id="payment-form">
        <div class="field">
            <input id="cardholder-name" class="input" placeholder="John Doe" />
            <label>Name</label>
        </div>
        <div class="field">
            <div id="card-number" class="input"></div>
            <label>Card Number</label>
        </div>
        <div class="field">
            <div id="exp-date" class="input "></div>
            <label>Expiration Date</label>
        </div>
        <div class="field">
            <div id="cvv" class="input"></div>
            <label>CVV</label>
        </div>
        <button type="submit" id="pay_button">Pay $25</button>
    </form>
    ...
</body>

2.3. Now into some vanilla Javascript

Create an instance of the secure fields by calling PayUSecureFields.Init(auth, options?). As the first argument you can pass authentication data and the second argument you can pass a list of custom fonts to be applied to the fields (this is optional).
Obtaining a sessionId can be done using create session API. This is a server-to-server call and should be done before loading the payment page.
Attention: A session must be unique per successful authorization, so after a successful authorization with secure-fields you have to obtain a new session.

/*
Create an instance of the secure fields.
As the first argument, we'll pass details about merchantCode and sessionId (this is mandatory).
The second argument, we'll pass a list of custom fonts to be applied to the fields (this is optional).
*/
const auth = {
    merchantCode: 'PayU merchant code',
    sessionId: '79fac7b6-4f87-4a17-92b4-bffe16f68f1e',
}
const fonts = [
    {
        src: 'https://fonts.googleapis.com/css?family=Source+Code+Pro',
    }
]
const formElements = new PayUSecureFields.Init(auth, {
    fonts
})

2.4. Create an instance of each field you want to show in your form

You do this by calling formElements.create(elementType,options?) with the type of field you want to instantiate and any additional options you want pass in. Then mount each field to the DOM.

/*
The placeholders object is just one example of an additional option you can pass when instantiating a field.
*/
const placeholders = {
    cardNumber: '1234 1234 1234 1234',
    expDate: 'MM / YY',
    cvv: '123'
}

// Instantiate the fields you want to show and mount them to the DOM.
const cardNumber = formElements.create('cardNumber', {
    placeholders
})
cardNumber.mount('#card-number')

const expiry = formElements.create('creditCardExpiry', {
    placeholders
})
expiry.mount('#exp-date')

const cvv = formElements.create('cvv', {
    placeholders
})
cvv.mount('#cvv')

2.5. Create a token

Call PayUSecureFields.createToken() when the user submits the form, but not until you fetched the card holder’s name so that you can pass it in the additional data object (the card holder name is a required field, so you cannot skip this step).
Jump to tokenization details

document.getElementById('payment-form').addEventListener('submit', async(event) => {
    event.preventDefault()
    const additionalData = {
        holder_name: document.getElementById('cardholder-name').value // This field is mandatory
    }
    const result = await PayUSecureFields.createToken(cardNumber, {
        additionalData
    })
    console.log(`The response is ${JSON.stringify(result)}`)
})

2.6. Let’s piece it all together

<!-- This is your html file -->
<body>
    <div class="checkout-page">
        <form id="payment-form">
            <div class="field">
                <input id="cardholder-name" class="input" placeholder="John Doe" />
                <label>Name</label>
            </div>
            <div class="field">
                <div id="card-number" class="input"></div>
                <label>Card Number</label>
            </div>
            <div class="field">
                <div id="exp-date" class="input"></div>
                <label>Expiration Date</label>
            </div>
            <div class="field">
                <div id="cvv" class="input"></div>
                <label>CVV</label>
            </div>
            <button type="submit" id="pay_button">Pay $25</button>
        </form>
    </div>
    <script src="https://ro.payu.local/pay/secure-fields/v1.js"></script>
    <script src="index.js"></script>
</body>

// This is your index.js file

/*
Create an instance of the secure fields.
As the first argument, we'll pass authentication data (this is mandatory).
The second argument, we'll pass a list of custom fonts to be applied to the fields (this is optional).
*/
const auth = {
    merchantCode:'PayU merchant code',
    sessionId: '79fac7b6-4f87-4a17-92b4-bffe16f68f1e',
}
const fonts = [
    {
        src: 'https://fonts.googleapis.com/css?family=Source+Code+Pro',
    }
]
const formElements = new PayUSecureFields.Init(auth, {
    fonts
})

/*
Create an object holding additional options that you can pass to the constructor for instantiating
the credit card and card expiry fields.
There are lots of other options available that you can pass to the constructor,
but to keep it simple we'll just show this one object in our example.
*/
const placeholders = {
    cardNumber: '1234 1234 1234 1234',
    expDate: 'MM / YY',
    cvv: '123'
}

// Instantiate the fields you want to show and mount them to the DOM.
const cardNumber = formElements.create('cardNumber', {
    placeholders
})
cardNumber.mount('#card-number')

const expiry = formElements.create('creditCardExpiry', {
    placeholders
})
expiry.mount('#exp-date')

const cvv = formElements.create('cvv', {
    placeholders
})
cvv.mount('#cvv')

/*
Create a token when the user submits the form, but not until we fetched the card holder's name
so that we can pass it in an additional data object to the createToken call.
*/
document.getElementById('payment-form').addEventListener('submit', async(event) => {
    event.preventDefault()
    const additionalData = {
        holder_name: document.getElementById('cardholder-name').value // This field is mandatory
    }
    const result = await PayUSecureFields.createToken(cardNumber, additionalData)
    console.log(`The response is ${JSON.stringify(result)}`)
})

If an error occurred, the response would look like below:

{
    statusCode: CLIENT_ERROR,
    errors: {
        invalid_pan: 'Card number did not pass luhn validation',
        invalid_expiry: 'Card expiration date is invalid',
        client_error: 'Invalid expiryYear or expiryMonth'
    }
}

Possible error keys: invalid_pan | invalid_expiry | invalid_cvv | invalid_cardholder_name | client_error | server_error
Possible status codes: CLIENT_ERROR | SERVER_ERROR | SUCCESS

In case of success, the response would look like below:

{
    statusCode: SUCCESS,
    token: "fAMfv4qbHx0TU1FwwXroS2H+G3DSKkvrgYuuPaB1a1VpJgIg+WjGB2Bg==",
    cardUniqueIdentifier: "b1e6d93a154e375695ed87d7404d25e27923442e3f7b34afb5a8aff3de69cb64",
    cardExpirationDate: "2024-10-31",
    lastFourDigits: "1234"
}

Calling createToken method in a short interval of time, without waiting for the previous calls to get a response, will trigger a client error with the message "Overlapping request".
The role of this mechanism is to avoid duplicate submits by mistake, for example double clicks on the submit button.
In order to avoid this, during Secure Fields integration, the integrator should make sure that before doing a new tokenize request, the previous one received a response.

3. Secure Field Instantiation Options

When instantiating the secure fields using PayUSecureFields.Init(authData, options?), there are several options you can pass. These include a list of custom fonts, or the option to disable the Luhn check done on a card number. For a list of available options, see the Secure Fields Instantiation Options.

4. Secure Field Types and Options

When walking you through the steps required to embed the Secure Fields Form in a checkout page, we settled for a bare-bones example that showed you how to instantiate the credit card number and the card expiration date fields. You’re not limited however to just those two field types. For an overview of all available types, see the Form Elements (Fields) reference.

5. Options for Instantiating Fields

Recall that you instantiate a field using formElements.create(elementType, options?). The second argument to this call is an object holding additional options, such as a placeholder for the card number:

const placeholders = {
    cardNumber: '1234 1234 1234 1234'
}

const cardNumber = formElements.create('cardNumber', {
    placeholders
})

There are more options you can choose from, however. For an overview of all available options, see the Form Elements (Fields)

6. Validating User Input

The Secure Fields Form provides event listeners for events that are triggered when the value of an input field has been changed, if the field gets focus or if the field loses focus. They are called like so:

cardNumber.on('change', (event) => {
    console.log(event)
})

cardNumber.on('focus', (event) => {
    console.log(event)
})

cardNumber.on('blur', (event) => {
    console.log(event)
})

The event parameter of the callback function will return the following object (values are examples):

{
    bin: "510510", // the card's BIN
    complete: false, // wether the card data is inserted and valid
    brand: "visa", // only returned for card numbers
    empty: false, // whether the input field is empty
    error: undefined // an error object if an error occurred on input
}

7. Additional Methods

There are some additional methods that you can use on each type of input field (see the Form Elements (Fields) reference for a list of all available field types). Those methods are listed below.

Method Description
update(optionsObject) Updates the input field with the specified options. See the Form Elements (Fields) reference for a list of options you can pass.
clear() Clears the input field
destroy() Unmounts the input field from the DOM. You can call this method only once.
mount(containerSelector) Mount the input field to the DOM.

8. Styling the Secure Fields Form

You can style the secure fields by passing style or classes objects to the formElements.create(elementType,options?) call. The style object allows you to style the secure fields based on their state (valid input, no input or invalid input). The classes object allows you to apply custom classes to the div elements wrapping the input fields, giving you even more control over the styles that you can apply to the fields. Let’s take a look at both.

8.1. Applying Styles

Jump to the Style Objects Reference

The style object has nested objects, whose keys are the names of the input status (valid input, no input or invalid input) that a field can have. The exception is the nested base object; this object has styles that are inherited by the other objects. Here’s a basic example, with a base object and an invalid object holding styles to be applied if the user input is invalid:

const style = {
    base: {
        color: '#fff',
        fontWeight: 600,
        fontFamily: 'Quicksand, Open Sans, Segoe UI, sans-serif',
        fontSize: '16px',
        fontSmoothing: 'antialiased'
    },
    invalid: {
        color: '#FF0000'
    }
};

You can also add pseudo-classes and pseudo-elements for even more granular control over how the elements are styled:

const style = {
    base: {
        color: '#fff',
        fontWeight: 600,
        fontFamily: 'Quicksand, Open Sans, Segoe UI, sans-serif',
        fontSize: '16px',
        fontSmoothing: 'antialiased',
        ':focus': {
            color: '#424770',
        },

        '::placeholder': {
            color: '#9BACC8',
        },

        ':focus::placeholder': {
            color: '#CFD7DF',
        },
    },
    invalid: {
        color: '#FF0000',
        ':focus': {
            color: '#FA755A',
        },
        '::placeholder': {
            color: '#FFCCA5',
        },
    }
};

When you’re done styling, pass the style object to the formElements.create(elementType,options?) call like so:

const cardNumber = formElements.create('cardNumber', {
    style,
    ...
})

8.2. Applying Custom Classes

Jump to the Classes Object Reference

The classes object allows you to apply custom classes to the div elements wrapping the input fields, according to the input status (valid input, no input, invalid input or has focus) that a field can have. The classes object’s keys are the names of each type of input status. In the example below, we apply the my-own-invalid-class class to the input field if the user enters some invalid input.

const classes = {
    invalid: 'my-own-invalid-class'
}

Note
The div element to which the classes are applied, is the element you pass to the formElement.mount(containerSelector) call.


If you do not pass a classes object, then default class names are applied.

When you’re done styling, pass the classes object to the formElements.create(elementType,options?) call like so:

const cardNumber = formElements.create('cardNumber', {
    classes,
    ...
})

You can style the form as you desire. Here’s an example:

9. What you Should Know About One Time Use Tokenization

One time use tokenization is a process that safeguards sensitive card data, converting a card’s details to a representative token. When you tokenize a user’s card information, PayU returns a one time use token object.

There are three things you should know about tokenization:

10. Installments. Receiving card information

To be able to get information about the card, like Installment Options, the secure-fields solution fires a `cardInfo` event on which you can listen and get the information as soon as the user inputs their card number
To receive this data, you need to provide this intent when creating the cardNumber field (or when creating fullCreditCard, etc.) by adding `getCardInfo: true` into the options.

There are 2 responses that you can receive from the cardInfo event:

//1. the card info object (with the following structure)
{
    binNumber: string
    cardProfile: ?string
    cardProgram: ?string
    cardScheme: ?string
    cardType: ?string
    installmentOptions: ?string
    issuerBank: ?string
    issuerCountryCode: ?string
    paymentMethod: ?string
}

//2. an empty object {} for cases when the previous response has been invalidated. (e.g. the user deletes the card number after fully typing it)
{}

Below you can find an example of how to create a card field, with the option to receive cardInfo active:

const cardNumber = fields.create('cardNumber', {
    style,
    placeholders,
    classes: elementClasses,
    getCardInfo: true
})

//Subscribing to the cardInfo event is as follows:
cardNumber.on('cardInfo', (event) => {
    console.log('cardInfoEvent', event)
})

11. What's next?

Now that you’ve collected your customer’s card information, proceed to create and authorize a payment.
Pay attention to authorization.oneTimeUseToken node from the request body. There you must pass the token obtained during Secure Fields tokenization process along with the same sessionId used for the SDK initialization on which the token was created.

Last updated: 25th June, 2023