14 KiB
Primer Web Components - Component Reference
Core Components
<primer-checkout>
The root component that initializes the Primer SDK and manages the checkout flow.
Required Attributes:
client-token- JWT token from backend client session
Optional Attributes:
custom-styles- JSON string of CSS custom propertiesloader-disabled- Boolean to disable loading indicator
Properties (set via JavaScript):
options- SDK configuration object (locale, payment methods, etc.)
Example:
<primer-checkout client-token="eyJ0eXAiOiJKV1QiLCJhbGc...">
<primer-main slot="main">
<!-- Content -->
</primer-main>
</primer-checkout>
<primer-main>
Container for checkout content with predefined slots for different states.
Slots:
payments- Main payment method selection areacheckout-complete- Success state contentcheckout-failure- Error state content
Example:
<primer-main slot="main">
<div slot="payments">
<primer-payment-method type="PAYMENT_CARD"></primer-payment-method>
</div>
<div slot="checkout-complete">
<h2>Payment Successful!</h2>
</div>
</primer-main>
<primer-payment-method>
Displays a specific payment method.
Attributes:
type- Payment method type (PAYMENT_CARD, PAYPAL, APPLE_PAY, GOOGLE_PAY, etc.)disabled- Boolean to disable the payment method
Example:
<primer-payment-method type="PAYMENT_CARD"></primer-payment-method>
<primer-payment-method type="PAYPAL"></primer-payment-method>
<primer-payment-method type="GOOGLE_PAY" disabled></primer-payment-method>
<primer-payment-method-container>
Declarative container for automatically rendering filtered payment methods.
Attributes:
include- Comma-separated list of payment method types to includeexclude- Comma-separated list of payment method types to exclude
Example:
<!-- Show only digital wallets -->
<primer-payment-method-container
include="APPLE_PAY,GOOGLE_PAY"
></primer-payment-method-container>
<!-- Show everything except cards -->
<primer-payment-method-container
exclude="PAYMENT_CARD"
></primer-payment-method-container>
Card Form Components
<primer-card-form>
Container for card payment inputs with built-in validation and state management.
Slots:
card-form-content- Custom content area for inputs
Example:
<primer-card-form>
<div slot="card-form-content">
<primer-input-card-number></primer-input-card-number>
<primer-input-card-expiry></primer-input-card-expiry>
<primer-input-cvv></primer-input-cvv>
<primer-card-form-submit></primer-card-form-submit>
</div>
</primer-card-form>
<primer-input-card-number>
Secure card number input field with automatic card type detection.
DOM Structure:
<primer-input-wrapper>
<primer-input-label slot="label">Card Number</primer-input-label>
<div slot="input">
<!-- Secure iframe for card number -->
<primer-card-network-selector></primer-card-network-selector>
</div>
</primer-input-wrapper>
<primer-input-card-expiry>
Expiration date input with automatic formatting (MM/YY).
<primer-input-cvv>
CVV/security code input field.
<primer-input-card-holder-name>
Cardholder name text input (not in secure iframe).
<primer-card-form-submit>
Localized submit button for card forms.
<primer-billing-address>
Collects billing address information (SDK Core only).
Attributes:
- Mode configuration (drop-in or custom layout)
Example:
<primer-card-form>
<div slot="card-form-content">
<primer-input-card-number></primer-input-card-number>
<primer-billing-address></primer-billing-address>
<primer-card-form-submit></primer-card-form-submit>
</div>
</primer-card-form>
Base UI Components
<primer-input-wrapper>
Container that provides consistent styling for form inputs.
Attributes:
has-error- Boolean to show error state
Slots:
label- For primer-input-labelinput- For primer-input or custom contenterror- For primer-input-error
Example:
<primer-input-wrapper>
<primer-input-label slot="label">Email</primer-input-label>
<primer-input slot="input" type="email"></primer-input>
<primer-input-error slot="error">Invalid email</primer-input-error>
</primer-input-wrapper>
<primer-input>
Standard input field with consistent styling.
Attributes:
- Standard HTML input attributes:
type,value,placeholder,disabled,required, etc. name- For form data collection
Events:
input- Value changedchange- Value committed (blur/Enter)focus- Input focusedblur- Input blurredinvalid- Validation failed
<primer-input-label>
Form label component.
Attributes:
for- ID of associated inputdisabled- Boolean for disabled state
<primer-input-error>
Error message component.
Attributes:
for- ID of associated inputactive- Boolean to show/hide error
<primer-button>
Styled button component.
Attributes:
variant- "primary" or "secondary"buttonType- "button" or "submit"disabled- Boolean
Example:
<primer-button variant="primary">Pay Now</primer-button>
<primer-button variant="secondary">Cancel</primer-button>
Utility Components
<primer-error-message-container>
Automatically displays payment processing errors.
Example:
<div slot="payments">
<primer-payment-method type="PAYMENT_CARD"></primer-payment-method>
<primer-error-message-container></primer-error-message-container>
</div>
<primer-vault-manager>
Displays saved payment methods when vault is enabled.
Requires:
- Vault enabled in SDK options:
{"vault": {"enabled": true}}
<primer-show-other-payments>
Manages visibility of alternative payment methods when vault is active.
Slots:
other-payments- Content shown when "Show other payments" is clicked
Events
Core Checkout Events
primer:ready
- Fired when SDK initialization completes
- Detail: PrimerJS instance with callbacks:
onPaymentSuccess- New in v0.7.0onPaymentFailure- New in v0.7.0onVaultedMethodsUpdate- New in v0.7.0onPaymentStart,onPaymentPreparerefreshSession(),getPaymentMethods()
Example:
checkout.addEventListener('primer:ready', (event) => {
const primer = event.detail;
// Set up callbacks
primer.onPaymentSuccess = ({ paymentSummary, paymentMethodType }) => {
console.log('Payment successful!');
};
primer.onPaymentFailure = ({ error, paymentMethodType }) => {
console.error('Payment failed:', error.message);
};
});
primer:state-change
- Fired when checkout state changes
- Detail:
{isProcessing, isSuccessful, isLoading, primerJsError, paymentFailure} - Note:
error→primerJsError,failure→paymentFailure(v0.7.0)
Example:
checkout.addEventListener('primer:state-change', (event) => {
const { isProcessing, isSuccessful, primerJsError, paymentFailure } =
event.detail;
if (primerJsError) {
console.error('SDK error:', primerJsError);
}
if (paymentFailure) {
console.error('Payment failed:', paymentFailure);
}
});
primer:methods-update
- Fired when available payment methods change
- Detail: InitializedPayments instance with
.toArray(),.get(),.size()methods - Note: Replaces
primer-payment-methods-updated
Example:
checkout.addEventListener('primer:methods-update', (event) => {
const methods = event.detail.toArray();
console.log(`${methods.length} payment methods available`);
// Get specific method
const cardMethod = event.detail.get('PAYMENT_CARD');
});
Payment Lifecycle Events (New in v0.7.0)
primer:payment-start
- Fired when payment processing begins
- Detail: undefined
primer:payment-success
- Fired when payment completes successfully
- Detail:
{paymentSummary, paymentMethodType, timestamp} - PaymentSummary is PII-filtered (safe for client-side)
Example:
checkout.addEventListener('primer:payment-success', (event) => {
const { paymentSummary, paymentMethodType, timestamp } = event.detail;
console.log(`✅ Payment successful via ${paymentMethodType}`);
console.log(
`Card: ${paymentSummary.network} ending in ${paymentSummary.last4Digits}`,
);
// Navigate to success page
});
primer:payment-failure
- Fired when payment fails
- Detail:
{error: {code, message, diagnosticsId}, paymentSummary?, paymentMethodType, timestamp}
Example:
checkout.addEventListener('primer:payment-failure', (event) => {
const { error, paymentMethodType } = event.detail;
console.error(`❌ Payment failed: ${error.message}`);
console.error(`Diagnostics ID: ${error.diagnosticsId}`);
// Show error to user
});
Vault Events (New in v0.7.0)
primer:vault:methods-update
- Fired when vaulted payment methods loaded/updated
- Detail:
{vaultedPayments, timestamp} - vaultedPayments API:
.toArray(),.get(id),.size()
Example:
checkout.addEventListener('primer:vault:methods-update', (event) => {
const { vaultedPayments } = event.detail;
console.log(`${vaultedPayments.size()} saved payment methods`);
vaultedPayments.toArray().forEach((method) => {
console.log(
`${method.paymentInstrumentType}: ${method.paymentInstrumentData.last4Digits}`,
);
});
});
Card Events
primer:card-network-change
- Fired when card network detected
- Detail:
{detectedCardNetwork, selectableCardNetworks, isLoading}
Example:
cardForm.addEventListener('primer:card-network-change', (event) => {
const { detectedCardNetwork, selectableCardNetworks } = event.detail;
console.log(`Detected: ${detectedCardNetwork}`);
});
primer:card-success
- Fired when card form submission succeeds
- Detail:
{result}
primer:card-error
- Fired when card validation errors occur
- Detail:
{errors: InputValidationError[]}
primer:card-submit (Triggerable)
- Dispatch this event to trigger card form submission programmatically
- Detail:
{source?: string}
Example:
// Trigger card form submission from external button
const cardForm = document.querySelector('primer-card-form');
cardForm.dispatchEvent(
new CustomEvent('primer:card-submit', {
detail: { source: 'external-button' },
}),
);
SDK Options Structure
interface SDKOptions {
// Core Options
sdkCore?: boolean; // Default: true since v0.4.0
locale?: string; // e.g., 'en-GB', 'fr-FR'
merchantDomain?: string; // For Apple Pay domain validation
disabledPayments?: boolean; // Disable all payment methods
enabledPaymentMethods?: PaymentMethodType[]; // Filter which methods display
// Card Options
card?: {
cardholderName?: {
required?: boolean;
visible?: boolean;
};
};
// Apple Pay Options
applePay?: {
buttonType?:
| 'buy'
| 'donate'
| 'plain'
| 'checkout'
| 'set-up'
| 'book'
| 'subscribe';
buttonStyle?: 'black' | 'white' | 'white-outline';
billingOptions?: {
requiredBillingContactFields?: (
| 'emailAddress'
| 'name'
| 'phoneNumber'
| 'postalAddress'
| 'phoneticName'
)[];
};
shippingOptions?: {
requiredShippingContactFields?: (
| 'emailAddress'
| 'name'
| 'phoneNumber'
| 'postalAddress'
| 'phoneticName'
)[];
requireShippingMethod?: boolean;
};
};
// Google Pay Options
googlePay?: {
buttonType?:
| 'long'
| 'short'
| 'book'
| 'buy'
| 'checkout'
| 'donate'
| 'order'
| 'pay'
| 'plain'
| 'subscribe';
buttonColor?: 'default' | 'black' | 'white';
buttonSizeMode?: 'fill' | 'static';
captureBillingAddress?: boolean;
emailRequired?: boolean;
requireShippingMethod?: boolean;
};
// PayPal Options
paypal?: {
style?: {
layout?: 'vertical' | 'horizontal';
color?: 'gold' | 'blue' | 'silver' | 'white' | 'black';
shape?: 'rect' | 'pill';
height?: number; // 25-55
label?: 'paypal' | 'checkout' | 'buynow' | 'pay' | 'installment';
tagline?: boolean;
borderRadius?: number; // 0-55
disableMaxWidth?: boolean;
};
disableFunding?: string[]; // ['credit', 'card', 'paylater', etc.]
enableFunding?: string[]; // ['venmo', etc.]
vault?: boolean;
buyerCountry?: string; // Sandbox only
debug?: boolean;
};
// Klarna Options
klarna?: {
paymentFlow?: 'DEFAULT' | 'PREFER_VAULT';
allowedPaymentCategories?: ('pay_now' | 'pay_later' | 'pay_over_time')[];
buttonOptions?: {
text?: string;
};
};
// Vault Options
vault?: {
enabled?: boolean;
showEmptyState?: boolean;
};
// Stripe Options
stripe?: {
mandateData?: {
fullMandateText?: string;
merchantName?: string;
};
publishableKey?: string;
};
// Submit Button Options
submitButton?: {
amountVisible?: boolean;
useBuiltInButton?: boolean; // Set false for external buttons
};
}
CSS Custom Properties
Brand Colors
--primer-color-brand- Primary brand color--primer-color-loader- Loading indicator color--primer-color-focus- Focus state color
Typography
--primer-typography-brand- Font family
Spacing & Sizing
--primer-space-base- Base spacing unit (default: 4px)--primer-size-base- Base size unit (default: 4px)--primer-radius-base- Border radius (default: 4px)
Theme-Specific Variables
.primer-light-theme {
--primer-color-text-primary: var(--primer-color-gray-900);
--primer-color-background-outlined-default: var(--primer-color-gray-000);
}
.primer-dark-theme {
--primer-color-text-primary: var(--primer-color-gray-100);
--primer-color-background-outlined-default: var(--primer-color-gray-800);
}
Usage Example
:root {
--primer-color-brand: #2f98ff;
--primer-radius-base: 8px;
--primer-typography-brand: 'Inter, sans-serif';
}
/* Or apply to specific checkout */
primer-checkout {
--primer-color-brand: #4a6cf7;
--primer-radius-base: 4px;
}