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.
The steps for embedding the Secure Fields Form in your checkout page are the following:
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>
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>
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
})
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')
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)}`)
})
<!-- 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.
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.
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.
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)
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
}
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. |
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.
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,
...
})
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:
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:
1. Once a payment is attempted (not necessarily with success ), you will no longer be able to use the same token again. If you want to reuse a token so that customers do not need to repeatedly update their card information, you must first create a permanent token and then use the stored token in your payment requests. For more information, see Token API.
2. Tokenization does not validate the card, but merely provides a means to protect sensitive card data. For instance, the customer’s credit card could have expired or the card might have been cancelled. This will not be detected when the card information is tokenized, but will be reported back to you by the provider when you perform your first transaction.
3. The customer’s CVV code is part of the token.
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)
})
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.