Build your checkout

Fully customize your checkout, handle 3D Secure and alternative payments with Universal Checkout and Checkout Components.

Customizing your checkout experience

Our checkout libraries enable you to build a best-in-class checkout, offering alternative payment methods and securely capturing sensitive payment information. Primer offers two modes of integration, providing varying degrees of out-the-box functionality: Universal Checkout, and Checkout Components.

PCI considerations

Both libraries handle the secure collection of card data, which means you will only need to complete the SAQ (A) Self assessment (the lowest level of PCI compliance). Your server is never exposed to sensitive payment data.

Installation

To get started, add the Primer Web SDK to your page:

1
2
3
4
5
<!-- Use the latest development version when building your integration -->
<script src="https://assets.primer.io/primer-sdk-web/v1-latest/Primer.js"></script>
<!-- Use the minified version for your production environment -->
<script src="https://assets.primer.io/primer-sdk-web/v1-latest/Primer.min.js"></script>
html

Initializing the SDK

Generate, and fetch a client token from your server and initialize the Primer SDK.

1
2
3
4
5
6
7
8
9
10
11
// checkout.js
window.addEventListener('load', onLoaded);
async function onLoaded() {
const primer = new Primer({
credentials: {
clientToken: '<YOUR_CLIENT_TOKEN>', // Your server generated client token
},
});
}
js

Security
If your checkout page uses the Content-Security-Policy header, you'll need to add a few things to your whitelist.

Universal Checkout

Building your checkout page

The Universal Checkout is a managed checkout experience for all payment types, and handles 3D Secure.

To render the checkout, load the Primer SDK and specify a container element.

Styling
You can use our default styles by adding Checkout.css in your page.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<!-- checkout.html -->
<html>
<!-- This added styling and css is entirely optional -->
<head>
<style>
#checkout-container {
display: flex;
flex-direction: column;
padding: 12px 24px;
}
</style>
<link rel="stylesheet" href="https://assets.primer.io/primer-sdk-web/v1-latest/Checkout.css">
</head>
<body>
<div id="checkout-container"></div>
<script src="https://assets.primer.io/primer-sdk-web/v1-latest/Primer.js"></script>
<!-- Include your script to initialize the SDK and send data to Primer -->
<script src="static/checkout.js"></script>
</body>
</html>
html

Assign your container, and specify callbacks for payment method tokenization and error handling:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// checkout.js
window.addEventListener('load', onLoaded);
async function onLoaded() {
const primer = new Primer({
credentials: {
clientToken: '<YOUR_CLIENT_TOKEN>', // Your server generated client token
},
});
// Use `.checkout()` to initialize and render the UI
await primer.checkout({
// specify the selector of the container element
container: '#checkout-container',
/**
* When a payment method is chosen and the customer clicks 'Pay',
* the payment method is tokenized and you'll receive a token in the
* `onTokenizeSuccess` callback which you can send to your server
* to authorize a transaction.
*/
onTokenizeSuccess(paymentMethod) {
// Send the payment method token to your server
fetch('<YOUR_SERVER_URL>', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(paymentMethod),
});
},
Show all 40 lines ↓
js

Adding alternative payment methods

Universal Checkout automatically displays your alternative payment methods.

For Apple Pay and Google Pay, you'll need to provide some additional information:
Additional informationDescription
countryCodeThe alpha2 ISO 3166 country code that your company operates in.
purchaseInfoThe currency and sale amount you want the alternative payment method to display.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// checkout.js
window.addEventListener('load', onLoaded);
async function onLoaded() {
await primer.checkout({
// In addition to the other options above
// Add your company's alpha2 ISO country code
countryCode: 'FR',
// Add the total sale amount for the APM to display
purchaseInfo: {
totalAmount: {
currency: 'EUR',
value: 700,
},
},
// Carry on with sending the token to your server for authorization.
Show all 30 lines ↓
js

3D Secure

To enable 3D Secure provide threeDSecure options in .checkout().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// checkout.js
/**
* Provide authentication information about your customer and the order.
* For the best result, provide as much information as you can.
* The options for 3DS are as follows:
*/
const threeDSecureOptions = {
// All of the order properties are mandatory and should be provided.
order: {
orderId: 'my-order-id',
email: 'john.doe@email.com',
billingAddress: {
firstName: 'John',
lastName: 'Doe',
addressLine1: '123 Fake St',
city: 'Paris',
countryCode: 'FR',
postalCode: '123456',
},
amount: {
currency: 'EUR',
value: 1199,
},
},
Show all 53 lines ↓
js

Customization

You can override the styles provided in Checkout.css for most elements. All class names follow BEM naming conventions. In order to style the inputs of the card form you must provide the card.css option in .checkout().

The <input> elements are assigned classes which describe the state: error, valid, active, dirty and touched. When the customer attempts to submit their card details, the containing <form> element is assigned a submitted class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// checkout.js
await primer.checkout({
// ... other options
card: {
css: `
input {
font-family: 'Open Sans', sans-serif;
}
form.submitted input.error {
color: #f44336;
}
`,
},
});
js

The checkout experience is split into scenes, each with the css class PrimerCheckout__scene. When one scene transitions to the next, a series of modifier classes are added and removed which you can use to apply your own styles.

  • PrimerCheckout--enter
  • PrimerCheckout--entering
  • PrimerCheckout--entered
  • PrimerCheckout--exit
  • PrimerCheckout--exiting
  • PrimerCheckout--exited

In addition to the css classes, you can specify transitions options in .checkout():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// checkout.js
await primer.checkout({
// ... other options
// transitions describes the scene transitions in the Universal Checkout
transitions: {
// You can specify the duration of an enter/exit transition in milliseconds
duration: {
enter: 200,
exit: 200
},
/**
* You can subscribe to various points of the transition
* for a scene using the following callback options.
* Each callback receives the html element of the scene
* as the first argument.
*/
onEnter(node) {},
onEntering(node) {},
onEntered(node) {},
onExiting(node) {},
onExited(node) {},
onExit(node) {},
},
});
js

During a transition from scene A to scene B, the following sequence occurs:

  • enter entering and entered are removed from scene A
  • exit and exiting are applied to scene A
  • There is a delay of duration.exit milliseconds
  • exited is applied to scene A
  • exit exiting and exited are removed from scene B
  • enter and entering are applied to scene B
  • There is a delay of duration.enter milliseconds
  • entered is applied to scene B

Checkout Components

Building your custom checkout page

If you want want more control over your checkout page, Checkout Components enables you to securely build your checkout page from scratch. To get started accepting card details you'll need to add elements for cardNumber, expiryDate and cvv.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html>
<!-- checkout.html -->
<html>
<head>
<!-- This added styling is entirely optional -->
<style>
.form-container {
padding: 20px;
display: flex;
flex-direction: column;
}
.form-input {
height: 32px;
background-color: white;
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 4px;
padding: 0px 5px;
}
.form-input + .form-input {
margin-top: 15px;
}
</style>
</head>
<body>
<div class="form-container" >
<div class="form-input" id="number-input"></div>
<div class="form-input" id="date-input"></div>
<div class="form-input" id="cvv-input"></div>
</div>
<button id="pay-now-button">Pay Now</button>
<script src="https://assets.primer.io/primer-sdk-web/v1-latest/Primer.min.js" ></script>
<!-- Include your script to initialize the SDK and send data to Primer -->
<script src="checkout.js" ></script>
</body>
</html>
html

Render the card form as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// checkout.js
window.addEventListener('load', onLoaded);
async function onLoaded() {
// Create a client as described in the 'Initializing the SDK' section
const client = new Primer({
credentials: { clientToken: '<YOUR_CLIENT_TOKEN>' }, // Your server generated client token
});
// Use `.render()` to initialize and render the components
await primer.render({
// Card form options
card: {
/**
* This is the selector of your button which submits the
* card details for tokenization.
*/
submitButton: '#pay-now-button',
Show all 51 lines ↓
js

Alternatively, you can omit the submitButton option and attach the event listener yourself.

1
2
3
button.addEventListener('click', () => {
checkout.card.tokenize();
});
js

Adding alternative payment methods

To add APMs such as Apple Pay, Google Pay and PayPal - add containers for your payment method buttons.

1
2
3
<div id="pay-with-apple-pay"></div>
<div id="pay-with-google-pay"></div>
<div id="pay-with-paypal"></div>
html

And now assign these containers in render().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// checkout.js
primer.render({
// other options as described before
// These APMs will require your company's country alpha2 country code
countryCode: 'FR',
// You'll also need to tell them how much you're going to charge the customer
purchaseInfo: {
totalAmount: {
currency: 'EUR',
value: 1995,
},
},
// Now just add the selectors of your buttons to the options for each APM
applePay: { container: '#pay-with-apple-pay' },
googlePay: { container: '#pay-with-google-pay' },
paypal: { container: '#pay-with-paypal' },
onTokenizeSuccess(paymentMethod) {
// Send the payment method token to your server for authorization
},
});
js

When the customer completes the payment flow, a one-time use payment method token is returned just as with cards - through the onTokenizeSuccess callback.

3D Secure

Add a container to display the 3DS challenge to your customers:

1
2
3
4
5
6
7
8
9
10
11
12
<!-- checkout.html -->
<style>
#threeds-modal {
position: absolute;
width: 100%;
height: 100%;
background-color: white;
display: none;
}
</style>
<div id="threeds-modal"></div>
html

Provide some authentication information about your customer and the order. For the best result, provide as much information as you can. The options for 3DS are as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// checkout.js
const threeDSecureOptions = {
// All of the order properties are mandatory and should be provided.
order: {
orderId: 'my-order-id',
email: 'john.doe@email.com',
billingAddress: {
firstName: 'John',
lastName: 'Doe',
addressLine1: '123 Fake St',
city: 'Paris',
countryCode: 'FR',
postalCode: '123456',
},
amount: {
currency: 'EUR',
value: 1199,
},
},
Show all 41 lines ↓
js

Once the card has been tokenized, you can pass the payment method token to threeDSecure.verify. If successful, a new token (containing the 3DS authentication data) will be returned which you should use to authorize the transaction:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// checkout.js
await primer.render({
card: {
submitButton: '#pay-now',
fields: {
cardNumber: { container: '#number-input' },
cvv: { container: '#cvv-input' },
expiryDate: { container: '#date-input' },
},
},
onTokenizeSuccess: onCardTokenized,
});
async function onCardTokenized(paymentMethod) {
const authorization = await primer.threeDSecure.verify({
token: paymentMethod.token,
container: '#threeds-modal',
onChallengeStart: () => toggleModalVisible(true),
onChallengeEnd: () => toggleModalVisible(false),
order: threeDSecureOptions.order,
additionalInfo: threeDSecureOptions.additionalInfo,
});
if (authorization.status === Primer.ThreeDSecureStatus.SUCCESS) {
Show all 45 lines ↓
js