Basic example based on Bootstrap 3
The same UI/UX example like Example 1 but shows how to tackle RTL languages
- Include Client API Library into
<head>
section of your page - Put markup, designed by your own or corporate site template
- Initialize integration with
API key
, getHosted Fields Manager
and manage protected fields using simple API
<html lang="en">
<head>
...
<!-- Include Client API Library in your page -->
<script src="https://cdn.payme.io/hf/v1/hostedfields.js"></script>
...
</head>
<body>...</body>
</html>
<html lang="en">
<head>
<!-- Include Client API Library in your page -->
...
</head>
<body>
<!-- PAYMENT FORM STARTS HERE -->
<div class="container">
<div class="row">
<div class="col-xs-12 col-md-4">
<div class="panel panel-default credit-card-box">
<div class="panel-body">
<form role="form" id="checkout-form">
<div class="row">
<div class="col-xs-12 col-md-12">
<div class="form-group" id="card-number-group">
<label for="card-number-container" class="control-label">CARD NUMBER</label>
<!-- Container for Credit Card number field -->
<div id="card-number-container" class="form-control input-lg"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-7 col-md-7">
<div class="form-group" id="card-expiration-group">
<label for="card-expiration-container" class="control-label">EXPIRATION DATE</label>
<!-- Container for Credit Card expiration date field -->
<div id="card-expiration-container" class="form-control input-lg"></div>
</div>
</div>
<div class="col-xs-5 col-md-5 pull-right">
<div class="form-group" id="card-cvv-group">
<label for="card-cvv-container" class="control-label">CVV</label>
<!-- Container for Credit Card CVV field -->
<div id="card-cvv-container" class="form-control input-lg"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<!-- Form submit button -->
<button class="subscribe btn btn-success btn-lg btn-block" id="submit-button" disabled>
Pay 55.00 USD
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- PAYMENT FORM ENDS HERE -->
</body>
</html>
<html lang="en">
<head>
<!-- Include Client API Library into your page -->
...
</head>
<body>
<!-- PAYMENT FORM STARTS HERE -->
...
<!-- PAYMENT FORM ENDS HERE -->
<script>
var apiKey = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; // Merchant API key from Settings page in the dashboard
PayMe.create(apiKey, { testMode: true }).then(function (instance) {
var fields = instance.hostedFields();
var cardNumber = fields.create('cardNumber');
var expiration = fields.create('cardExpiration');
var cvc = fields.create('cvc');
cardNumber.mount('#card-number-container');
expiration.mount('#card-expiration-container');
cvc.mount('#card-cvv-container');
...
}).catch(function(error) {
// Instantiation error occurs
})
</script>
</body>
</html>
First of all you must get and provide your Merchant API key from Settings page in the dashboard.
...
// This is the Merchant API key (Test API key in this case)
var apiKey = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
...
Second, you must obtain integration instance for the merchant
var apiKey = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
// There is optional configuration object with testMode: true
// because we are using the API key from the test server
PayMe.create(apiKey, { testMode: true })
.then(function (instance) {
// Here we can work with successfully initialized
// integration instance - Integration Manager
...
})
.catch(function(error) {
// Here you can handle instantiation error
...
});
...
Property | Default value | Available values | Description |
---|---|---|---|
testMode |
false | true / false | Test mode - used to control in which environment payment will be processed |
language |
'en' | 'en' / 'he' | Language - controls the language of the messages and text direction (rtl or ltr). en (for English) or he (for Hebrew) |
tokenIsPermanent |
true | true / false | Token is permanent - Indicates whether it is a one-time-use or multi-use token |
Next step - to initialize integration type and get corresponding manager
Note
for now only one integration type is available - Hosted Fields
This integration type allows you to use secure way to collect user's sensitive data for making payments.
To obtain Hosted Fields manager you must call hostedFields
method
PayMe.create(key, { testMode: true })
.then(function (instance) {
// Getting Hosted Fields Integration manager
var fields = instance.hostedFields();
})
.catch(function(error) {
...
});
...
As soon as you have Hosted Fields Manager you are ready to create actual protected fields. You can create as many fields as you need but you can create each field type only once. It means if cardNumber
field was created you can't create more cardNumber
fields, but you can create payerEmail
and other (for details see Tokenization section).
For example let's create 3 most important fields
PayMe.create(key, { testMode: true })
.then(function (instance) {
var fields = instance.hostedFields();
// Hosted fields creation
var cardNumber = fields.create('cardNumber');
var expiration = fields.create('cardExpiration');
var cvc = fields.create('cvc');
})
.catch(function(error) {
...
});
...
Hint
We propose to create field names within
Payme.fields
object
// Instead of this
var cardNumber = fields.create('cardNumber');
// try to use this
var cardNumber = fields.create(PayMe.fields.NUMBER);
Having created all of necessary fields, they must be mounted to the chosen page place. Fields will be shown on your page only after mounting
PayMe.create(key, { testMode: true })
.then(function (instance) {
var fields = instance.hostedFields();
var cardNumber = fields.create('cardNumber');
var expiration = fields.create('cardExpiration');
var cvc = fields.create('cvc');
// Mount credit card inside container with id="card-number-container"
cardNumber.mount('#card-number-container');
...
})
.catch(function(error) {
...
});
...
Hint
Field instance
mount
method accepts any valid query selector
// Select by id
cardNumber.mount('#card-number-container');
// Select by class
cardNumber.mount('.credit-card-wrapper');
// Select by attribute
cardNumber.mount('[data-role="credit-card-input"]');
Hint
We can be notified on result by promise, because field mounting is an asynchronous process
...
// Mount credit card inside container with id="card-number-container"
cardNumber.mount('#card-number-container').then(function() {
// Field was mounted successfully
}).catch(function(error){
// There is error handling code
});
...
.create(field, options)
method accepts an optional second argument options
which is helpful in case you want to customize the field created with it.
var cardNumberFieldOptions = {
placeholder: 'Enter your Credit Card Number',
messages: {
required: 'The Credit Card Number is Required!',
invalid: 'Bad Credit Card Number'
},
styles: ... // see below
};
var cardNumber = fields.create('cardNumber', cardNumberFieldOptions);
The available properties for customization:
Property | Description |
---|---|
placeholder |
Placeholder text for empty field |
messages |
Validation messages object, can have required and invalid properties (see Field Event Object and Examples page) |
styles |
CSS properties for "protected" fields (see Field Styling) |
Because the "protected" fields are protected, you can use only limited CSS properties and selectors:
Whitelisted CSS properties:
color
font-size
text-align
letter-spacing
text-decoration
text-shadow
text-transform
Whitelisted CSS rules:
::placeholder
Allowed properties and selectors are organized into three groups within field creation configuration options.
var cardNumberFieldOptions = {
placeholder: 'Enter your Credit Card Number',
// ...
styles: {
base: {
'color': '#000000',
'::placeholder': {
color: '#F2F2F2'
}
},
invalid: {
'color': 'red'
},
valid: {
'color': 'green'
}
}
};
Each group represents the state of the protected field like a CSS class. For example:
/* base styles would be applied as a default style */
input.credit-card .base {
color: #000000;
font-size: 16px;
text-align: center;
letter-spacing: .25em;
text-decoration: underline;
text-shadow: 1px 1px 2px black, 0 0 1em red;
text-transform: uppercase;
}
input.credit-card ::placeholder {
color: gray;
}
/* .invalid styles would be applied on top of the .base when field validation fails */
input.credit-card .base .invalid {
color: red;
}
/* .valid styles would be applied on top of the .base when the field became valid */
input.credit-card .base .valid {
color: green;
}
Note
This information is related only to "protected" fields, which were created with Hosted Fields Integration manager via .create(...) and .mount(...) call.
In case you are going to use your own markup/widget/etc. for any additional fields (first name, last name, email, phone number, social ID) then you are free to use any CSS code to style them.
Right after field creation you can use field instance to listen basic set of events to interact with code on your page.
For example, let's listen keyup
event on credit card field
PayMe.create(key, { testMode: true })
.then(function (instance) {
var fields = instance.hostedFields();
var cardNumber = fields.create('cardNumber');
...
cardNumber.on('keyup', function(event) {
console.log(event);
})
// Mount credit card inside container with id="card-number-container"
cardNumber.mount('#card-number-container');
...
})
.catch(function(error) {
...
});
...
and each time when keyup
event occurs on cardNumber field you will be notified. Keep in mind, for security reasons event
object was significantly simplified (see Field Event Object)
Using field events your can build you own logic. There is available limited set of events out of the box, those caused by security reasons
Event type | Fields | Description |
---|---|---|
change |
all | works like standard change |
blur |
all | works like standard blur |
focus |
all | works like standard focus |
keyup |
all | works like standard keyup |
keydown |
all | works like standard keydown |
keypress |
all | works like standard keypress |
validity-changed |
all | emits when field validity state changed. Can be used for showing error messages |
card-type-changed |
cardNumber | emits when library detects vendor of entered Credit Card number |
Shape of change
, blur
, focus
, keyup
, keydown
, keypress
, validity-changed
are the same among all the fields and can be either valid or not
// Valid field
{
type: "focus",
event: "focus", // event type
field: "cardNumber", // field which emits this event
isValid: false // field validity status
}
// Invalid field
{
type: "focus",
event: "focus", // event type
field: "cardNumber", // field which emits this event
isValid: false, // field validity status
message: "Bad credit card number" // validation error message
}
Shape of card-type-changed
is little bit different and it can be used for displaying credit card brand icon
// Valid field
{
type: "card-type-changed"
event: "card-type-changed"
field: "cardNumber"
isValid: true,
cardType: "visa" // Card vendor
}
// Invalid field
{
type: "card-type-changed"
event: "card-type-changed"
field: "cardNumber"
isValid: false
message: "Bad credit card number",
cardType: "visa" // Card vendor
}
here cardType
property can be founded. All available types are listed below
cardType value | Brand | Mask |
---|---|---|
unknown |
Unknown brand | |
amex |
American Express: starts with 34/37 | 34♦♦ ♦♦♦♦♦♦ ♦♦♦♦ |
diners |
Diners Club: starts with 300-305/309... | 300♦ ♦♦♦♦♦♦ ♦♦♦♦ |
jcb |
JCB: starts with 35/2131/1800 | 35♦♦ ♦♦♦♦ ♦♦♦♦ ♦♦♦♦ |
visa |
VISA: starts with 4 | 4♦♦♦ ♦♦♦♦ ♦♦♦♦ ♦♦♦♦ |
mastercard |
MasterCard: starts with 51-55/22-27 | 51♦♦ ♦♦♦♦ ♦♦♦♦ ♦♦♦♦ |
discover |
Discover: starts with 6011/65/644-649 | 6011 ♦♦♦♦ ♦♦♦♦ ♦♦♦♦ |
So far so good, now you have form with some fields and you are ready to tokenize sensitive data to create actual sale
Is a process of storing sensitive data in the protected vault and providing safe data for you, to make actual payment
To kick off tokenization you must call tokenize(...)
method on Ingeration Manager
instance
PayMe.create(key, { testMode: true })
.then(function (instance) {
var fields = instance.hostedFields();
...
// Data for tokenization
var saleData = {
payerFirstName: 'Foo',
payerLastName: 'Bar',
payerEmail: '[email protected]',
payerPhone: '1231231',
payerSocialId: '12345',
payerZipCode: '123456',
total: {
label: 'Order #123123',
amount: {
currency: 'USD',
value: '55.00',
}
}
};
// Call for tokenization
instance.tokenize(saleData)
.then(function (tokenizationResult) {
// Successfull tokenization
//
// Now you can send 'tokenizationResult' to
// your server and call `generate-sale` on Core API
console.log(tokenizationResult);
})
.catch(function(tokenizationError) {
// Failed tokenization
//
// you can explore 'tokenizationError' object and show error message on your taste
console.error(tokenizationError)
});
})
...
...
If you want let your users input additional data (First name, Last name, Email, Phone number, Social ID) rather provide by yourself you can use native HTML inputs within your page (please check our examples page). This approach let you to use any presenting, styling, formatting logic. To help you with validation we were exposing validators to you
Particular validator can be obtained and used by this code
// ...
var firstNameValidator = PayMe.validators[PayMe.fields.NAME_FIRST];
var validation = firstNameValidator.test('John');
// ...
console.log(validation);
If validation result is success then validation
would be null
in the other case:
{required: true}
- iffirstNameValidator.test(...)
argument not defined{invalid: true}
- iffirstNameValidator.test(...)
argument has unallowable value
Note
Values within additional fields must pass exposed validators. Without fulfilling this condition tokenization will fail!
{
// Safe Credit Card data
card: {
cardMask: "411111******1111",
cardholderName: "Firstname Lastname",
expiry: "1119"
},
// Service data
type: "tokenize-success",
testMode: true,
token: "BUYER154-0987247Y-MLJ10OI7-LXRDNDYP", // Payment token for generating payment
// Payer data
payerEmail: "[email protected]",
payerName: "Firstname Lastname",
payerPhone: "1231231",
payerSocialId: "111123",
// Payment data
total: {
label: "🚀 Rubber duck",
amount: {
currency: "USD",
value: "55.00"
}
}
}
{
// Says that the form contains invalid values
validationError: true
// Here you can find error messages for particular field
errors: {
payerEmail: "Mandatory field",
payerPhone: "Mandatory field"
},
type: "tokenize-error",
}
also you can receive such shape (may be changed in the future)
{
// This error means that tokenization has already
// performed for this Intergation Instance
error: "Bad session",
message: "Session expired or deleted",
statusCode: 400,
type: "tokenize-error",
}
As was mentioned above you can create as many different fields as you need but sometimes you have some user's data, supposed to be filled
and you don't want bother your users requested filling their data again. You can do that, just provide it within Data for tokenization
object.
For example if you already have email or First + Last Name and you want provide it directly, you can to it
PayMe.create(key, { testMode: true })
.then(function (instance) {
var fields = instance.hostedFields();
// Create and Mount credit card field
var cardNumber = fields.create('cardNumber');
cardNumber.mount('#card-number-container');
...
// Create and Mount credit card expiration field
...
// Create and Mount CVV field
...
// Create and Mount Social Id field
...
// Create and Mount Phone number field
...
// Provide all available data
var saleData = {
payerFirstName: 'Foo', // First name field wasn't created, we must provide value
payerLastName: 'Bar', // Last name field wasn't created, we must provide value
payerEmail: '[email protected]', // Email field wasn't created, we must provide value
// payerPhone: '1231231', // value isn't needed due created Phone number field
// payerSocialId: '12345', // value isn't needed due created Social Id field
// payerZipCode: '123456', // value isn't needed due created Zip Code field
total: {
label: 'Order #123123',
amount: {
currency: 'USD',
value: '55.00',
}
}
};
// Call for tokenization
instance.tokenize(saleData)
.then(function (tokenizationResult) {
// Successfull tokenization
//
// Now you can send 'tokenizationResult' to
// your server and call `generate-sale` on Core API
console.log(tokenizationResult);
})
.catch(function(tokenizationError) {
// Failed tokenization
//
// you can explore 'tokenizationError' object and show error message on your taste
console.error(tokenizationError)
});
})
...
...
Rule of thumb
If you didn't create field - provide corresponding value in tokenization payload, but with one exception. You can't provide values for Credit Card Number, Credit Card Expiration and CVV - corresponding fields must be created and mounted always
You as an embedder may decide by yourself but main idea is send tokenization data to backend and generate sale using Generate sale
Let's say you have such tokenization result and sent it to your backend
{
card: {
cardMask: "411111******1111",
cardholderName: "Firstname Lastname",
expiry: "1119"
},
type: "tokenize-success",
testMode: true,
token: "BUYER154-0987247Y-MLJ10OI7-LXRDNDYP",
payerEmail: "[email protected]",
payerName: "Firstname Lastname",
payerPhone: "1231231",
payerSocialId: "111123",
total: {
label: "🚀 Rubber duck",
amount: {
currency: "USD",
value: "55.00"
}
}
}
On the backend side you can build sale payload
Generate Sale Attribute | Tokenization Attribute |
---|---|
sale_price |
tokenizationData.total.amount.value - must be converted to the integer |
product_name |
tokenizationData.total.label |
currency |
tokenizationData.total.amount.currency |
buyer_key |
tokenizationData.token |
and send it to the https://****.payme.io/api/generate-sale
Our service has a strict Content Security Policy (CSP) which in some cases might cause error messages to appear in the browser console. These errors do not cause any kind of malfunction or security risk to our service, and you can safely ignore them.
- In most cases the errors are caused by third party extensions installed on the browser. Try turning off the extensions or browse in private mode.
- The error messages might also be caused by potentially malicious JavaScripts or browser extensions which inject JavaScript to the content pages. Our service is protected against such threats.