README
@primer-io/checkout-web
Using the Primer SDK for the web, you can quickly and easily create a checkout page for your site to securely collect card information and offer a multitude of alternative payment methods to your customers through one simple integration.
Get Started!
Check out our guides on how to get started here.
Since the SDK must be loaded through our CDN. This npm package exposes a function loadPrimer
that loads the Primer SDK from our CDN.
This function returns a promise that resolves to a class Primer
.
import {loadPrimer} from '@primer-io/checkout-web'
const Primer = await loadPrimer();
const client = new Primer({
credentials: { clientToken: "...", },
});
Reference
Global
class Primer(options: PrimerClientOptions)
Creates a primer client
type PrimerClientOptions
Options to configure the primer client
type PrimerClientOptions {
credentials: {
// Your server-generated client token
clientToken: string;
};
// maximum time in milliseconds to wait for third party scripts to load [default=10000]
thirdPartyScriptTimeout?: number;
}
Primer (checkout)
Primer#checkout(options: Options): Promise<PrimerCheckout>
Create a Primer checkout component. The structure of options
depends on options.uxFlow
, which can have three values:
CHECKOUT
: to render a checkout that displays multiple payment methods and the vaulted payment methodsSINGLE_PAYMENT_METHOD_CHECKOUT
: to render a checkout that displays a single payment methodMANAGE_PAYMENT_METHODS
: to render a UI for managing saved payment methods
By default, uxFlow
is CHECKOUT
.
Common options
Some options are common to all uxFlow
.
type Options = {
// Optimise the UX for managing saved payment methods by specifying 'MANAGE_PAYMENT_METHODS' for uxFlow
uxFlow: 'CHECKOUT' | 'MANAGE_PAYMENT_METHODS' | 'SINGLE_PAYMENT_METhOD_CHECKOUT,
// An HTML element (or a selector to the HTML element) in which to place the checkout component
container: string | Element;
// Override the locale
// By default translations will be provided for the browser's locale
locale?: string;
// 3DS options. These must be provided if you want to perform SCA
// See the section on 3DS below for more info.
threeDSecure?: ThreeDSecureVerifyOptions;
// A callback for when tokenization begins
onTokenizeStart?: () => void;
// A callback for tokenization success. This will receive the payment method token.
onTokenizeSuccess: (data: PaymentMethodToken) => Promise<SuccessCallbackReturnType>;
// A callback for when tokenization fails. See the error message here for more information
onTokenizeError: (message: string) => void;
// A callback for when changes have been made to the totalAmount provided to the checkout
onAmountChange?: (data: AmountChange) => void;
// A callback for when changes totalAmount is being updated
onAmountChanging?: (isChanging: boolean) => void;
// A callback for when an error occured while updating the totalAmount
onAmountChangeError?: (message: PrimerError) => void;
// A callback for receiving actions which can be used to update the client session
onClientSessionActions?: (data: onClientSessionActionData) => Promise<{ clientToken: string } | false>;
// Options and callbacks relating to scenes
scene?: SceneOptions;
// Style of the Checkout
style?: CheckoutStyle;
// Set options relating to the card form
form?: FormOptions;
submitButton?: {
// Deprecated in favor of using `useBuiltInButton`
visible?: boolean;
// Set whether to use built-in submit button
// Or use your own submit button
// Defaults to true
useBuiltInButton?: boolean;
// The following callbacks can be used to update your custom submit button
// Ensuring that your button has the correct state and content for a specific scene
// Callback for receiving the submit button's visible state in the current scene
onVisible: (isVisible: boolean, context: { currentSceneId: string; } ) => void;
// Callback for receiving the submit button's disabled state in the current scene
onDisable: (isDisabled: boolean, context: { currentSceneId: string }) => void;
// Callback for receiving the submit button's loading state in the current scene
onLoading: (isLoading: boolean, context: { currentSceneId: string }) => void;
// Callback for receiving the submit button's content in the current scene
onContentChange: (content: string, context: { currentSceneId: string }) => void;
};
processingIndicator?: {
// Show a processing indicator overlay on top of the checkout when submitting a form
// Default to false if the submit button is visible
// Default to true if the submit button is hidden
visible?: boolean;
};
// Handles the error message displayed below the submit button when an error occurs
errorMessage?: {
// Disable the appearance of the default error message
// Default to false
disabled?: boolean;
// A callback for when the error message is displayed
onErrorMessageShow?: (message: string) => void;
// A callback for when the error message is hidden
onErrorMessageHide?: () => void;
}
/* PAYMENT METHOD SPECIFIC */
// Card networks allowed for checkout
// An error will be showed to the customer if they attempt to pay with a card network that is not allowed, whether it is using a card form, or via Google Pay/Apple Pay/...
// By default, all card networks are allowed
allowedCardNetworks: CardNetwork[];
card?: CheckoutCardOptions;
paypal?: PayPalOptions;
googlePay?: GooglePayOptions;
applePay?: ApplePayOptions;
directDebit?: DirectDebitOptions;
redirect?: {
// URL to return to in case of a redirect
returnUrl?: string;
// Default to false. Set to true to force a redirect for testing purposes.
forceRedirect?: boolean;
}
}
type CardNetwork =
| 'american-express'
| 'diners-club'
| 'discover'
| 'elo'
| 'hiper'
| 'hipercard'
| 'interac'
| 'jcb'
| 'maestro'
| 'mastercard'
| 'mir'
| 'unionpay'
| 'visa';
type SuccessCallbackReturnType =
| /* Show success screen (for backward compability) */ undefined
| /* Show success screen */ true
| /* Refresh client token and perform new step */ { clientToken: string };
Client session actions
The checkout has the ability to return actions in the onClientSessionActions
callback. These actions should be sent to your backend and forwarded to Primer's /client-session/actions
endpoint. Actions have the ability to mutate the client session when certain events occur, based on pre-defined conditions. The following types describe what could be returned in onClientSessionActions
:
// The data returned in onClientSessionActions
type ClientSessionActionData = {
actions: ClientSessionAction[];
clientToken: string;
};
type ClientSessionAction = {
type: ActionType;
params: ActionsParameters;
};
// The currently supported action types forwarded to onClientSessionActions
type ActionType = 'SELECT_PAYMENT_METHOD' | 'UNSELECT_PAYMENT_METHOD';
type ActionParameters = SelectPaymentMethodParamters | DefaulActionParameters;
type SelectPaymentMethodParamters = {
paymentMethodType: string;
binData?: BinData;
};
type DefaulActionParameters = {
type: string;
};
CHECKOUT
Specific options to type CheckoutOptions = {
// By default, all payment methods configured in the dashboard are proposed in the Checkout
// This filters the payment methods available
allowedPaymentMethods?: PaymentMethodType[];
// Additional information relating to merchant operating address. Used for tax calculation
businessDetails?: {
address: {
countryCode: string;
state: string;
postalCode: string;
city: string;
addressLine1: string;
};
nexusAddresses: [
{
countryCode: string;
state: string;
postalCode: string;
city: string;
addressLine1: string;
},
];
};
// Additional information relating to customer. Details can be used for tax calculation
customerDetails?: {
customerTaxId: string;
shippingAddress: {
countryCode: string;
state: string;
postalCode: string;
city: string;
addressLine1: string;
};
};
// Options regarding the vault and one-click checkout feature
vault?: {
// Fetch the vaulted payment methods of the customerId of the current session and display them
// Default to true
visible?: boolean;
// Disable the option to delete a saved payment method.
// Default to false
deletionDisabled?: boolean;
};
// A callback for when a resume action succeeds. This will receive the resume token used to resume a payment
onResumeSuccess: (data: ResumeToken) => Promise<SuccessCallbackReturnType>;
// A callback for when an error happened when resuming
onResumeError: (error: PrimerClientError) => void;
};
enum PaymentMethodType {
PAYMENT_CARD = 'PAYMENT_CARD',
APPLE_PAY = 'APPLE_PAY',
GOOGLE_PAY = 'GOOGLE_PAY',
PAYPAL = 'PAYPAL',
GO_CARDLESS = 'GOCARDLESS',
}
interface FormOptions {
inputLabelsVisible?: boolean; // Show or hide form input labels
}
type SceneOptions = {
// A callback for receiving the current scene that is entering
onEntering?: (sceneId: string) => void;
// Specify options relating to scene transitions
// Setting to false disables all scene transitions
// Defaults to a 'SLIDE_UP' type transition with a duration of 700ms
transition?: SceneTransitionOptions | false
}
type SceneTransitionOptions = {
// The type of transition animation which will be used
type: TransitionType;
// The duration of the transition animation
duration: number;
};
type TransitionType =
| 'SLIDE_UP'
| 'SLIDE_DOWN'
| 'SLIDE_HORIZONTAL
onTokenizeSuccess
and onResumeSuccess
Dynamic Checkout Flow: When a payment method is chosen and the customer clicks 'Pay', the payment method is tokenized and you'll receive a payment method token in the onTokenizeSuccess
callback. Send it to your server to create a payment using Primer's Payments API.
The checkout stays in a loading state while you perform this request. The Payments API may return a new client token: pass it as a return value of onTokenizeSuccess
to perform the action tied to the new token, such as performing 3DS.
// Called after a payment method is tokenized
// The checkout stays is a loading state until this Promise is resolved or rejected
async onTokenizeSuccess(paymentMethod) {
// Send the payment method token to your server to then create a payment
const response = await sendPaymentMethodToken(paymentMethod);
// If a new client token is available, resolve the Promise with it to refresh the checkout session
// The checkout will automatically perform the action required by the Workflow
// e.g. Perform a 3D Secure challenge
if (response.clientToken) {
return { clientToken: response.clientToken };
}
// Display the success screen
return true;
}
When the new action commanded by the Payments API is completed, you'll receive a resume token via the onResumeSuccess
callback. Send it to your server to resume a payment using Primer's Payments API.
The checkout stays in a loading state while you perform this request. The Payments API may return a new client token: pass it as a return value of onResumeSuccess
to perform the action tied to the new token.
// Called after an extra-step - such as performing 3D Secure - is completed
// The checkout stays is a loading state until this Promise is resolved or rejected
async onResumeSuccess({resumeToken}) {
// Send the payment method token to your server to then create a payment
const response = await sendResumeToken(resumeToken);
// If a new client token is available, resolve the Promise with it to refresh the checkout session
// The checkout will automatically perform the action required by the Workflow
if (response.clientToken) {
// e.g. Trigger a 3D Secure challenge
return { clientToken: response.clientToken };
}
// Display the success screen
return true;
}
MANAGE_PAYMENT_METHODS
Specific options to orderDetails?: {
currencyCode: string;
};
// Disable the option to delete a saved payment method.
// Default to false
deletionDisabled?: boolean;
SINGLE_PAYMENT_METHOD_CHECKOUT
Specific options to type SinglePaymentMethodCheckoutOptions = {
// The single payment method to render
// Only "GOCARDLESS" at the moment
paymentMethod: PaymentMethodType;
};
Specific to payment methods
CheckoutCardOptions
type CheckoutCardOptions = {
// A stylesheet to inject into each input element
// For more information see the guide on styling
css?: string;
// deprecated after v1.2.0
// Choose if the cardholder name should be required or not
// default value is true
cardholderNameRequired?: boolean;
cardholderName?: {
// Choose if the cardholder name should be visible
// default value is true
visible?: boolean;
// Choose if the cardholder name should be required
// If visible, the default value is true
required?: boolean;
// Placeholder text which will be displayed within input
// defaults to localized placeholder if not provided
placeholder?: string;
};
cardNumber?: {
// Placeholder card number which will be displayed within input
// default card number placeholder displayed if not provided
placeholder?: string;
};
expiryDate?: {
// Placeholder expiry date which will be displayed within input
// default expiry date placeholder displayed if not provided
placeholder?: Label;
};
cvv?: {
// Placeholder cvv which will be displayed within input
// default cvv placeholder displayed if not provided
placeholder?: Label;
};
// Choose if the card form should be embedded in the home scene
// Or have a dedicated card scene accessible by a card button
// Note that the preferredFlow will not necessarily be the chosen flow
preferredFlow?: CardPreferredFlow;
// Checkout will ask the customer id they want to save their card.
// To disable this feature provide a value for the vault option.
// When true: the card will be vaulted
// When false: the card will not be vaulted
vault?: boolean | () => boolean;
};
// DEDICATED_SCENE: can be used if more than one payment method is available
// EMBEDDED_IN_HOME: is provided by default. It will override the dedicated scene option
// When card is the only available payment method
type CardPreferredFlow = 'DEDICATED_SCENE' | 'EMBEDDED_IN_HOME';
PayPalOptions
type PayPalOptions = {
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;
paymentFlow?: PaymentFlow;
};
DirectDebitOptions
type DirectDebitOptions = {
customerCountryCode: Alpha2CountryCode;
// The following options are used in the Direct Debit mandate
companyName: string;
companyAddress: string;
// The following options are used to prefill the Direct Debit form
customerName?: string;
customerEmail?: string;
customerAddressLine1?: string;
customerAddressLine2?: string;
customerCity?: string;
customerPostalCode?: string;
iban?: string;
// Label of the submit button
submitButtonLabels?: {
// Label of the submit button in the form
form?: string;
// Label of the submit button in the mandate
mandate: string;
};
};
ApplePayOptions
type ApplePayOptions = {
buttonType?:
| 'plain'
| 'buy'
| 'set-up'
| 'donate'
| 'check-out'
| 'book'
| 'subscribe';
buttonStyle?: 'white' | 'white-outline' | 'black';
};
GooglePayOptions
buttonType?: "long" | "short";
buttonColor?: "default" | "black" | "white";
Setters
The checkout provides setter methods which can be called to update options after initialization.
const checkout = await primer.checkout(checkoutOptions);
checkout.setClientToken(clientToken);
Manual Form Submission
The checkout provides a method for manually calling submit. This is useful when using your own custom submit button.
const checkout = await primer.checkout(checkoutOptions);
const handleMySubmitButtonClick = () => {
// Forward all submit button clicks to the SDK
checkout.submit();
};
Style
interface CheckoutStyle {
fontFaces?: Array<FontFace>; // Injected into hosted fields
stylesheets?: Array<Stylesheet>; // Injected into hosted fields
loadingScreen?: {
// Color of the loading screen indicator
color?: string;
};
// Style of the inputs
input?: {
// Base style
base?: InputStyle & {
hover?: InputStyle; // :hover
focus?: InputStyle; // :focus
placeholder?: InputStyle; // ::placeholder
webkitAutofill?: InputStyle; // :-webkit-autofill
selection?: InputStyle; // ::selection
};
// Error
error?: InputStyle & {
hover?: InputStyle;
focus?: InputStyle;
placeholder?: InputStyle;
webkitAutofill?: InputStyle;
selection?: InputStyle;
};
};
// Style of the label displayed above the input fields
inputLabel?: TextStyle;
// Style of the error messages displayed below the input fields
inputErrorText?: TextStyle & TextAlignmentStyle;
formSpacings?: {
// Vertical spacing between a label and an input field
betweenLabelAndInput?: string;
// Vertical spacing between inputs
betweenInputs: string;
};
showMorePaymentMethodsButton?: {
base?: TextStyle;
disabled?: TextStyle;
}
// Style APM buttons
// PayPal, Apple Pay, Google Pay and Klarna can only be styled in height and borderRadius.
paymentMethodButton: {
background?: string;
borderRadius?: number | string;
boxShadow?: string;
borderColor?: string;
height?: number;
primaryText?: TextStyle;
logoColor?: logoColor;
marginTop?: string;
};
submitButton?: {
base?: {
hover?: TextStyle & BlockStyle;
focus?: TextStyle & BlockStyle;
};
loading?: {
hover?: TextStyle & BlockStyle;
focus?: TextStyle & BlockStyle;
};
};
// Small print in direct debit
smallPrint?: TextStyle;
directDebit?: {
mandate?: {
header?: TextStyle;
label?: TextStyle;
content?: TextStyle;
creditorDetails?: TextStyle:
};
success?: {
icon?: {
color?: string;
}
};
};
backButton?: {
color?: string;
};
// Pop-up menu to manage the vaulted payment methods
vaultMenu?: {
// Pencil icon
editButton?: {
// Backend of the pencil icon
background?: string;
// Color of the pencil icon
color?: string;
};
item?: {
label?: textStyle;
// Delete & Cancel button
actionButton?: TextStyle;
confirmButton?: TextStyle & BlockStyle;
};
}
}
interface FontFace {
fontFamily?: string;
src?: string;
unicodeRange?: string;
fontVariant?: string;
fontFeatureSettings?: string;
fontVariationSettings?: string;
fontStretch?: string;
fontWeight?: string;
fontStyle?: string;
}
interface Stylesheet {
href: string;
}
interface TextStyle {
color?: string;
fontFamily?: string;
fontWeight?: string;
fontSize?: string;
fontSmoothing?: string;
lineHeight?: string;
textTransform?: string;
letterSpacing?: string;
}
interface BlockStyle {
background?: string;
borderRadius?: number | string;
boxShadow?: string;
borderStyle?: string;
borderColor?: number | string;
borderWidth?: number | string;
}
interface TextAlignmentStyle {
textAlign?: string;
}
type IconColor = 'black' | 'white' | 'color';
Input Style
interface InputStyle {
height?: number;
paddingHorizontal?: number;
background?: string;
borderRadius?: number | string;
boxShadow?: string;
borderStyle?: string;
borderColor?: number | string;
borderWidth?: number | string;
color?: string;
fontFamily?: string;
fontWeight?: string;
fontSize?: string;
fontSmoothing?: string;
lineHeight?: string;
}
Success Screen
By default, what happens after onTokenizeSuccess
is called (and the Promise is resolved) depends on the payment method that the customer chooses. Some of them such as GoCardless requires us to display a screen that sums up the order to be displayed. Others, such as Card, does not require anything.
However, it is good practice to show a success screen once the payment is validated. You can use the successScreen
options to define which of the built-in success screens you want to show, or to disable the default behavior and display a custom success screen.
Note that when the successScreen
is undefined
, the CHECK
success screen will be used by default. Also, when explicitly using { type: 'CHECK' }
a custom title can be defined.
enum SuccessScreenType {
PAYMENT_METHOD = 'PAYMENT_METHOD',
CHECK = 'CHECK',
}
export type CheckSuccessScreenOptions = {
type: SuccessScreenType.CHECK;
title: Label;
};
export type PaymentMethodSuccessScreenOptions = {
type: SuccessScreenType.PAYMENT_METHOD;
};
type SuccessScreenOptions =
| /* No success screen will be displayed */ false
| /* Show the default success screen of the payment method*/ undefined
// Proposed success screens
| CheckSuccessScreenOptions
| PaymentMethodSuccessScreenOptions;
Primer (Advanced usage)
Primer#render(options: PrimerClientRenderOptions): Promise<void>
Securely render a credit card form or alternative payment method buttons.
type PrimerClientRenderOptions
type PrimerClientRenderOptions {
// Override the locale
// By default translations will be provided for the browser's locale
locale?: string;
// Additional information relating to merchant operating address
// [required for tax calculation via TaxJar]
businessDetails?: {
address: {
countryCode: string;
state: string;
postalCode: string;
city: string;
addressLine1: string;
},
nexusAddresses: [
{
countryCode: string;
state: string;
postalCode: string;
city: string;
addressLine1: string;
},
],
},
// Additional information relating to customer
// [required for tax calculation via TaxJar]
customerDetails?: {
customerTaxId: string;
shippingAddress: {
countryCode: string;
state: string;
postalCode: string;
city: string;
addressLine1: string;
},
},
// A success callback, receives the payment method token.
onTokenizeSuccess: (data: PaymentMethodToken) => void;
// A callback for when tokenization begins
onTokenizeStart?: () => void;
// A callback for when there is a tokenization error
onTokenizeError?: (message: string) => void;
// A callback for when tokenization ends
onTokenizeEnd?: () => void;
// A callback for when changes have been made to the totalAmount provided to the checkout
onAmountChange?: (data: AmountChange) => void;
// A callback for when changes totalAmount is being updated
onAmountChanging?: (isChanging: boolean) => void;
// A callback for when an error occured while updating the totalAmount
onAmountChangeError?: (message: PrimerError) => void;
// A callback for receiving actions which can be used to update the client session
onClientSessionActions?: (data: ClientSessionActionData) => Promise<{ clientToken: string } | false>;
// Configuration for a credit card form
card?: {
// The name of the cardholder
// If a function is set, the function will be called when calling the `tokenize` function
cardholderName?: string | () => string;
// A callback for form metadata
onChange?: (state: FormState) => void;
// A callback for extra card information (brand etc)
onCardMetadata?: (meta: CardMetadata) => void;
// Whether or not the form is disabled - this will prevent tokenization if it returns true
disabled?: () => boolean;
// A stylesheet to inject into each input element
// For more information see the guide on styling
css?: string;
// A DOM selector for a button which will begin the tokenization
submitButton: string;
// Credit card field configuration
fields: {
// Card number field configuration
cardNumber: CreditCardFieldConfig;
// Expiry Date field configuration
expiryDate: CreditCardFieldConfig;
// CVV field configuration
cvv: CreditCardFieldConfig;
};
};
// Configuration for alternative payment methods
// The container property should be a DOM selector for a visible element n the page.
applePay?: { container: string; };
googlePay?: { container: string; };
paypal?: { container: string; };
}
type PaymentMethodToken
A tokenized payment method. Use the token property to authorize transactions. The token object also contains some other metadata describing the payment method which it represents.
type PaymentMethodToken {
// The token used to authorize transactions
token: string;
// The type of payment method which this token represents
paymentMethodType: 'PAYMENT_CARD' | 'GOOGLE_PAY' | 'APPLE_PAY' | 'PAYPAL';
// Additional data about the payment method
paymentMethodData: object;
}
type FormState
Form metadata
type FormState {
// Whether any field is dirty
dirty: boolean;
// Whether any field was been touched
touched: boolean;
// Whether any field is active
active: boolean;
// Whether all fields are valid
valid: boolean;
// Whether the card form was submitted
submitted: boolean;
}
type CardMetadata
Additional Card information
type CardMetadata {
// The card type - 'visa' | 'mastercard' etc
type?: string;
// The possible types of the card given the current value
possibleTypes: string[];
}
type CreditCardFieldConfig
Configuration for credit card fields
type CreditCardFieldConfig {
// A DOM selector specifying where to render the input
container: string;
// Whether to append or prepend the input to the container element
placement?: 'append' | 'prepend';
// A placeholder to display when the input is empty
placeholder?: string;
// A callback which receives input metadata
onChange: (data: { meta: FieldMetadata; }) => void;
}
type FieldMetadata
Input metadata for a Credit card field
type FieldMetadata {
// A validation error
error?: string;
// Whether or not the input contains valid information
valid: boolean;
// Whether or not the input is focussed
active: boolean;
// Whether or not the input's value has been changed
dirty: boolean;
// Whether or not the customer has focussed the field
touched: boolean;
// Whether or not the form has been submitted
submitted: boolean;
}
Primer#threeDSecure: ThreeDSecure
Primer 3DS verification API
ThreeDSecure#verify(options: ThreeDSecureVerifyOptions): Promise<ThreeDSecureVerification>
Perform a 3DS verification on a payment method token
type ThreeDSecureVerifyOptions
Options provided to 3DS verification
type ThreeDSecureVerifyOptions {
// A Payment method token to perform 3DS verification for
token: string;
// An element selector for the parent of the 3DS challenge UI
container: string;
// A callback for when the 3DS challenge starts
onChallengeStart?: () => void;
// A callback for when the 3DS challenge ends
onChallengeEnd?: () => void;
// 3DS order details
order: {
// Your order ID
orderId: string;
amount: {
// The sale amount
value: number | string;
// The alpha3 currency code of the sale
currency: string;
};
// The customer's email address
email: string;
billingAddress: {
// Billing first name
firstName: string;
// Billing last name
lastName: string;
// Street address
addressLine1: string;
// Extended street address
addressLine2?: string
// Extended street address
addressLine3?: string
// City or town
city: string;
// State, region or province
state?: string;
// The alpha2 country code of the address
countryCode: string;
// The postal or zip code
postalCode: string;
}
};
}
type ThreeDSecureVerification
The result of a 3DS verification. If successful, a new token will be returned.
{
// The result status of the verification
status: 'AUTH_SUCCESS' | 'AUTH_FAILED' | 'SKIPPED';
// Optional error if something unexpected happened
error?: Error;
// Data object containing the new token
data?: PaymentMethodToken;
}