Try Live
Add Docs
Rankings
Pricing
Docs
Install
Theme
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Primer Components
https://github.com/primer-io/examples
Admin
This repository provides public documentation and integration examples for Primer's Beta Primer
...
Tokens:
95,047
Snippets:
884
Trust Score:
7.7
Update:
2 months ago
Context
Skills
Chat
Benchmark
81.4
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Primer Checkout SDK Primer Checkout is a Web Components-based payment SDK for building modern checkout integrations. It provides a composable, framework-agnostic approach to payment processing using Custom Elements, Shadow DOM, and CSS Custom Properties for theming. The SDK supports multiple payment methods including cards, Apple Pay, Google Pay, PayPal, Klarna, BLIK, and redirect-based Alternative Payment Methods (APMs). The SDK is designed for modern browsers (Chrome, Firefox, Safari, Edge) and works seamlessly with any JavaScript framework including React, Vue, and vanilla JavaScript. It provides both drop-in checkout experiences and granular control for fully customized payment flows through a component-based architecture with secure iframe-based card inputs. ## Installation Install the SDK and initialize Primer in your application. ```bash npm install @primer-io/primer-js ``` ```javascript import { loadPrimer } from '@primer-io/primer-js'; // Initialize Primer SDK await loadPrimer(); // Get reference to checkout component const checkout = document.querySelector('primer-checkout'); // Set client token (obtained from your backend) checkout.setAttribute('client-token', 'your-client-token'); ``` ## Basic Drop-in Checkout The simplest integration using the checkout component as a complete drop-in solution. ```html <!-- Minimal drop-in checkout - displays all available payment methods automatically --> <primer-checkout client-token="your-client-token"></primer-checkout> ``` ```javascript import { loadPrimer } from '@primer-io/primer-js'; await loadPrimer(); const checkout = document.querySelector('primer-checkout'); checkout.setAttribute('client-token', 'your-client-token'); // Listen for SDK initialization checkout.addEventListener('primer:ready', (event) => { const primerJS = event.detail; console.log('✅ Primer SDK ready'); // Configure payment success handler primerJS.onPaymentSuccess = ({ paymentSummary, paymentMethodType }) => { console.log(`Payment successful! Card: ${paymentSummary.network} ending in ${paymentSummary.last4Digits}`); window.location.href = '/order-confirmation'; }; // Configure payment failure handler primerJS.onPaymentFailure = ({ error, paymentMethodType }) => { console.error('Payment failed:', error.message); // Display error to user }; }); // Monitor checkout state changes checkout.addEventListener('primer:state-change', (event) => { const { isProcessing, isSuccessful, paymentFailure } = event.detail; if (isProcessing) { console.log('Processing payment...'); } if (paymentFailure) { console.error('Payment failure:', paymentFailure.message); } }); ``` ## SDK Options Configuration Configure SDK behavior, payment methods, and features through the options property. ```javascript import { PaymentMethodType } from '@primer-io/primer-js'; const checkout = document.querySelector('primer-checkout'); checkout.setAttribute('client-token', 'your-client-token'); // Configure SDK options checkout.options = { // Core options locale: 'en-GB', sdkCore: true, // Default: true (SDK Core enabled) // Enable specific payment methods enabledPaymentMethods: [ PaymentMethodType.PAYMENT_CARD, PaymentMethodType.APPLE_PAY, PaymentMethodType.GOOGLE_PAY, PaymentMethodType.PAYPAL, ], // Card form options card: { cardholderName: { visible: true, required: true, defaultValue: 'John Doe', // Pre-fill cardholder name }, }, // Apple Pay configuration applePay: { buttonType: 'buy', buttonStyle: 'black', billingOptions: { requiredBillingContactFields: ['postalAddress', 'emailAddress'], }, }, // Google Pay configuration googlePay: { buttonType: 'checkout', buttonColor: 'black', buttonSizeMode: 'fill', captureBillingAddress: true, emailRequired: true, }, // PayPal configuration paypal: { style: { color: 'gold', shape: 'rect', borderRadius: 22, }, }, // Submit button options submitButton: { amountVisible: true, useBuiltInButton: true, }, // Redirect options for APMs redirect: { returnUrl: 'https://example.com/checkout/complete', forceRedirect: false, }, }; ``` ## Custom Card Form Layout Build custom card form layouts using individual card input components. ```html <primer-checkout client-token="your-client-token"> <primer-main slot="main"> <div slot="payments"> <primer-card-form> <div slot="card-form-content"> <primer-input-card-holder-name></primer-input-card-holder-name> <primer-input-card-number></primer-input-card-number> <div style="display: flex; gap: 8px;"> <primer-input-card-expiry></primer-input-card-expiry> <primer-input-cvv></primer-input-cvv> </div> <button type="submit">Pay Now</button> </div> </primer-card-form> <!-- Error message container --> <primer-error-message-container></primer-error-message-container> </div> <!-- Custom completion screen --> <div slot="checkout-complete"> <h2>Thank you for your purchase!</h2> </div> </primer-main> </primer-checkout> ``` ```javascript const checkout = document.querySelector('primer-checkout'); // Handle card form submission success checkout.addEventListener('primer:card-success', (event) => { console.log('Card form submitted successfully:', event.detail.result); }); // Handle card form validation errors checkout.addEventListener('primer:card-error', (event) => { const errors = event.detail.errors; errors.forEach(error => { console.error(`${error.field}: ${error.error}`); }); }); // Programmatic form submission from external button document.getElementById('external-pay-button').addEventListener('click', () => { document.dispatchEvent(new CustomEvent('primer:card-submit', { bubbles: true, composed: true, detail: { source: 'external-button' } })); }); ``` ## Payment Method Container Declaratively filter and display specific payment methods. ```html <!-- Include only specific payment methods --> <primer-payment-method-container include="APPLE_PAY,GOOGLE_PAY"> </primer-payment-method-container> <!-- Exclude specific payment methods --> <primer-payment-method-container exclude="PAYMENT_CARD"> </primer-payment-method-container> ``` ```javascript // Listen for available payment methods checkout.addEventListener('primer:methods-update', (event) => { const paymentMethods = event.detail; console.log(`${paymentMethods.size()} payment methods available`); // Get all payment methods as array const methods = paymentMethods.toArray(); methods.forEach(method => { console.log(`Available: ${method.type}`); }); // Get specific payment method const cardMethod = paymentMethods.get('PAYMENT_CARD'); if (cardMethod) { console.log('Card payments available'); } }); ``` ## Vault Manager (Saved Payment Methods) Enable customers to save and reuse payment methods. ```javascript const checkout = document.querySelector('primer-checkout'); checkout.options = { vault: { enabled: true, showEmptyState: true, }, }; checkout.addEventListener('primer:ready', (event) => { const primerJS = event.detail; // Handle vaulted payment methods updates primerJS.onVaultedMethodsUpdate = ({ vaultedPayments, cvvRecapture }) => { console.log(`${vaultedPayments.size()} saved payment methods`); vaultedPayments.toArray().forEach(method => { console.log(`Saved: ${method.paymentInstrumentType} - ${method.paymentInstrumentData?.last4Digits}`); }); if (cvvRecapture) { console.log('CVV re-entry required for this payment method'); } }; }); // Listen for vault selection changes checkout.addEventListener('primer:vault:selection-change', (event) => { const { paymentMethodId } = event.detail; if (paymentMethodId) { console.log('Payment method selected:', paymentMethodId); } else { console.log('Payment method deselected'); } }); ``` ## Headless Vault Implementation Build completely custom vault UIs with full programmatic control. ```javascript const checkout = document.querySelector('primer-checkout'); checkout.options = { vault: { enabled: true, headless: true, // Hide default vault UI showEmptyState: false, }, }; checkout.addEventListener('primer:ready', (event) => { const primerJS = event.detail; primerJS.onVaultedMethodsUpdate = async ({ vaultedPayments, cvvRecapture }) => { const vaultContainer = document.getElementById('custom-vault'); vaultContainer.innerHTML = ''; // Render custom vault cards vaultedPayments.toArray().forEach((method, index) => { const card = document.createElement('div'); card.className = 'vault-card'; card.innerHTML = ` <input type="radio" name="vault-method" value="${method.id}" id="method-${index}"> <label for="method-${index}"> ${method.paymentInstrumentData?.network || 'Card'} •••• ${method.paymentInstrumentData?.last4Digits} </label> `; vaultContainer.appendChild(card); }); // Add CVV input if required if (cvvRecapture) { const cvvInput = await primerJS.vault.createCvvInput({ placeholder: 'CVV', }); if (cvvInput) { document.getElementById('cvv-container').appendChild(cvvInput); } } }; // Handle custom pay button document.getElementById('custom-pay-btn').addEventListener('click', async () => { try { await primerJS.vault.startPayment(); } catch (error) { console.error('Payment failed:', error); } }); // Delete vaulted payment method document.getElementById('delete-btn').addEventListener('click', async () => { const methodId = document.querySelector('input[name="vault-method"]:checked')?.value; if (methodId) { await primerJS.vault.delete(methodId); console.log('Payment method deleted'); } }); }); ``` ## Payment Lifecycle Events Handle payment events throughout the checkout flow. ```javascript const checkout = document.querySelector('primer-checkout'); // SDK ready event checkout.addEventListener('primer:ready', (event) => { const primerJS = event.detail; // Payment start callback primerJS.onPaymentStart = () => { console.log('Payment creation started'); showLoadingSpinner(); }; // Payment preparation callback (validate before payment) primerJS.onPaymentPrepare = (data, handler) => { console.log('Preparing payment for:', data.paymentMethodType); // Validate order (e.g., check stock availability) if (isOrderValid()) { handler.continuePaymentCreation(); } else { handler.abortPaymentCreation(); showError('Order validation failed'); } }; // Payment success callback primerJS.onPaymentSuccess = ({ paymentSummary, paymentMethodType, timestamp }) => { console.log('Payment successful!'); console.log(`Method: ${paymentMethodType}`); console.log(`Card: ${paymentSummary.network} ending in ${paymentSummary.last4Digits}`); // Redirect to confirmation window.location.href = '/order-confirmation'; }; // Payment failure callback primerJS.onPaymentFailure = ({ error, paymentMethodType, paymentSummary }) => { console.error('Payment failed:', error.message); console.error('Error code:', error.code); if (error.diagnosticsId) { console.error('Diagnostics ID:', error.diagnosticsId); } showError(error.message); hideLoadingSpinner(); }; }); // DOM events checkout.addEventListener('primer:payment-start', () => { console.log('Payment started (DOM event)'); }); checkout.addEventListener('primer:payment-success', (event) => { const { paymentSummary, paymentMethodType } = event.detail; console.log('Payment success (DOM event):', paymentSummary); }); checkout.addEventListener('primer:payment-failure', (event) => { const { error } = event.detail; console.log('Payment failure (DOM event):', error.message); }); ``` ## React Integration Integrate Primer Checkout with React applications (React 18 and React 19 patterns). ```typescript // types/primer.d.ts - Required TypeScript setup import type { CheckoutElement } from '@primer-io/primer-js'; declare global { namespace JSX { interface IntrinsicElements { 'primer-checkout': CheckoutElement; } } } ``` ```tsx // React 19 pattern (direct props) import { useEffect, useMemo } from 'react'; import { loadPrimer } from '@primer-io/primer-js'; // Define options outside component for stable reference const SDK_OPTIONS = { locale: 'en-GB', card: { cardholderName: { required: true, visible: true }, }, }; function CheckoutPage({ clientToken }: { clientToken: string }) { useEffect(() => { loadPrimer(); }, []); return ( <primer-checkout client-token={clientToken} options={SDK_OPTIONS} /> ); } // React 18 pattern (ref + useEffect) import { useRef, useEffect } from 'react'; function CheckoutPageReact18({ clientToken }: { clientToken: string }) { const checkoutRef = useRef<HTMLElement>(null); useEffect(() => { loadPrimer(); }, []); useEffect(() => { const checkout = checkoutRef.current; if (!checkout) return; // Imperative property assignment for React 18 checkout.options = SDK_OPTIONS; const handleReady = (event: CustomEvent) => { const primerJS = event.detail; primerJS.onPaymentSuccess = (data) => { console.log('Payment successful:', data); }; }; checkout.addEventListener('primer:ready', handleReady); return () => checkout.removeEventListener('primer:ready', handleReady); }, []); return ( <primer-checkout ref={checkoutRef} client-token={clientToken} /> ); } // Dynamic options with useMemo function DynamicCheckout({ clientToken, userLocale }: { clientToken: string; userLocale: string }) { const sdkOptions = useMemo(() => ({ locale: userLocale, card: { cardholderName: { required: true } }, }), [userLocale]); return ( <primer-checkout client-token={clientToken} options={sdkOptions} /> ); } ``` ## Custom Styling Customize checkout appearance using CSS Custom Properties or JSON styles. ```css /* CSS Custom Properties approach */ primer-checkout { /* Brand colors */ --primer-color-brand: #4a6cf7; --primer-color-focus: #2f98ff; --primer-color-loader: #4a6cf7; /* Typography */ --primer-typography-brand: 'Inter', sans-serif; /* Border radius */ --primer-radius-base: 4px; --primer-radius-medium: 8px; /* Spacing */ --primer-space-base: 4px; --primer-space-small: 8px; --primer-space-medium: 16px; /* Error states */ --primer-color-text-negative: #dc2626; --primer-color-border-outlined-error: #dc2626; } /* Dark theme */ primer-checkout.primer-dark-theme { --primer-color-brand: #60a5fa; --primer-color-background: #1f2937; --primer-color-text: #f9fafb; } ``` ```html <!-- JSON styles via attribute --> <primer-checkout client-token="your-client-token" custom-styles='{ "primerColorBrand": "#4a6cf7", "primerTypographyBrand": "Inter, sans-serif", "primerRadiusBase": "4px", "primerSpaceBase": "8px" }' ></primer-checkout> ``` ```javascript // Dynamic theme switching const checkout = document.querySelector('primer-checkout'); function setLightTheme() { checkout.className = 'primer-light-theme'; } function setDarkTheme() { checkout.className = 'primer-dark-theme'; } ``` ## PrimerJS Instance Methods Access SDK methods through the PrimerJS instance. ```javascript checkout.addEventListener('primer:ready', (event) => { const primerJS = event.detail; // Get available payment methods const methods = primerJS.getPaymentMethods(); console.log('Payment methods:', methods); // Refresh checkout session (after server-side updates) await primerJS.refreshSession(); // Programmatically set cardholder name primerJS.setCardholderName('John Doe'); // Vault API (v0.11.0+) // Create CVV input for vault payments const cvvInput = await primerJS.vault.createCvvInput({ placeholder: 'CVV', }); // Start vault payment await primerJS.vault.startPayment(); // Delete vaulted payment method await primerJS.vault.delete('payment-method-id'); }); ``` ## Card Network Detection Monitor and respond to card network changes. ```javascript checkout.addEventListener('primer:card-network-change', (event) => { const { detectedCardNetwork, selectableCardNetworks, isLoading } = event.detail; if (isLoading) { console.log('Detecting card network...'); return; } if (detectedCardNetwork) { console.log(`Card network: ${detectedCardNetwork.displayName}`); // Update UI with card logo updateCardLogo(detectedCardNetwork.network); } // Handle co-branded cards (e.g., Cartes Bancaires) if (selectableCardNetworks.length > 1) { console.log('Co-branded card detected'); selectableCardNetworks.forEach(network => { console.log(`Available network: ${network.displayName}`); }); } }); ``` ## Summary Primer Checkout SDK provides a comprehensive payment solution for web applications, supporting drop-in checkout experiences, custom card form layouts, payment method vaulting, and multiple payment methods including cards, digital wallets, and alternative payment methods. The Web Components-based architecture ensures framework-agnostic integration that works with React, Vue, Angular, or vanilla JavaScript applications. Key integration patterns include using the `primer-checkout` component as a container, configuring SDK options for payment methods and behavior, listening to payment lifecycle events (`primer:ready`, `primer:payment-success`, `primer:payment-failure`), and customizing appearance through CSS Custom Properties. For React applications, use stable object references with useMemo or constants outside components to optimize performance. The SDK supports both simple drop-in implementations and complex custom checkout flows with headless vault management for maximum flexibility.