Primer Web SDK
Primer's Official Universal Checkout Web SDK
💪 Features of the Web SDK
📚 Documentation
Consider looking at the following resources:
💡 Support
For any support or integration related queries, feel free to Contact Us.
📋 Prerequisites
🔑 Generate a client token by creating a client session in your backend🧱 Prepare a container in which to render Universal Checkout🎉 That's it!
🧱 Installation
With npm (recommended)
Our Web SDK is available on npm under the name @primer-io/checkout-web
.
This package includes TypeScript definitions.
# With yarn
yarn add @primer-io/checkout-web
# With npm
npm install --save @primer-io/checkout-web
Then import Primer
in your application
import { Primer } from '@primer-io/checkout-web';
// For example
Primer.showUniversalCheckout(clientToken, {
/* Options */
});
With our CDN
Include the Primer.min.js
script on the page where you want the checkout to be rendered.
Ensure that you're providing the desired version in the script tag. In the case below, it's v2.0.0
:
<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>
Primer.min.js
will add the Primer
object to the global scope.
const { Primer } = window;
// For example
Primer.showUniversalCheckout(clientToken, {
/* Options */
});
🎉 Ways of integrating the SDK
The simplest way to integrate with Primer is with our Drop-In Universal Checkout. With just a few lines of code, you can display a fully in-context checkout UI with all your payment methods.
Where there is a need for more customization and control over the checkout experience, a headless version of Primer’s Universal Checkout is available. You can use Headless Universal Checkout with your own UI, giving you more flexibility and allowing you to move faster when making design changes, while still having Primer capture sensitive PCI card data or other form data.
Drop-In Universal Checkout
🚀 Quick start
Take a look at our Quick Start Guide for accepting your first payment with Universal Checkout.
👩💻 Usage
🔍 Rendering the checkout
Availing Universal Checkout is as easy as implementing one line of code:
import { Primer } from '@primer-io/checkout-web';
const universalCheckout = await Primer.showUniversalCheckout(
clientToken,
options,
);
Below is an example of a basic implementation of Universal Checkout:
const clientToken = '...'; // client token retrieved from your backend
const options = {
// Container element which will contain the checkout
container: '#container',
onCheckoutComplete({ payment }) {
// Notifies you that a payment was created
// Move on to next step in your checkout flow:
// e.g. Show a success message, giving access to the service, fulfilling the order
},
};
const universalCheckout = await Primer.showUniversalCheckout(
clientToken,
options,
);
Note that there are more options which can be passed to Universal Checkout. Please refer to the section below for more information.
Wait for Universal Checkout to be loaded
Primer.showUniversalCheckout(clientToken, options)
returns a Promise that is resolved when Universal Checkout has been properly rendered on the page.
// With await
try {
const universalCheckout = await Primer.showUniversalCheckout(
clientToken,
options,
);
console.log('Universal Checkout is ready!');
} catch (e) {
console.log('Failed to show Universal Checkout', e);
}
// With then/catch
Primer.showUniversalCheckout(clientToken, options)
.then((universalCheckout) => {
console.log('Universal Checkout is ready!');
})
.catch((e) => {
console.log('Failed to show Universal Checkout', e);
});
🧰 Checkout Options
When calling showUniversalCheckout
you can provide Universal Checkout with some configuration options.
These options range from callbacks notifying you of the current payment's status, to styling options for certain UI elements.
Below are some options to consider when integrating Universal Checkout:
🚀 General Options
⚙️ Container and locale
option | Type | Description | |
---|---|---|---|
container |
string or Element
|
The container element in which the checkout should be rendered | required |
locale |
string |
This option forces the locale. By default, the locale will be set to the browser's locale | optional |
⚙️ Payment Lifecycle Callbacks
Callbacks can be provided to Universal Checkout which will notify you on certain checkout events such as payment creation.
These callbacks can be used to inform you on the current state of the checkout.
We strongly recommend you to implement onCheckoutComplete
to redirect the user to an order confirmation page when the payment is successful:
const options = {
/* Other checkout options ... */
onPaymentCreationStart() {
// Notifies you before the checkout tokenizes a payment method
// and creates a payment
}
onBeforePaymentCreate(data, handler) {
// Notifies you that a payment will be created
// Update your UI accordingly
// e.g. Show custom loading UI, etc.
// Abort or continue with payment creation
// Primer will continue with payment creation if onBeforePaymentCreate is not implemented
// ⚠️ You MUST call one of the functions of the `handler` if `onBeforePaymentCreate` is implemented
// Choose to abort a payment
return handler.abortPaymentCreation();
// Choose to continue with payment creation
return handler.continuePaymentCreation();
},
onCheckoutComplete({ payment }) {
// Notifies you that a payment was created
// Move on to next step in your checkout flow:
// e.g. Show a success message, giving access to the service, fulfilling the order, ...
},
onCheckoutFail(error, { payment }, handler) {
// Notifies you that the checkout flow has failed and a payment could not be created
// This callback can also be used to display an error state within your own UI.
// ⚠️ `handler` is undefined if the SDK does not expect anything from you
if (!handler) {
return;
}
// ⚠️ If `handler` exists, you MUST call one of the functions of the handler
// Show a default error message
return handler.showErrorMessage();
// Show a custom error message
return handler.showErrorMessage('This is my custom error message');
},
};
⚙️ Client Session Lifecycle Callbacks
Callbacks can be provided to Universal Checkout which will notify you on client session update events.
const options = {
/* Other checkout options ... */
onBeforeClientSessionUpdate() {
// Notifies you that the client session is in the process of being updated
// Use it to show a loading indicator on your UI
},
onClientSessionUpdate(clientSession) {
// Notifies you when the client session has been updated by the checkout
// Returns updated client session
// Updated client session can be used to inform your UI
// e.g. update tax, shipping or discount amounts displayed to your customers
},
};
⚙️ Payment Method Callbacks
To receive updates on which payment method was selected by a customer, you can use the following callback:
const options = {
/* Other checkout options ... */
onPaymentMethodAction(action, { paymentMethodType }) {
// Notifies you when a specific payment method has been selected or unselected
// action will either be 'PAYMENT_METHOD_SELECTED' or 'PAYMENT_METHOD_UNSELECTED'
},
};
💳 Payment Methods Options
Learn more about the payment method specific options.
🎨 Customization Options
Learn more about the customization options.
⚙️ Styling
option | Type | Description | Default | |
---|---|---|---|---|
style |
Object | Custom style applied to the UI. Learn more about the capabilities in our Customization Guide |
Default style | optional |
⚙️ Form Options
option | Type | Description | Default | |
---|---|---|---|---|
form.inputLabelsVisible |
Boolean | Choose whether to show the label above inputs | true | optional |
⚙️ Submit Button Options & Callbacks
Universal Checkout allows you to use your own submit button for submitting forms. By default, the built-in submit button will be favored:
option | Type | Description | Default | |
---|---|---|---|---|
submitButton.useBuiltInButton |
Boolean | Set whether to use built-in submit button or to display your own custom button | true | optional |
submitButton.amountVisible |
Boolean | Set whether the total order amount should be displayed in the submit button content | false | optional |
Note that when disabling the built-in submit button and using your own custom submit button, it is required to implement the submit()
function in order to notify Universal Checkout of form submissions.
Read more about the submit()
function in the Manual Form Submission section referenced below.
When using your own custom submit button, it's important to use the following callbacks to ensure that your submit button is in the correct state and in sync with the checkout as your customers interact with it:
const options = {
/* Other options ... */
submitButton: {
useBuiltInButton: false, // Default to true
// Callback for receiving the submit button's visible state in the current scene
onVisible(isVisible, context: { currentSceneId }) {
// Show or hide your custom submit button
},
// Callback for receiving the submit button's disabled state in the current scene
onDisable(isDisabled, context: { currentSceneId }) {
// Disable or enable your custom submit button
},
// Callback for receiving the submit button's loading state in the current scene
onLoading(isLoading, context: { currentSceneId }) {
// Show your submit button in a loading state
},
// Callback for receiving the submit button's content in the current scene
onContentChange(content, context: { currentSceneId }) {
// Set your submit button's content with either the content provided or your own custom content
},
},
};
⚙️ Processing Indicator Options
Show a processing indicator overlay on top of the checkout when submitting a form:
option | Type | Description | Default | |
---|---|---|---|---|
processingIndicator.visible |
Boolean | Choose whether a processing indicator overlay should be shown on form submission | true | optional |
⚙️ Error Message Options & Callbacks
When Universal Checkout encounters errors processing payments, these errors will be shown to your users by default, below the submit button.
If you want to show your own error messages, you have the option to disable the default, built-in error messages:
option | Type | Description | Default | |
---|---|---|---|---|
errorMessage.disabled |
Boolean | Choose whether to allow Universal Checkout to show error messages | false | optional |
You can use the following callbacks to get notified when Universal Checkout intends to display an error message. By using these callbacks, you can respond with your own UI changes and avail a custom error message:
const options = {
/* Other options ... */
errorMessage: {
disabled: false, // Default to false
// A callback for when the error message should be displayed
onErrorMessageShow(message) {
// Choose to use provided message for own purposes
},
// A callback for when the error message should be hidden
onErrorMessageHide() {
// Update own UI accordingly
},
},
};
⚙️ Success Screen Options
When the checkout is succefully complete, Universal Checkout displays a success scene with a default success message "Your payment was successful!".
Set the option successScreen
to modify the behavior of the success scene.
const options = {
/* Other options ... */
// Remove the success screen
successScreen: false,
// Change the message of the default success screen
successScreen: {
type: 'CHECK',
title: 'This is a custom success message!',
},
};
🔨 Advanced Options
⚙️ Manual Payment Creation & Tokenization Lifecycle Callbacks
By default, Universal Checkout will automatically create payments and manage their lifecycles on your behalf. The manual payment creation flow used in previous Web SDK versions is still supported.
Check our Manual Payment Creation guide.
⚙️ Show the flow for a single payment method
The payment methods featuring a dedicated screen allows you to directly display their flow. For that, make sure to:
- Set
uxFlow
toSINGLE_PAYMENT_METHOD_CHECKOUT
- Set
paymentMethod
to the payment method you want to display
const options = {
/* Other options ... */
uxFlow: 'SINGLE_PAYMENT_METHOD_CHECKOUT',
paymentMethod: 'PAYMENT_CARD',
};
Payment methods that supports this flow
- Card:
PAYMENT_CARD
- Klarna:
KLARNA
- GoCardless:
GOCARDLESS
⚙️ Customizable Payment Method Button options
Some payment methods enables you to customize the payment method button.
option | Type | Description | Default | |
---|---|---|---|---|
logoSrc |
String | Data URL representing the logo you want to display in the payment method button. | required | |
background |
String | Color of the background of the payment method button. | required | |
logoAlt |
String | Accessibility marker for the payment method button | required | |
text |
String | Label to display to the right of logoSrc
|
Only show logoSrc
|
optional |
Payment methods that supports this flow
- Gift Cards with Mollie:
PAYMENT_CARD
🔧 Checkout Methods
Universal Checkout exposes some methods which can be called to perform some specific actions:
Updating the Client Session
When updating the client session, after the checkout has been initialized, you will receive a client token in the response returned from PATCH /client-session
.
In order for the checkout to know that you have updated the client session, you will need to pass this new client token back to the checkout:
const universalCheckout = await Primer.showUniversalCheckout(
clientToken,
options,
);
// Refresh client session by calling refreshClientSession
// This will result in Universal Checkout having access to the latest changes made to the client session
universalCheckout.refreshClientSession();
Note that refreshClientSession
replaces the deprecated setClientToken
method since version v2.11.0
.
Manual Form Submission
The checkout provides a method for manually calling submit. This is useful when using your own custom submit button.
const universalCheckout = await Primer.showUniversalCheckout(
clientToken,
options,
);
const handleMySubmitButtonClick = () => {
// Forward all submit button clicks to the SDK
universalCheckout.submit();
};
Disabling Tokenization and Payment Creation
The checkout provides setPaymentCreationEnabled
and setTokenizationEnabled
to enable or disable tokenization and payment creation. Use this if you would like to prevent users from paying because your side of the checkout is not valid.
These two functions can be used interchangibly.
const universalCheckout = await Primer.showUniversalCheckout(
clientToken,
options,
);
// Disable payment creation
universalCheckout.setPaymentCreationEnabled(false);
universalCheckout.setTokenizationEnabled(false);
// Enable payment creation
universalCheckout.setPaymentCreationEnabled(true);
universalCheckout.setTokenizationEnabled(true);
Headless Universal Checkout
👩💻 Usage
🔍 Initialize the headless checkout
Once you have a client token, you can initialize Primer’s Headless Checkout with Primer.createHeadless(clientToken)
.
You should then configure the onAvailablePaymentMethodsLoad
listener. The listener will return the available payment methods for the client session.
Payment methods are added and configured through Primer's Dashboard. The listener will return the payment methods whose conditions match the current client session.
Below is an example of a basic implementation of Headless Universal Checkout:
const clientToken = '...'; // client token retrieved from your backend
async function onAvailablePaymentMethodsLoad(paymentMethods) {
for (const paymentMethod of paymentMethods) {
switch (paymentMethod.type) {
case 'PAYMENT_CARD': {
// Configure your card form
// await configureCardForm();
break;
}
case 'PAYPAL': {
// Render the payment method button
// configurePayPalButton();
break;
}
case 'APPLE_PAY': {
// Render the payment method button
// configureApplePayButton();
break;
}
case 'GOOGLE_PAY': {
// Render the payment method button
// configureGooglePayButton();
break;
}
// More payment methods to follow
}
}
}
function onCheckoutComplete({ payment }) {
console.log('onCheckoutComplete', payment);
}
function onCheckoutFail(error, { payment }, handler) {
console.error('onCheckoutFail', error, payment);
handler?.showErrorMessage('fail');
}
const { Primer } = window;
const headless = await Primer.createHeadless(clientToken);
await headless.configure({
onAvailablePaymentMethodsLoad,
onCheckoutComplete,
onCheckoutFail,
});
await headless.start();
console.log('Headless Universal Checkout is loaded!');
Note that there are more options which can be passed to Universal Checkout. Please refer to the section below for more information.
🧰 onAvailablePaymentMethodsLoad
onAvailablePaymentMethodsLoad
return a list of objects, each representing a payment method. This what each object is made of:
option | Type | Description | |
---|---|---|---|
type |
string |
The payment method type. e.g. PAYMENT_CARD , PAYPAL , ADYEN_IDEAL , ... |
required |
managerType |
CARD , NATIVE , REDIRECT
|
The type of manager to instantiate with createPaymentMethodManager
|
required |
🚀 Create your UI
Primer enables you to create the UI that suits your needs, using the provided inputs and components we provide.
Handle payment methods
Handling payment methods is done by instantiating a payment method manager using createPaymentMethodManager(paymentMethodType: string, options)
. The instance of the manager depends on managerType
.
Handle card forms
When the payment method's managerType
is CARD
(relevant for the payment method type PAYMENT_CARD
), use the payment method manager to build your own card form using Primer input elements.
See the relevant (documentation)[https://primer.io/docs/accept-payments/headless-universal-checkout/web/#show-card-components] for further instructions.
Handle native payment method buttons
When the payment method's managerType
is NATIVE
, use the payment method manager to render the payment method button to your customers.
See the relevant (documentation)[https://primer.io/docs/accept-payments/headless-universal-checkout/web/#render-the-button] for further instructions
Handle redirect payment methods
When the payment method's managerType
is REDIRECT
, use the payment method manager to start the redirect flow.
See the relevant (documentation)[https://primer.io/docs/accept-payments/headless-universal-checkout/web#step-4c-handle-payment-methods-with-redirect] for further instructions.
🧰 Headless Options
option | Type | Description | |
---|---|---|---|
paymentHandling |
string |
Either AUTO (default) or MANUAL
|
optional |
locale |
string |
This option forces the locale. By default, the locale will be set to the browser's locale | optional |
🧰 Headless Methods
When you call primer.createHeadless(clientToken)
, you get an instance of PrimerHeadlessCheckout
:
export interface PrimerHeadlessCheckout {
// Set the configuration of the headless checkout
configure: (options: HeadlessUniversalCheckoutOptions) => void;
// Start the headless checkout
start: () => void;
// Create a payment method manager
createPaymentMethodManager;
}
createPaymentMethodManager
createPaymentMethodManager
creates a manager based on the provided payment method type.
And the following managers (with their methods):
CardPaymentMethodManager
Method | Parameters | Output | Description |
---|---|---|---|
createHostedInputs |
- | {cardNumberInput: HeadlessHostedInput, expiryInput: HeadlessHostedInput, cvvInput: HeadlessHostedInput} |
Creates instances of the hosted inputs. |
createHostedInput |
name: CreditCardFieldName |
HeadlessHostedInput |
Creates a hosted input based on the type. |
removeHostedInputs |
- | - | Removes all of the hosted input fields from the DOM. |
setCardholderName |
cardholderName: string |
- | Set the cardholder name. |
validate |
- | Promise<Validation> |
Validates all the hosted inputs. |
submit |
- | - | Submits all the data from the hosted inputs, tokenizes the card data and creates the payment. |
reset |
- | - | Resets all the inputs. |
NativePaymentMethodManager
Method | Parameters | Output | Description |
---|---|---|---|
createButton |
- | HeadlessPaymentMethodButton |
Creates the button for the payment method |
RedirectPaymentMethodManager
Method | Parameters | Output | Description |
---|---|---|---|
start |
- | Promise<void> |
Starts the redirect flow, shows a popup, and handle the web view. |
getAssetsManager
Use the AssetsManager
to get access to assets for each of the available payment methods.
const assetsManager = headless.getAssetsManager();
const assets: ButtonPaymentMethodAsset = await assetsManager.getPaymentMethodAsset(
'MOLLIE_IDEAL',
);
ButtonPaymentMethodAsset
Field | Type | Description |
---|---|---|
iconUrl |
object |
Object containing colored , dark and light : each being URL to download the icon of the payment method. |
paymentMethodName |
string |
Human-readable payment method name. |
backgroundColor |
string |
Object containing colored , dark and light : each being background color we encourage you to use for the payment method button. |
Values
Below are some types mentioned above:
Type | Values |
---|---|
CreditCardFieldName |
cardNumber , expiryDate , cvv
|
Classes
Below are some classes mentioned above (and their methods):
HeadlessHostedInput
Method | Parameters | Output | Description |
---|---|---|---|
getName |
- | string |
Get the name of the hosted input |
getOptions |
- | HeadlessHostedInputOptions |
Get all the available optons for the hosted input. |
setOptions |
options: HeadlessHostedInputOptions |
- | Set options on the hosted input. |
addEventListener |
event: EventTypes, callback: EventListener |
- | Add an event listener. |
render |
container: string, options: HeadlessHostedInputOptions |
- | Render the hosted input in a container. |
focus |
- | - | Focus the hosted input UI. |
blur |
- | - | Blur the hosted input UI. |
setDisabled |
status: boolean |
- | Disable the hosted input UI. |
HeadlessPaymentMethodButton
Method | Parameters | Output | Description |
---|---|---|---|
render |
containerId: string, options: HeadlessButtonRenderOptions |
- | Render the button in your container. |
setDisabled |
disabled: boolean |
- | Disable or enable the button. |
clean |
- | - | Hide the button. |
blur |
- | - | Unfocus the button. |
focus |
- | - | Focus the button. |
addEventListener |
event: EventTypes, callback: EventListener |
- | Add an event listener. |
HeadlessHostedInputOptions
Field | Type | Description |
---|---|---|
placeholder |
string |
Placeholder text. |
ariaLabel |
string |
Label |
style |
Record<string, any> |
All the style to apply. |
HeadlessButtonRenderOptions
Field | Type | Description |
---|---|---|
style |
GooglePayStyles | PayPalStyles | ApplePayStyles |
All the style to apply. |
Validation
Field | Type | Description |
---|---|---|
valid |
boolean |
Whether the value is valid or not. |
validationErrors |
[InputValidationError] |
A list of validation errors. |
error |
string |
The raw error. |
InputValidationError
Field | Type | Description |
---|---|---|
name |
string |
A friendly name for the validation error. |
error |
string |
The raw error. |
message |
string |
The error message. |
InputMetadata
Field | Type | Description |
---|---|---|
errorCode |
string |
An error code in the case of an error. |
error |
string |
The raw error. |
valid |
boolean |
Is the input data valid. |
active |
boolean |
Is the input active. |
dirty |
boolean |
Is the input dirty. |
touched |
boolean |
Is the input touched. |
submitted |
boolean |
Has the input been submitted |
Payment
Field | Type | Description |
---|---|---|
id |
string |
The payment ID. Used to look up the payment on the Primer API. |
orderId |
string |
Your provided order ID. |
paymentMethodData |
PaymentMethodData |
Additional payment method data. |
PrimerClientError
Field | Type | Description |
---|---|---|
code |
ErrorCode |
A unique error identifier. |
message |
string |
The raw error message. |
diagnosticsId |
string |
A unique ID to give to Primer for support. |
Style options
PayPalStyles
Field | Type |
---|---|
buttonColor |
'gold' | 'blue' | 'silver' | 'white' | 'black' |
buttonShape |
'pill' | 'rect' |
buttonSize |
'small' | 'medium' | 'large' | 'responsive' |
buttonHeight |
number |
buttonLabel |
'checkout' | 'credit' | 'pay' | 'buynow' | 'paypal' | 'installment' |
buttonTagline |
boolean |
GooglePayStyles
Field | Type |
---|---|
buttonColor |
'default' | 'black' | 'white' |
buttonType |
'long' | 'short' |
ApplePayStyles
Field | Type |
---|---|
buttonColor |
'default' | 'black' | 'white' |
buttonType |
'plain' | 'buy' | 'set-up' | 'donate' | 'check-out' | 'book' | 'subscribe' |
buttonStyle |
'white' | 'white-outline' | 'black' |
🧰 Headless Events
Hosted Input Events
On a hosted input, you can listen to an event as follows:
cardNumberInput.addEventListener('change', (...args) => {
console.log('cardNumberInput change', ...args);
});
And the following event types are supported:
enum EventTypes {
CHANGE = 'change',
ERROR = 'error',
FOCUS = 'focus',
BLUR = 'blur',
}
Callbacks
You can register various callbacks on the headless checkout using configure
as follows:
...
// Create an instance of the headless checkout
const headless = await Primer.createHeadless(clientToken);
// Configure headless
await headless.configure({
onAvailablePaymentMethodsLoad,
onCheckoutComplete,
onCheckoutFail
});
The following callbacks are required:
Callback | Parameters | Description |
---|---|---|
onAvailablePaymentMethodsLoad |
paymentMethods: PaymentMethodData[] |
Get the list of payment methods you should handle. |
The following callbacks are optional:
Callback | Parameters | Description |
---|---|---|
onBeforePaymentCreate |
{payment: Payment}, handler |
Triggered before the payment is created. |
onCheckoutComplete |
{payment: Payment} |
Triggered on a successful payment. |
onCheckoutFail |
error: PrimerClientError, {payment: Payment}, handler |
Triggered if the payment fails for any reason. |