Web


Start accepting payments with Primer in 3 simple steps:

  1. 1
    Create a client session
  2. 2
    Render a secure checkout with Universal Checkout
  3. 3
    Listen for notifications via webhooks when a payment is successfully processed
Clone Project

Pre-requisites

Prepare your API key

server.js

Server-side requests are authenticated using an API key in the X-Api-Key header.

You can create an API key by visiting your Dashboard.

Learn more about API authentication in our API Reference.

Add the Primer Web SDK as a dependency

checkout.html

To get started, add the Primer Web SDK to your checkout page. In this example, we will add the SDK using our CDN.

If you prefer to use npm, add the package @primer-io/checkout-web to your dependencies.

12345
# With Yarnyarn add @primer-io/checkout-web # With npmnpm i -s @primer-io/checkout-web
bash
copy

Learn more about the different ways to include the SDK in the Installation guide.

Prepare a container element

checkout.html

Prepare a container element for Universal Checkout.

Process successful payments

Listen to onCheckoutComplete

client.js

On the client-side, listen to the onCheckoutComplete callback to be notified when the payment has been successfully completed. Use it to show an order confirmation screen, or to fulfill the order.

Process the Payment Status Webhook

server.js

Create a PAYMENT.STATUS Webhook on your Dashboard to be notified of payment status changes on your backend.

Use this event as a single source of truth to update your database and to fulfill the order.

Show Universal Checkout

Create a client session

server.js

client session is the starting point for integrating payments at Primer. You can attach all the metadata associated with the order to the client session, and generate a clientToken, a temporary key used to initialize Universal Checkout.

The information you include in the client session is used in the Dashboard to conditionally route payments with Workflows, and activate payment methods and other features in Universal Checkout, so pass as much information as you can.

The clientToken is a key concept within Primer. You may receive a client token from various places but as long as you pass it to the SDK, Universal Checkout knows where to start/resume the flow.

On your server, create a client session with POST/client-session.
Make sure to pass at least the following data:

FieldDescription
Your reference for the payment.

Make sure to keep track of orderId - you will later receive updates to the payment via Webhooks. The payment will contain the orderId specified in the client session.
The three-letter currency code in ISO 4217 format.
e.g. use USD for US dollars.
The details of the line items of the order.

The body of a successful response contains a

that you will use to initialize Universal Checkout.

Render Universal Checkout

client.js

Use the generated client token on the client-side to show Universal Checkout by calling Primer.showUniversalCheckout(clientToken, options). Make sure to pass a selector to your container in the options.

This function returns a Promise that is resolved when Universal Checkout has been properly rendered on the page, and is ready to be used.

When the user selects a payment method and enters their credentials, Universal Checkout will automatically create a payment with the

passed in the client session and manage its lifecycle.

What's next?

Customize Universal Checkout

client.js

Prepare 3DS

server.js

3DS is supported out of the box by Universal Checkout.

To improve 3DS success rates, it is recommended to pass the following elements in the Client Session:

FieldDescription
  1. customer
The customer's email address
  1. customer
The customer's billing address

Learn more about how to configure 3DS!

123456789101112131415161718192021
<!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://sdk.primer.io/web/v2.0.0/Checkout.css" />		<script src="https://sdk.primer.io/web/v2.0.0/Primer.min.js" crossorigin="anonymous"></script>	</head>	<body>		<div id="checkout-container"></div>		<!-- Include your script to initialize the SDK and send data to Primer -->		<script src="/static/client.js"></script>	</body></html>
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
// This example is built using expressconst path = require('path')const express = require('express')const fetch = require('node-fetch') const API_KEY = process.env.PRIMER_API_KEY // YOUR API KEYconst PRIMER_API_URL = 'https://api.sandbox.primer.io' const app = express() const staticDir = path.join(__dirname, 'static')const checkoutPage = path.join(__dirname, 'checkout.html') app.use(express.json())app.use('/static', express.static(staticDir)) app.get('/', (req, res) => {	return res.sendFile(checkoutPage)}) app.post('/client-session', async (req, res) => {	const url = `${PRIMER_API_URL}/client-session` 	const response = await fetch(url, {		method: 'post',		headers: {			'Content-Type': 'application/json',			'X-Api-Version': '2.1',			'X-Api-Key': API_KEY,		},		body: JSON.stringify({			// Create an orderId for this client session			// Make sure to keep track of it: you will later receive updates through Webhooks.			orderId: 'order-' + Math.random(), 			// 3-character Currency Code used for all the amount of this session			currencyCode: 'EUR', 			order: {				// Line items for this session				// If your checkout does not have line items:				//  > Pass a single line item with the total amount!				lineItems: [					{						itemId: 'shoes-123',						description: 'Some nice shoes!',						amount: 2500, // Amount should be in minor units!						quantity: 1,					},				],			},		}),	}).then(data => data.json()) 	return res.send(response)}) app.post('/webhooks/payment-status', async (req, res) => {	const { eventType, payment } = req.body	const { id, orderId, status } = payment 	if (status === 'AUTHORIZED' || status === 'SETTLED') {		// Fullfil the order for the Payment / Client Session labelled with `orderId`	}}) app.listen(process.env.PORT)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
// /static/client.js window.addEventListener('load', onLoaded) async function onLoaded() {	const clientSession = await fetch('/client-session', {		method: 'post',		headers: { 'Content-Type': 'application/json' },	}).then(data => data.json()) 	const { clientToken } = clientSession 	try {		const universalCheckout = await Primer.showUniversalCheckout(clientToken, {			// Specify the selector of the container element			container: '#checkout-container', 			/**			 * When the checkout flow has been completed, you'll receive			 * the successful payment via `onCheckoutComplete`.			 * Implement this callback to redirect the user to an order confirmation page and fulfill the order.			 */			onCheckoutComplete({ payment }) {				console.log('Checkout Complete!', payment)			}, 			// Other optional options			style: {				submitButton: {					base: {						color: '#ffffff',						background: '#000000',						borderRadius: '8px',						boxShadow: 'none',					},					disabled: {						color: '#9b9b9b',						background: '#e1deda',					},				},			},		}) 		console.log('Universal Checkout is loaded!')	} catch (e) {		console.log('Universal Checkout failed to load!')	}}