================ LIBRARY RULES ================ - Use Web Components standards (Custom Elements, Shadow DOM, Slots) - Style components using CSS Custom Properties for theme customization - Components are framework-agnostic and work with any JavaScript framework - Target modern browsers only (Chrome, Firefox, Safari, Edge) - Use @primer-io/primer-js SDK for payment integrations # Primer Beta SDK Components - Documentation & Examples Repository This repository is a comprehensive monorepo for Primer's Beta SDK Components, providing both detailed documentation and working integration examples. It serves as the primary resource for developers integrating Primer's payment solutions into their applications. The project uses Yarn workspaces and Turbo for efficient task orchestration across multiple packages, including a Docusaurus documentation site and various standalone example applications demonstrating different implementation patterns. The repository is structured to support multiple integration approaches, from basic vanilla JavaScript implementations to advanced React-based custom layouts and theming systems. All examples utilize the `@primer-io/primer-js` SDK (v0.3.3) or the newer `@primer-io/checkout-web` SDK (v2.x), showcasing payment flows, custom styling, drop-in checkout solutions, and headless implementations. The monorepo architecture ensures consistent tooling and code standards across all workspaces while allowing each example to remain self-contained and independently runnable. ## APIs and Key Functions ### Client Token Fetching Fetches authentication tokens from the Primer API to initialize checkout sessions. This is a foundational utility used across all examples to securely obtain client tokens for payment processing. ```typescript // examples/primer-checkout-basic/src/fetchClientToken.ts export interface ClientTokenSuccessResponse { clientToken: string; success: true; } export interface ClientTokenErrorResponse { error: string; success: false; } export type ClientTokenResponse = | ClientTokenSuccessResponse | ClientTokenErrorResponse; export async function fetchClientToken( apiKey: string, example?: string, ): Promise { try { const url = new URL('api/examples', 'https://sdk-demo.primer.io'); if (example) { url.searchParams.append('example', example); } const response = await fetch(url.toString(), { method: 'GET', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, }); const data = await response.json(); if (!response.ok) { return { error: data.error || 'Failed to fetch client token', success: false, }; } return data as ClientTokenResponse; } catch (error) { console.error('Error fetching client token:', error); return { error: 'An unexpected error occurred', success: false, }; } } // Usage example const response = await fetchClientToken('a1b2c3d4e5f6g7h8i9j0'); if (response.success) { console.log('Token:', response.clientToken); } else { console.error('Error:', response.error); } ``` ### Basic Primer Checkout Initialization Initializes the Primer checkout component with minimal configuration. This vanilla JavaScript approach is the simplest way to integrate Primer's payment solution into a web application. ```typescript // examples/primer-checkout-basic/src/main.ts import { loadPrimer } from '@primer-io/primer-js'; import { fetchClientToken } from './fetchClientToken.ts'; (async function () { // Load the Primer SDK await loadPrimer(); // Get the checkout element const checkout = document.querySelector('primer-checkout')!; // Fetch client token from backend const response = await fetchClientToken('a1b2c3d4e5f6g7h8i9j0'); if (response.success) { // Set the client token using setAttribute checkout.setAttribute('client-token', response.clientToken); } else { console.error('Failed to initialize:', response.error); } })(); ``` ```html Primer Checkout Basic
``` ### React Custom Form Layout Creates a custom payment form layout with React, demonstrating how to arrange Primer components in a branded checkout experience with order summaries and discount code sections. ```tsx // examples/primer-checkout-custom-form/src/App.tsx import { PrimerCheckoutComponent } from '@primer-io/primer-js'; import { useEffect, useRef, useState } from 'react'; import { DiscountCodeSection } from './components/DiscountCodeSection'; import { OrderSummary } from './components/OrderSummary'; import { SecureBadge } from './components/SecureBadge'; import { fetchClientToken } from './fetchClientToken.ts'; import './styles.css'; function App() { const [clientToken, setClientToken] = useState(''); const [isLoading, setIsLoading] = useState(true); const [isProcessing, setIsProcessing] = useState(false); const checkoutRef = useRef(null); const orderItems = [{ name: 'Premium Plan (Annual)', price: '$199.00' }]; const taxAmount = '$19.90'; const totalAmount = '$218.90'; useEffect(() => { async function getToken() { setIsLoading(true); const response = await fetchClientToken('a1b2c3d4e5f6g7h8i9j0'); if (response.success) { setClientToken(response.clientToken); } setIsLoading(false); } getToken(); }, []); useEffect(() => { if (clientToken && checkoutRef.current) { checkoutRef.current.addEventListener('primer:state-change', (event) => { setIsProcessing(!!event.detail?.isProcessing); }); } }, [clientToken]); if (isLoading) { return
Loading checkout...
; } return (

Complete Your Order

Payment Details

Pay {totalAmount}
); } export default App; ``` ### Payment Method Container for Custom Layouts Demonstrates declarative payment method organization using containers to filter and display specific payment methods in custom sections without event handling complexity. ```tsx // examples/primer-checkout-custom-layout - Conceptual implementation import { useState, useEffect } from 'react'; import { fetchClientToken } from './fetchClientToken.ts'; function CustomLayoutApp() { const [clientToken, setClientToken] = useState(''); useEffect(() => { async function init() { const response = await fetchClientToken('a1b2c3d4e5f6g7h8i9j0'); if (response.success) { setClientToken(response.clientToken); } } init(); }, []); return (
{/* Individual payment method for card with custom styling */}

Pay with Card

{/* Individual PayPal for quick checkout section */}

Quick Checkout

{/* Container for all other methods - automatically filtered */}

Alternative Payment Methods

); } ``` ### Drop-In Checkout with React Hook (SDK v2) Provides a reusable React hook for initializing Primer's universal drop-in checkout with automatic lifecycle management, error handling, and state tracking. ```typescript // examples/sdk-v2-stackbllitz-dropin/hooks/usePrimerDropIn.ts 'use client'; import { Primer, PrimerCheckout } from '@primer-io/checkout-web'; import { useCallback, useEffect, useState, useRef } from 'react'; interface UsePrimerDropInOptions { clientToken?: string | null; containerId?: string; onCheckoutComplete?: (data: any) => void; } export function usePrimerDropIn({ clientToken, containerId = 'container', onCheckoutComplete, }: UsePrimerDropInOptions = {}) { const primerInstanceRef = useRef(null); const isInitializingRef = useRef(false); const previousTokenRef = useRef(null); const [isLoading, setIsLoading] = useState(false); const [isSuccess, setIsSuccess] = useState(false); const [error, setError] = useState(null); useEffect(() => { if (!clientToken || typeof window === 'undefined') return; if (clientToken === previousTokenRef.current && primerInstanceRef.current) { return; } if (isInitializingRef.current) { return; } isInitializingRef.current = true; setIsLoading(true); setError(null); const initialize = async () => { try { if (primerInstanceRef.current) { await primerInstanceRef.current.teardown(); primerInstanceRef.current = null; } const checkout = await Primer.showUniversalCheckout(clientToken, { container: `#${containerId}`, onCheckoutComplete: (data: any) => { console.log('Checkout completed', data); setIsSuccess(true); setIsLoading(false); if (onCheckoutComplete) { onCheckoutComplete(data); } }, onCheckoutFail: (err: any, data: any, handler: any) => { console.error('Checkout failed:', err); setError(err as Error); setIsLoading(false); return handler?.showErrorMessage(); }, }); if (!checkout) { throw new Error('Failed to create drop-in checkout'); } primerInstanceRef.current = checkout; previousTokenRef.current = clientToken; } catch (err) { console.error('Failed to initialize Primer drop-in checkout:', err); setError(err as Error); primerInstanceRef.current = null; } finally { setIsLoading(false); isInitializingRef.current = false; } }; initialize(); return () => { if (primerInstanceRef.current) { primerInstanceRef.current.teardown(); } }; }, [clientToken, containerId, onCheckoutComplete]); const resetPrimerInstance = useCallback(async () => { if (isInitializingRef.current) return; setIsLoading(true); try { if (primerInstanceRef.current) { await primerInstanceRef.current.teardown(); } } catch (err) { console.error('Error tearing down Primer checkout:', err); } finally { primerInstanceRef.current = null; previousTokenRef.current = null; isInitializingRef.current = false; setIsSuccess(false); setError(null); setIsLoading(false); } }, []); return { dropInCheckout: primerInstanceRef.current, isLoading, isSuccess, error, resetPrimerInstance, }; } // Usage in component import { usePrimerDropIn } from '@/hooks/usePrimerDropIn'; import { useRouter } from 'next/navigation'; function CheckoutComponent({ clientToken }: { clientToken: string | null }) { const router = useRouter(); const { isLoading, isSuccess, error, resetPrimerInstance } = usePrimerDropIn({ clientToken, containerId: 'primer-checkout-container', onCheckoutComplete: (data) => { console.log('Payment completed!', data); setTimeout(() => router.push('/success'), 2000); }, }); return (
{isLoading &&
Processing payment...
} {error && (

Error: {String(error)}

)} {isSuccess &&
Payment successful!
} {!error && !isSuccess && (
)}
); } ``` ### CSS Variable Theming System Demonstrates how to create custom themes for Primer checkout using CSS variables, enabling complete visual customization without modifying HTML structure. ```css /* examples/primer-checkout-themes/src/neon-cyberpunk-theme.css */ /* Apply theme class directly to primer-checkout element */ primer-checkout.neon-cyberpunk-theme { /* Core brand colors */ --primer-color-brand: #00ff9f; --primer-color-background: #0a0e27; --primer-color-focus: #ff00ff; /* Text colors */ --primer-color-text-primary: #00ffff; --primer-color-text-placeholder: #7b2cbf; --primer-color-text-disabled: #4a5568; --primer-color-text-negative: #ff0055; /* Border radius */ --primer-radius-small: 0px; --primer-radius-medium: 2px; /* Spacing */ --primer-space-small: 12px; --primer-space-medium: 20px; /* Typography */ --primer-typography-brand: 'Courier New', monospace; /* Container states */ --primer-outlined-container-background-default: rgba(0, 255, 159, 0.05); --primer-outlined-container-border-default: #00ff9f; --primer-outlined-container-background-hover: rgba(0, 255, 159, 0.1); --primer-outlined-container-border-hover: #00ffff; } /* Page-level styling using data-theme attribute */ :root[data-theme='neon-cyberpunk'] { background: linear-gradient(135deg, #0a0e27 0%, #1a1a2e 100%); color: #00ffff; } :root[data-theme='neon-cyberpunk'] h1 { text-shadow: 0 0 10px #00ff9f, 0 0 20px #00ff9f; } ``` ```typescript // Dynamic theme switching function applyTheme(themeName: string) { const checkout = document.querySelector('primer-checkout'); // Remove existing theme classes checkout?.classList.remove('primer-dark-theme', 'neon-cyberpunk-theme'); // Apply new theme if (themeName === 'dark') { checkout?.classList.add('primer-dark-theme'); } else if (themeName === 'neon-cyberpunk') { checkout?.classList.add('neon-cyberpunk-theme'); } // Update page-level theme document.documentElement.setAttribute('data-theme', themeName); // Save preference localStorage.setItem('theme', themeName); } // Usage applyTheme('neon-cyberpunk'); ``` ### Monorepo Development Commands Manages the Yarn workspace monorepo with Turbo for coordinated builds, linting, and development across all packages and examples. ```bash # Install all dependencies for the entire monorepo yarn install # Start all development servers (docs + all examples) in parallel yarn dev # Run ESLint across all workspaces yarn lint:es # Run TypeScript type checking across all workspaces yarn lint:ts # Format all files with Prettier (auto-fix) yarn format # Check Prettier formatting without fixing yarn lint:format # Start only the documentation site (runs on port 9000) cd docs && yarn dev # Start a specific example cd examples/primer-checkout-basic && yarn dev # Build a specific workspace cd examples/primer-checkout-custom-form && yarn build # Preview production build cd examples/primer-checkout-basic && yarn preview ``` ```json // Root package.json configuration { "name": "primer-playground", "packageManager": "yarn@4.9.1", "workspaces": [ "packages/*", "docs", "examples/*" ], "nohoist": [ "**/react", "**/react-dom", "**/typescript", "**/@types/react", "**/@types/react-dom" ], "scripts": { "dev": "turbo run dev", "lint:es": "turbo run lint", "lint:ts": "turbo run check-types", "format": "prettier . --write", "lint:format": "prettier . --check", "postinstall": "simple-git-hooks" } } ``` ```json // turbo.json - Task orchestration configuration { "$schema": "https://turbo.build/schema.json", "tasks": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] }, "dev": { "persistent": true, "cache": false }, "lint": { "dependsOn": ["transit"] }, "check-types": { "dependsOn": ["transit"] } } } ``` ## Summary This repository serves as the authoritative reference for implementing Primer's payment solutions across various frameworks and use cases. It includes seven working examples ranging from basic vanilla JavaScript implementations to advanced Next.js applications with custom React hooks, plus a comprehensive Docusaurus documentation site. Each example is fully self-contained with its own dependencies, build configuration, and README, making it easy to copy and adapt patterns for real-world applications. The examples cover fundamental integration patterns (basic checkout), UI customization (custom forms and layouts), visual theming (eight different themes including dark mode, minimal, high contrast, kawaii, brutalist, and neon cyberpunk), and modern SDK v2 features (drop-in and headless checkouts). The monorepo architecture provides shared tooling for code quality (Prettier, ESLint, TypeScript) while maintaining workspace independence through Yarn's nohoist configuration. Turbo orchestrates parallel task execution for development servers and builds, significantly improving developer experience. All examples follow consistent patterns: Vite-based builds for fast development, TypeScript for type safety, fetchClientToken utilities for authentication, and standardized command naming (dev, build, preview). This consistency makes it easy to understand one example and apply those concepts to others, while the variety of implementations ensures developers can find patterns matching their specific framework, styling approach, or integration requirements.