From 0143c47a86d5b621ee50ca7ac84dcd3a7f3cee91 Mon Sep 17 00:00:00 2001 From: Enes Date: Tue, 30 Jun 2026 16:31:55 +0300 Subject: [PATCH 1/3] docs(payments): add WalletConnect Pay for PSPs (Overview + Headless SDK) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a public docs section for PSPs building their own gateway on the headless @walletconnect/pay-* SDK: - payments/psps/overview — positioning, audience, the 4-package map, the headless-runtime + host / five-seam architecture, server-side API-key split, and the payment lifecycle. - payments/psps/headless-sdk — step-by-step React/Next.js walkthrough (server proxy, browser transport, AppKit init, wallet seam, signer, usePaymentSession, snapshot rendering) plus the verified public API of pay-core, pay-state, pay-react, and pay-appkit, and a framework-neutral path. Snippets mirror examples/headless-checkout. - docs.json — wire the "WalletConnect Pay for PSPs" group into both nav versions, between Merchant API and Pay for Wallets. WCPBX-844 Co-Authored-By: Claude Opus 4.8 --- docs.json | 14 + payments/psps/headless-sdk.mdx | 666 +++++++++++++++++++++++++++++++++ payments/psps/overview.mdx | 176 +++++++++ 3 files changed, 856 insertions(+) create mode 100644 payments/psps/headless-sdk.mdx create mode 100644 payments/psps/overview.mdx diff --git a/docs.json b/docs.json index 6a1880f..fd49d99 100644 --- a/docs.json +++ b/docs.json @@ -60,6 +60,13 @@ "payments/merchant-api/logo-specification" ] }, + { + "group": "WalletConnect Pay for PSPs", + "pages": [ + "payments/psps/overview", + "payments/psps/headless-sdk" + ] + }, { "group": "WalletConnect Pay for Wallets", "pages": [ @@ -202,6 +209,13 @@ "payments/merchant-api/logo-specification" ] }, + { + "group": "WalletConnect Pay for PSPs", + "pages": [ + "payments/psps/overview", + "payments/psps/headless-sdk" + ] + }, { "group": "WalletConnect Pay for Wallets", "pages": [ diff --git a/payments/psps/headless-sdk.mdx b/payments/psps/headless-sdk.mdx new file mode 100644 index 0000000..31ed883 --- /dev/null +++ b/payments/psps/headless-sdk.mdx @@ -0,0 +1,666 @@ +--- +title: "Headless SDK" +sidebarTitle: "Headless SDK" + +metatags: + description: "Build your own branded crypto checkout on WalletConnect Pay with the @walletconnect/pay-* Headless SDK. A step-by-step React / Next.js walkthrough with the full public API of pay-core, pay-state, pay-react, and pay-appkit." +--- + +The Headless SDK is a set of framework-agnostic [`@walletconnect/pay-*`](https://www.npmjs.com/org/walletconnect) packages that own the WalletConnect Pay payment flow while you own the UI. This page walks through building a complete checkout in **React / Next.js**, then documents the public API of every package. + + + **Beta (v0.1.x)** — Public APIs may change between minor releases until 1.0. New to the SDK? Read the [PSP Overview](/payments/psps/overview) first for the architecture and the role of each package. + + +The fastest way to learn the SDK is to read the reference checkout it's extracted from: + + + A complete, branded Next.js checkout built on the four packages. Every snippet on this page comes from it. + + +## How a payment flows + +Your gateway never holds the Engine API key in the browser. The browser talks to *your* server, and your server talks to the WalletConnect Pay Engine: + +``` +Browser Your Next.js server WalletConnect Pay +──────── ─────────────────── ───────────────── +usePaymentSession + │ Transport seam + ▼ (createHttpTransport) + fetch /api/wcp/payment/:id/* ──► Route Handler + │ createEngineClient + ▼ (holds the API key) + Engine API ──────────────► /v1/gateway/payment/:id/* +``` + +You build four things, all shown below: + +1. A **server proxy** — Route Handlers that forward to the Engine with your secret key. +2. A **browser transport** — points the runtime at those routes. +3. A **wallet integration** — Reown AppKit, adapted into the `WalletProvider` seam. +4. A **signer** — turns the connected wallet into signing capability. + +Then `usePaymentSession` ties them together and gives you a snapshot to render. + +## Prerequisites + +- **Node 18+** and a React 18/19 app. This guide uses **Next.js** (App Router). +- A **Reown Project ID** — create one at [dashboard.reown.com](https://dashboard.reown.com). Enable the **headless** feature on the project. +- A **WalletConnect Pay Gateway API key** for the Engine (server-side). [Talk to us](https://share.hsforms.com/1XsMCkUxFT2Cte8SCeAh89wnxw6s) to get onboarded. + +## Install + +```bash +# The Headless SDK +npm install @walletconnect/pay-core @walletconnect/pay-state \ + @walletconnect/pay-react @walletconnect/pay-appkit + +# Reown AppKit + adapters for wallet connection (EVM via Wagmi, plus Solana) +npm install @reown/appkit @reown/appkit-adapter-wagmi @reown/appkit-adapter-solana \ + wagmi viem @solana/web3.js @tanstack/react-query +``` + + + `@walletconnect/pay-appkit` requires a Reown AppKit version that exposes the WalletConnect URI on its public state (used to render the pairing QR). Pin `@reown/appkit` (and its adapters) to **the same version**, at or above the SDK's peer range — check the [`pay-appkit` peer dependencies](https://www.npmjs.com/package/@walletconnect/pay-appkit) for the current minimum. All `@reown/appkit*` packages must share one version. + + +## Step 1 — Server proxy (keep the API key server-side) + +Create a server-only module that constructs the Engine client once and forwards calls. The key comes from server env and never ships to the browser. + +```typescript lib/server/engine.ts +import 'server-only' +import { createEngineClient } from '@walletconnect/pay-core/server' + +const client = createEngineClient({ + apiUrl: process.env.WCP_API_URL ?? 'https://staging.api.pay.walletconnect.org', + apiKey: process.env.WCP_WALLET_API_KEY ?? '' // secret — server-side only +}) + +/** Forward a browser call to the Engine and return an EngineResponse-shaped Response. */ +export async function callEngine( + path: string, + init: { method: 'GET' | 'POST'; body?: unknown } +): Promise { + const paymentId = path.split('/')[4]! // /v1/gateway/payment/:id/... + let result + + if (path.endsWith('/options')) { + result = await client.getPaymentOptions(paymentId, init.body as never) + } else if (path.endsWith('/fetch')) { + result = await client.fetchOptionActions(paymentId, init.body as never) + } else if (path.endsWith('/confirm')) { + result = await client.confirmPayment(paymentId, init.body as never) + } else if (path.endsWith('/status')) { + result = await client.getPaymentStatus(paymentId) + } else { + result = await client.getPayment(paymentId) + } + + return Response.json(result) +} +``` + +Then expose one Route Handler per Engine call under `/api/wcp/payment/[id]`. The browser transport (Step 2) calls exactly these paths: + +```typescript app/api/wcp/payment/[id]/options/route.ts +import { callEngine } from '@/lib/server/engine' + +export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) { + const { id } = await params + const body = await req.json() + return callEngine(`/v1/gateway/payment/${id}/options`, { method: 'POST', body }) +} +``` + +```typescript app/api/wcp/payment/[id]/status/route.ts +import { callEngine } from '@/lib/server/engine' + +export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) { + const { id } = await params + return callEngine(`/v1/gateway/payment/${id}/status`, { method: 'GET' }) +} +``` + +Create the same handler for each route the transport uses: + +| Route Handler | Method | Engine call | +| -------------------------------------- | ------ | ------------------- | +| `app/api/wcp/payment/[id]/route.ts` | `GET` | `getPayment` | +| `app/api/wcp/payment/[id]/options/route.ts` | `POST` | `getPaymentOptions` | +| `app/api/wcp/payment/[id]/fetch/route.ts` | `POST` | `fetchOptionActions` | +| `app/api/wcp/payment/[id]/confirm/route.ts` | `POST` | `confirmPayment` | +| `app/api/wcp/payment/[id]/status/route.ts` | `GET` | `getPaymentStatus` | + +## Step 2 — Browser transport + +On the client, point the runtime at your proxy. `createHttpTransport` issues requests to `${baseUrl}/payment/:id/...`, matching the routes above. + +```typescript +import { createHttpTransport } from '@walletconnect/pay-core' + +const transport = createHttpTransport({ baseUrl: '/api/wcp' }) +``` + +That's the entire `Transport` seam. It speaks the same five methods as the server client, but routes through your origin — no key, no CORS. + +## Step 3 — Initialize Reown AppKit (headless) + +Construct an AppKit instance once on the client, in headless mode (no built-in modal — you render your own wallet picker). Pass the **same `networks`** array to AppKit so a connected wallet's address expands across the chains the Engine quotes against. + +```tsx components/providers.tsx +'use client' + +import type { AppKit } from '@walletconnect/pay-appkit' // type re-exported by the SDK +import { SolanaAdapter } from '@reown/appkit-adapter-solana' +import { WagmiAdapter } from '@reown/appkit-adapter-wagmi' +import { mainnet, polygon, arbitrum, optimism, base, solana, type AppKitNetwork } from '@reown/appkit/networks' +import { createAppKit } from '@reown/appkit/react' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { createContext, useContext, useEffect, useState } from 'react' +import { WagmiProvider } from 'wagmi' + +export const networks: [AppKitNetwork, ...AppKitNetwork[]] = [ + mainnet, polygon, arbitrum, optimism, base, solana +] + +const projectId = process.env.NEXT_PUBLIC_APPKIT_PROJECT_ID ?? '' + +let wagmiAdapter: WagmiAdapter | null = null +let appKitInstance: AppKit | null = null + +function initAppKit() { + if (wagmiAdapter && appKitInstance) return { adapter: wagmiAdapter, appKit: appKitInstance } + if (typeof window === 'undefined' || !projectId) return null + + wagmiAdapter = new WagmiAdapter({ networks, projectId }) + appKitInstance = createAppKit({ + adapters: [wagmiAdapter, new SolanaAdapter()], + networks, + projectId, + features: { headless: true }, // no modal — you own the picker + metadata: { + name: 'Acme Pay', + description: 'Headless checkout', + url: typeof window !== 'undefined' ? window.location.origin : 'https://example.com', + icons: [] + } + }) + return { adapter: wagmiAdapter, appKit: appKitInstance } +} + +const AppKitContext = createContext(null) +export function useAppKit(): AppKit { + const appKit = useContext(AppKitContext) + if (!appKit) throw new Error('useAppKit must be used within ') + return appKit +} + +export function Providers({ children }: { children: React.ReactNode }) { + const [state, setState] = useState<{ adapter: WagmiAdapter; appKit: AppKit } | null>(null) + const [queryClient] = useState(() => new QueryClient()) + + useEffect(() => setState(initAppKit()), []) + if (!state) return
Initializing…
+ + return ( + + + {children} + + + ) +} +``` + + + Call `createAppKit` **once** at module scope (guarded as above), never inside a component render. Keep every `@reown/appkit*` package on the same version. + + +## Step 4 — The wallet seam + +`@walletconnect/pay-appkit/react` turns the AppKit instance into the runtime's `WalletProvider` seam **and** a ready-made wallet-picker controller (list, search, pagination, the pairing QR URI). One hook gives you both. + +```tsx +import { useAppKitWalletProvider } from '@walletconnect/pay-appkit/react' + +const { + wallet, // ← the WalletProvider seam to hand the runtime + wallets, // wallet list for your picker + connectedWallets, // currently connected, per namespace + supportedNamespaces, + wcUri, // WalletConnect URI for the QR + getWcUri, // generate a generic pairing URI eagerly + searchQuery, setSearchQuery, + hasMore, loadMore, + isFetchingWallets, + isInitialized +} = useAppKitWalletProvider(appKit, { + wcPayUrl: typeof window !== 'undefined' ? window.location.href : undefined +}) +``` + +## Step 5 — The signer + +The signing strategies (EVM + Solana) live in `@walletconnect/pay-state`; they just need the wallet's providers, which only your host can supply. Wire them into a `Signer`: + +```typescript lib/signer.ts +import { loadSolanaWeb3 } from '@walletconnect/pay-appkit' +import { + EvmSigningStrategy, + SolanaSigningStrategy, + signOptionActions, + type ActionRange, + type EIP1193Provider, + type PaymentOptionExtended, + type Signer, + type SignPaymentResult, + type SigningStrategy, + type SolanaProvider, + type WalletProvider +} from '@walletconnect/pay-state' + +export function createAppKitSigner(wallet: WalletProvider): Signer { + return { + signActions(option: PaymentOptionExtended, range?: ActionRange): Promise { + const strategies: SigningStrategy[] = [] + + const evm = wallet.getProvider({ namespace: 'eip155', chainId: '1' }) + if (evm) { + strategies.push( + new EvmSigningStrategy(evm as EIP1193Provider, { + switchNetwork: caip => wallet.switchNetwork(caip), + getActiveChainId: () => { + const caip = wallet.getAccounts().eip155?.caipAddress + const [ns, chain] = caip?.split(':') ?? [] + return chain ? `${ns}:${chain}` : undefined + } + }) + ) + } + + const sol = wallet.getProvider({ namespace: 'solana', chainId: 'mainnet' }) + strategies.push(new SolanaSigningStrategy(sol as SolanaProvider | null, { loadWeb3: loadSolanaWeb3 })) + + return signOptionActions(option, strategies, range) + } + } +} +``` + +## Step 6 — Drive the session + +Assemble the seams and call `usePaymentSession`. It returns the public `snapshot` plus named actions — no XState, no `send`. + +```tsx components/checkout.tsx +'use client' + +import { createHttpTransport } from '@walletconnect/pay-core' +import { useAppKitWalletProvider } from '@walletconnect/pay-appkit/react' +import { browserClock, type PaymentOptionExtended } from '@walletconnect/pay-state' +import { usePaymentSession } from '@walletconnect/pay-react' +import { useMemo } from 'react' +import { useAppKit } from '@/components/providers' +import { createAppKitSigner } from '@/lib/signer' + +export function Checkout({ paymentId }: { paymentId: string }) { + const appKit = useAppKit() + const { wallet, wallets, wcUri, getWcUri } = useAppKitWalletProvider(appKit, { + wcPayUrl: typeof window !== 'undefined' ? window.location.href : undefined + }) + + // Assemble the runtime seams. `signer` lives inside `seams`; `wallet` is passed separately. + const seams = useMemo( + () => ({ + transport: createHttpTransport({ baseUrl: '/api/wcp' }), + clock: browserClock, + signer: createAppKitSigner(wallet) + }), + [wallet] + ) + + const { snapshot, connectWallet, selectOption, confirmSelection, submitInfoCapture } = + usePaymentSession({ paymentId, seams, wallet }) + + return
{/* render per snapshot.state — see Step 7 */}
+} +``` + +Render it from a route, wrapped in your providers: + +```tsx app/[paymentId]/page.tsx +import { Checkout } from '@/components/checkout' +import { Providers } from '@/components/providers' + +export default async function PaymentPage({ params }: { params: Promise<{ paymentId: string }> }) { + const { paymentId } = await params + return +} +``` + +## Step 7 — Render the snapshot + +`snapshot.state` is a single string you switch on. Each state maps to one piece of UI; the named actions advance the flow. + +```tsx +switch (snapshot.state) { + case 'ReadyForWallet': + // Show the QR (snapshot is connecting-agnostic; QR comes from getWcUri/wcUri) + // and a wallet picker. On pick: + return connectWallet(w, w.namespaces[0])} /> + + case 'ConnectingWallet': + return + + case 'LoadingOptions': + return + + case 'OptionsReady': + return ( + selectOption(opt, rank)} + /> + ) + + case 'NoOptions': + return + + case 'InformationCapture': + // Render snapshot.collectData.fields, then: + return + + case 'OptionSelected': + case 'RequiresApproval': + return ( + + ) + + case 'AwaitingWalletApproval': + return + + case 'WaitingForConfirmation': + return + + case 'Succeeded': + return + + case 'Failed': + case 'PaymentExpired': + case 'PaymentCancelled': + case 'InvalidPayment': + case 'SanctionedUser': + return +} +``` + +That's a full gateway. Connect → options → (optional KYC) → confirm → sign → settle, all driven by the runtime; you only render and call actions. + +## Environment variables + +```bash .env.local +# Reown AppKit project ID — required for wallet connection / QR pairing (public) +NEXT_PUBLIC_APPKIT_PROJECT_ID= + +# WalletConnect Pay Engine — server-side only, NEVER exposed to the browser +WCP_API_URL=https://staging.api.pay.walletconnect.org +WCP_WALLET_API_KEY= +``` + +--- + +# Package reference + +## `@walletconnect/pay-core` + +The foundation: Engine contract types, CAIP utilities, and the `Transport` seam. Zero runtime dependencies. Two entry points — a browser-safe main entry and a server-only `/server` entry that holds the key. + +### Browser entry — `@walletconnect/pay-core` + +```typescript +import { createHttpTransport, type Transport, type HttpTransportConfig } from '@walletconnect/pay-core' + +interface HttpTransportConfig { + baseUrl?: string // default '/api/wcp' + fetch?: typeof fetch + timeoutMs?: number // default 30_000 +} + +function createHttpTransport(config?: HttpTransportConfig): Transport +``` + +The `Transport` contract (the seam the runtime depends on) — five methods, each resolving to an `EngineResponse` envelope (never throwing): + +```typescript +interface Transport { + getPayment(paymentId): Promise> + getPaymentOptions(paymentId, request): Promise> + fetchOptionActions(paymentId, request): Promise> + confirmPayment(paymentId, request): Promise> + getPaymentStatus(paymentId): Promise> +} +``` + +Also exported: all Engine contract types (`GetPaymentResponse`, `PaymentOptionExtended`, `Amount`, `CollectData`, `PaymentStatus`, …), CAIP utilities (`parseCaip2`, `parseCaip10`, …), and transport error helpers (`TRANSPORT_ERROR_CODES`, `isAbortError`, `DEFAULT_TIMEOUT_MS`). + +### Server entry — `@walletconnect/pay-core/server` + +```typescript +import { createEngineClient, type EngineClient, type EngineClientConfig, DEFAULT_VERSION } from '@walletconnect/pay-core/server' + +interface EngineClientConfig { + apiUrl: string // e.g. https://staging.api.pay.walletconnect.org (no trailing slash) + apiKey: string // your Gateway Api-Key — keep secret, server-side only + version?: string // WCP API version; defaults to DEFAULT_VERSION + fetch?: typeof fetch + timeoutMs?: number // default 30_000 +} + +function createEngineClient(config: EngineClientConfig): EngineClient +``` + +`EngineClient` exposes the same five methods as `Transport`, but attaches the secret `Api-Key` and `Wcp-Version` headers and calls the Engine directly. Use it only on the server. + +## `@walletconnect/pay-state` + +The headless runtime: the payment state machine, the injectable seam contracts, the framework-agnostic `PaymentController`, the signing strategies, and the public `PaymentSnapshot` view-model. + +### `createPaymentController` + +The framework-agnostic binding (the React hook wraps this). Use it directly in a non-React host. + +```typescript +import { createPaymentController, type PaymentController, type PaymentControllerOptions } from '@walletconnect/pay-state' + +interface PaymentControllerOptions { + paymentId: string + seams: PaymentSessionSeams // { transport, clock, signer?, telemetry? } + wallet: WalletProvider + initialPayment?: GetPaymentResponse + signingTimeoutMs?: number + onMachineEvent?: MachineEventObserver // read-only analytics observer +} + +function createPaymentController(options: PaymentControllerOptions): PaymentController +``` + +```typescript +interface PaymentController { + getSnapshot(): PaymentSnapshot + subscribe(listener: () => void): () => void + start(): void + destroy(): void + // domain actions (see the React hook below for the full list) + connectWallet(wallet, namespace?, options?): void + // … +} +``` + +### Seams + +The runtime reaches the outside world only through these contracts: + +```typescript +interface PaymentSessionSeams { + transport: Transport // Engine calls (from pay-core) + clock: Clock // intervals + page visibility (for status polling) + signer?: Signer // signing capability — required for an end-to-end payment + telemetry?: Telemetry // optional analytics breadcrumbs +} + +interface WalletProvider { + getAccounts(): Partial> + getProvider(network: Caip2 & { namespace: T }): ProviderForNamespace | null + connect(wallet: WalletRef, namespace: Namespace, options?: WalletConnectOptions): Promise + disconnect(namespace?: Namespace): Promise + switchNetwork(caipNetworkId: string): Promise + subscribe(listener: () => void): () => void +} + +interface Signer { + signActions(option: PaymentOptionExtended, range?: ActionRange): Promise +} +``` + +Browser defaults are provided so you only have to inject `transport`, `wallet`, and `signer`: + +```typescript +import { browserClock, noopTelemetry, browserDefaults } from '@walletconnect/pay-state' +``` + +### Signing strategies + +```typescript +import { EvmSigningStrategy, SolanaSigningStrategy, signOptionActions } from '@walletconnect/pay-state' + +// Dispatch each of an option's actions to the matching strategy: +signOptionActions(option: PaymentOptionExtended, strategies: SigningStrategy[], range?: ActionRange): Promise +``` + +### `PaymentSnapshot` + +The public, serializable view-model. `state` is one of: + +| Group | States | +| ----------- | ---------------------------------------------------------------------------------------- | +| Loading | `Initializing` | +| Wallet | `ReadyForWallet`, `ConnectingWallet` | +| Options | `LoadingOptions`, `OptionsReady`, `NoOptions`, `OptionSelected` | +| Compliance | `InformationCapture` | +| Approval | `RequiresApproval`, `AwaitingWalletApproval`, `WaitingForConfirmation` | +| Terminal | `Succeeded`, `Failed`, `PaymentExpired`, `PaymentCancelled`, `InvalidPayment`, `SanctionedUser` | + +```typescript +interface PaymentSnapshot { + state: PaymentState + payment?: GetPaymentResponse + options: PaymentOptionExtended[] + selectedOption?: PaymentOptionExtended + collectData?: CollectData | null + wallet: { isConnected: boolean; accounts: string[] } + requiresApproval: boolean + signingError?: { code: string; message?: string } + isQuoteExpired: boolean + profileId?: string + profileNotFound: boolean + // …diagnostic fields +} +``` + +## `@walletconnect/pay-react` + +A single hook — `usePaymentSession` — a `useSyncExternalStore`-based binding over the controller. SSR-safe, tear-free, zero XState leak. + +```typescript +import { usePaymentSession, type UsePaymentSessionOptions, type PaymentSessionApi } from '@walletconnect/pay-react' + +function usePaymentSession(options: UsePaymentSessionOptions): PaymentSessionApi +``` + +`UsePaymentSessionOptions` matches `PaymentControllerOptions` (`paymentId`, `seams`, `wallet`, `initialPayment?`, `signingTimeoutMs?`, `onMachineEvent?`). The return is `{ snapshot }` plus the named actions: + +| Action | Drives | +| ----------------------------------------------- | ----------------------------------------------- | +| `connectWallet(wallet, namespace?, options?)` | Begin connecting a wallet | +| `disconnectWallet(namespace?)` | Disconnect one namespace, or all | +| `selectOption(option, rank)` | Pick a payment option | +| `confirmSelection()` | Confirm and move toward signing | +| `unselectOption()` | Return to the option list | +| `submitInfoCapture(data)` | Submit collected KYC/contact data | +| `navigateBack()` | Step back | + +Plus a **host-orchestration channel** for signals the runtime can't observe itself — `refreshOptions`, `notifyQuoteExpired`, `acknowledgeQuoteExpiry`, `markUserSanctioned`, `setProfileLookup`, `notifyPaymentExpired`, `failWalletConnection`. Most gateways won't need these to start. + +## `@walletconnect/pay-appkit` + +The Reown AppKit adapter — implements the `WalletProvider` seam over an AppKit instance, and ships a headless wallet-picker controller. The main entry is framework-neutral; the React hook lives on `/react`. + +### Main entry — `@walletconnect/pay-appkit` + +```typescript +import { + createAppKitWalletProvider, // (appKit, options?) => WalletProvider + createAppKitWalletList, // (appKit, options?) => AppKitWalletList (framework-neutral picker) + loadSolanaWeb3, // lazy @solana/web3.js codec loader for the Solana signing strategy + applyPlacements, resolvePlacements, // wallet-ordering helpers + type AppKit, // re-exported AppKit instance type — don't import @reown/appkit directly + type WalletListItem, type ConnectedWallet, type WalletListState, type WalletListOptions +} from '@walletconnect/pay-appkit' +``` + +`createAppKitWalletList` returns a controller — `wallet` (the seam), `getState()`, `subscribe()`, `fetchWallets()`, `search()`, `loadMore()`, `getWcUri()` — for non-React hosts. + +### React entry — `@walletconnect/pay-appkit/react` + +```typescript +import { useAppKitWalletProvider, type AppKitWalletProviderHandle, type UseAppKitWalletProviderOptions } from '@walletconnect/pay-appkit/react' + +function useAppKitWalletProvider(appKit: AppKit | undefined, options?: UseAppKitWalletProviderOptions): AppKitWalletProviderHandle +``` + +The handle extends `WalletListState` and adds `wallet` (the seam to hand `usePaymentSession`), `searchQuery`/`setSearchQuery` (debounced), `fetchWallets`, `loadMore`, and `getWcUri` for the pairing QR. + +--- + +## Without React (framework-neutral) + +Every React API here wraps a framework-agnostic core. A vanilla / Vue / Svelte host uses `createPaymentController` (pay-state) + `createAppKitWalletList` (pay-appkit) directly: + +```typescript +import { createHttpTransport } from '@walletconnect/pay-core' +import { createAppKitWalletList } from '@walletconnect/pay-appkit' +import { browserClock, createPaymentController } from '@walletconnect/pay-state' + +const walletList = createAppKitWalletList(appKit, { wcPayUrl: window.location.href }) + +const controller = createPaymentController({ + paymentId, + wallet: walletList.wallet, + seams: { + transport: createHttpTransport({ baseUrl: '/api/wcp' }), + clock: browserClock, + signer: createAppKitSigner(walletList.wallet) + } +}) + +controller.subscribe(() => render(controller.getSnapshot())) +controller.start() +``` + + + The same checkout with no framework — `createPaymentController` + manual subscribe + imperative render. + + +## Next steps + + + + The architecture, the five seams, and when to reach for the Headless SDK. + + + The Gateway and Payments endpoints behind the SDK. + + diff --git a/payments/psps/overview.mdx b/payments/psps/overview.mdx new file mode 100644 index 0000000..8102b80 --- /dev/null +++ b/payments/psps/overview.mdx @@ -0,0 +1,176 @@ +--- +title: "WalletConnect Pay - PSP Integration" +sidebarTitle: "Overview" + +metatags: + description: "Build your own branded crypto checkout on WalletConnect Pay with the Headless SDK — a set of framework-agnostic @walletconnect/pay-* packages that own the payment flow while you own the UI, transport, and wallet connection." +--- + +WalletConnect Pay ships a **hosted gateway** at `pay.walletconnect.com` that handles the full payment experience out of the box. PSPs, acquirers, and platforms that need a **fully branded, fully owned checkout** can instead build their own gateway on the same engine using the **Headless SDK**. + +The Headless SDK is the exact runtime that powers our own hosted Buyer Experience — extracted into a set of framework-agnostic [`@walletconnect/pay-*`](https://www.npmjs.com/org/walletconnect) packages. You get the payment state machine, Engine API client, and wallet orchestration; you bring your own UI, branding, routing, and infrastructure. + + + **Beta (v0.1.x)** — The Headless SDK is under active development. Public APIs may change between minor releases until 1.0. [Talk to us](https://share.hsforms.com/1XsMCkUxFT2Cte8SCeAh89wnxw6s) before going to production. + + +## Who is it for? + +- **Payment Service Providers & Acquirers** who want crypto checkout inside their own product, with their own design system and domain — not a redirect to a third-party page. +- **Platforms & marketplaces** embedding pay-with-crypto directly into an existing checkout flow. +- **Teams that need control** over every step of the UX: wallet selection, network/token choice, compliance prompts, success and error states. + +If you just want to accept payments with the least effort, use the [hosted gateway](/payments/overview) or the [Ecommerce integration](/payments/ecommerce/overview) instead. Reach for the Headless SDK when you need to **own the experience**. + +## What you get + + + + The full payment state machine (load → connect → quote → sign → confirm → settle) as a framework-agnostic engine. No UI imposed. + + + A zero-dependency client for the WalletConnect Pay Engine, with the secret API key safely held server-side. + + + A single `usePaymentSession` hook that projects the runtime into a clean, serializable snapshot plus named actions — zero state-machine internals leak into your components. + + + A ready-made adapter over [Reown AppKit](https://reown.com/appkit) for wallet connection across EVM and Solana — or bring your own. + + + +## The packages + +The runtime is split into layered packages. Each is independently consumable, and lower layers never depend on higher ones — so you can take only what you need. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PackageRoleDepends on
+ @walletconnect/pay-core + Engine API client — contract types, CAIP utilities, the browser Transport seam (createHttpTransport), and the server-side createEngineClient that holds your API key. The foundation.
+ @walletconnect/pay-state + The headless runtime — the payment state machine, orchestration, the injectable seam contracts, the session factory, and the public PaymentSnapshot view-model. No React, no HTTP client, no wallet SDK.pay-core
+ @walletconnect/pay-react + Thin React binding — usePaymentSession returns the snapshot plus domain actions. Zero state-machine leak.pay-state
+ @walletconnect/pay-appkit + Reown AppKit adapter — implements the WalletProvider seam over a Reown AppKit instance (plus the Solana web3 loader). A /react subpath ships the wallet-connection hook.pay-state
+ +## How it fits together + +The Headless SDK follows a **headless runtime + host** model. The payment flow lives entirely in the SDK; your application is the **host** that consumes it. The runtime reaches the outside world only through five injectable **seams** — so you can swap in your own transport, wallet, timing, and analytics, and reuse the exact same payment machine. + +``` + Your gateway (the host) + ─ your UI, branding, routing ─ + │ consumes + ▼ + @walletconnect/pay-react · usePaymentSession + │ drives + ▼ + @walletconnect/pay-state · payment runtime + │ reaches the outside world only through seams + ▼ + ┌──────────┬───────────────┬─────────┬────────┬───────────┐ + │Transport │ WalletProvider │ Signer │ Clock │ Telemetry │ + └────┬─────┴───────┬───────┴────┬────┴───┬────┴─────┬─────┘ + │ │ │ │ │ + your server AppKit / signing timers/ analytics + → Engine API your wallet strategy polling +``` + +The five seams the runtime depends on: + +| Seam | What it abstracts | Provided by | +| ---------------- | ------------------------------------------ | ---------------------------------------------------- | +| `Transport` | Engine HTTP calls | `pay-core` `createHttpTransport` → your server route | +| `WalletProvider` | connect / accounts / provider / switch | `pay-appkit`, or your own wallet integration | +| `Signer` | sign a payment option's wallet-RPC actions | `pay-state` signing strategies over the wallet seam | +| `Clock` | intervals + page visibility (for polling) | browser timers (the SDK ships a default) | +| `Telemetry` | analytics breadcrumbs | your analytics pipeline (optional) | + + + `@walletconnect/pay-state` ships browser-ready `Clock` and `Telemetry` defaults (`browserClock`, `noopTelemetry`). In practice a React/Next.js gateway wires three seams itself — a `Transport` (pointed at your server route), a `WalletProvider` (the AppKit adapter), and a `Signer` (built from the wallet) — and reuses `browserClock` for the rest. `Telemetry` is optional. + + +## The Engine API key never reaches the browser + +WalletConnect Pay's Engine API is authenticated with a secret API key that **must stay server-side**. The SDK enforces this split: + +``` +Browser → Transport seam → your server route → Engine API + (no secret) (createEngineClient holds the key) +``` + +`pay-core` exposes two entry points for exactly this: `createHttpTransport` for the browser (talks to *your* server) and `createEngineClient` (imported from `@walletconnect/pay-core/server`) which holds the API key and talks to the Engine. In a Next.js app, your Route Handler sits in the middle. + +## The payment lifecycle + +Whatever UI you build, the runtime moves a payment through the same stages. Your job is to render each stage and call the matching action. + + + + The buyer arrives with a payment ID (from a QR code, link, or your checkout). The runtime fetches the payment intent — amount, merchant, accepted tokens. + + + The buyer connects a wallet through the `WalletProvider` seam. The runtime reads their accounts across the supported networks. + + + Given the connected accounts, the Engine returns the concrete ways to pay — token, network, amount, fees, and whether compliance data is required. + + + The buyer picks an option. The runtime builds the transaction(s) and the exact wallet-RPC actions to sign. + + + The `Signer` drives the wallet through the required signatures (e.g. a permit + the payment). + + + Signed results are submitted to confirm the payment. The runtime then polls status until the payment succeeds, fails, or expires. + + + +## Supported networks & tokens + +The Headless SDK supports the full WalletConnect Pay token and network coverage — USDC, USDT, EURC, PYUSD and more across Ethereum, Polygon, Base, Optimism, Arbitrum (and Solana, rolling out). See [Token & chain coverage](/payments/token-and-chain-coverage) for the live list. + +## Next steps + + + + Install the packages and build a checkout in React / Next.js, step by step. + + + The Gateway and Payments endpoints behind the SDK. + + + + + Are you a PSP or acquirer? Tell us what you're building and we'll help you get set up. + From a2d0429ca8450a9d2eadbedcf48ec23fffd97b07 Mon Sep 17 00:00:00 2001 From: Enes Date: Wed, 1 Jul 2026 17:46:28 +0300 Subject: [PATCH 2/3] docs(payments): restructure PSP docs into nested Headless SDK section Address review feedback on the WCP-for-PSPs docs (WCPBX-844): - Slim the PSP Overview to a general page presenting the two integration paths (hosted gateway vs Headless SDK) instead of leading with the headless "special use case". - Split the long Headless SDK page into a nested sidebar section: Overview / How it works / Implementation / Packages Reference. - Move the packages table onto Packages Reference; move the lifecycle, seams model, and API-key split onto How it works. - Convert the "How it fits together" and API-key ASCII diagrams to Mermaid. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs.json | 20 +- payments/psps/headless-sdk/how-it-works.mdx | 105 +++++++ .../implementation.mdx} | 257 +---------------- payments/psps/headless-sdk/overview.mdx | 63 ++++ .../psps/headless-sdk/packages-reference.mdx | 269 ++++++++++++++++++ payments/psps/overview.mdx | 163 ++--------- 6 files changed, 484 insertions(+), 393 deletions(-) create mode 100644 payments/psps/headless-sdk/how-it-works.mdx rename payments/psps/{headless-sdk.mdx => headless-sdk/implementation.mdx} (56%) create mode 100644 payments/psps/headless-sdk/overview.mdx create mode 100644 payments/psps/headless-sdk/packages-reference.mdx diff --git a/docs.json b/docs.json index fd49d99..4e7a758 100644 --- a/docs.json +++ b/docs.json @@ -64,7 +64,15 @@ "group": "WalletConnect Pay for PSPs", "pages": [ "payments/psps/overview", - "payments/psps/headless-sdk" + { + "group": "Headless SDK", + "pages": [ + "payments/psps/headless-sdk/overview", + "payments/psps/headless-sdk/how-it-works", + "payments/psps/headless-sdk/implementation", + "payments/psps/headless-sdk/packages-reference" + ] + } ] }, { @@ -213,7 +221,15 @@ "group": "WalletConnect Pay for PSPs", "pages": [ "payments/psps/overview", - "payments/psps/headless-sdk" + { + "group": "Headless SDK", + "pages": [ + "payments/psps/headless-sdk/overview", + "payments/psps/headless-sdk/how-it-works", + "payments/psps/headless-sdk/implementation", + "payments/psps/headless-sdk/packages-reference" + ] + } ] }, { diff --git a/payments/psps/headless-sdk/how-it-works.mdx b/payments/psps/headless-sdk/how-it-works.mdx new file mode 100644 index 0000000..48986ee --- /dev/null +++ b/payments/psps/headless-sdk/how-it-works.mdx @@ -0,0 +1,105 @@ +--- +title: "How it works" +sidebarTitle: "How it works" + +metatags: + description: "How the WalletConnect Pay Headless SDK works — the payment lifecycle, the headless runtime + host model with five injectable seams, and how the Engine API key stays server-side." +--- + +Before you install anything, it helps to understand the shape of the SDK: the stages a payment moves through, how the runtime plugs into your app, and why your Engine API key never reaches the browser. + +## The payment lifecycle + +Whatever UI you build, the runtime moves a payment through the same stages. Your job is to render each stage and call the matching action. + + + + The buyer arrives with a payment ID (from a QR code, link, or your checkout). The runtime fetches the payment intent — amount, merchant, accepted tokens. + + + The buyer connects a wallet through the `WalletProvider` seam. The runtime reads their accounts across the supported networks. + + + Given the connected accounts, the Engine returns the concrete ways to pay — token, network, amount, fees, and whether compliance data is required. + + + The buyer picks an option. The runtime builds the transaction(s) and the exact wallet-RPC actions to sign. + + + The `Signer` drives the wallet through the required signatures (e.g. a permit + the payment). + + + Signed results are submitted to confirm the payment. The runtime then polls status until the payment succeeds, fails, or expires. + + + +## How it fits together + +The Headless SDK follows a **headless runtime + host** model. The payment flow lives entirely in the SDK; your application is the **host** that consumes it. The runtime reaches the outside world only through five injectable **seams** — so you can swap in your own transport, wallet, timing, and analytics, and reuse the exact same payment machine. + +```mermaid +flowchart TD + Host["Your gateway — the host
your UI, branding, routing"] + React["@walletconnect/pay-react
usePaymentSession"] + State["@walletconnect/pay-state
payment runtime"] + + Host -->|consumes| React + React -->|drives| State + State -->|reaches the outside world
only through seams| Seams + + subgraph Seams["Injectable seams"] + direction LR + Transport["Transport"] + Wallet["WalletProvider"] + Signer["Signer"] + Clock["Clock"] + Telemetry["Telemetry"] + end + + Transport --> T2["your server → Engine API"] + Wallet --> W2["AppKit / your wallet"] + Signer --> S2["signing strategy"] + Clock --> C2["timers / polling"] + Telemetry --> A2["analytics"] +``` + +The five seams the runtime depends on: + +| Seam | What it abstracts | Provided by | +| ---------------- | ------------------------------------------ | ---------------------------------------------------- | +| `Transport` | Engine HTTP calls | `pay-core` `createHttpTransport` → your server route | +| `WalletProvider` | connect / accounts / provider / switch | `pay-appkit`, or your own wallet integration | +| `Signer` | sign a payment option's wallet-RPC actions | `pay-state` signing strategies over the wallet seam | +| `Clock` | intervals + page visibility (for polling) | browser timers (the SDK ships a default) | +| `Telemetry` | analytics breadcrumbs | your analytics pipeline (optional) | + + + `@walletconnect/pay-state` ships browser-ready `Clock` and `Telemetry` defaults (`browserClock`, `noopTelemetry`). In practice a React/Next.js gateway wires three seams itself — a `Transport` (pointed at your server route), a `WalletProvider` (the AppKit adapter), and a `Signer` (built from the wallet) — and reuses `browserClock` for the rest. `Telemetry` is optional. + + +## The Engine API key never reaches the browser + +WalletConnect Pay's Engine API is authenticated with a secret API key that **must stay server-side**. The SDK enforces this split: the browser talks to *your* server, and your server talks to the Engine. + +```mermaid +flowchart LR + Browser["Browser
Transport seam
(no secret)"] + Server["Your server route
createEngineClient
(holds the API key)"] + Engine["WalletConnect Pay
Engine API"] + + Browser -->|"fetch /api/wcp/payment/:id/*"| Server + Server -->|"Api-Key header"| Engine +``` + +`pay-core` exposes two entry points for exactly this: `createHttpTransport` for the browser (talks to *your* server) and `createEngineClient` (imported from `@walletconnect/pay-core/server`) which holds the API key and talks to the Engine. In a Next.js app, your Route Handler sits in the middle. + +## Next steps + + + + Build a complete checkout in React / Next.js, step by step. + + + The public API of pay-core, pay-state, pay-react, and pay-appkit. + + diff --git a/payments/psps/headless-sdk.mdx b/payments/psps/headless-sdk/implementation.mdx similarity index 56% rename from payments/psps/headless-sdk.mdx rename to payments/psps/headless-sdk/implementation.mdx index 31ed883..74f08cc 100644 --- a/payments/psps/headless-sdk.mdx +++ b/payments/psps/headless-sdk/implementation.mdx @@ -1,38 +1,12 @@ --- -title: "Headless SDK" -sidebarTitle: "Headless SDK" +title: "Implementation" +sidebarTitle: "Implementation" metatags: - description: "Build your own branded crypto checkout on WalletConnect Pay with the @walletconnect/pay-* Headless SDK. A step-by-step React / Next.js walkthrough with the full public API of pay-core, pay-state, pay-react, and pay-appkit." + description: "Build a branded WalletConnect Pay checkout with the @walletconnect/pay-* Headless SDK — a step-by-step React / Next.js walkthrough: server proxy, transport, AppKit, wallet seam, signer, and rendering the snapshot." --- -The Headless SDK is a set of framework-agnostic [`@walletconnect/pay-*`](https://www.npmjs.com/org/walletconnect) packages that own the WalletConnect Pay payment flow while you own the UI. This page walks through building a complete checkout in **React / Next.js**, then documents the public API of every package. - - - **Beta (v0.1.x)** — Public APIs may change between minor releases until 1.0. New to the SDK? Read the [PSP Overview](/payments/psps/overview) first for the architecture and the role of each package. - - -The fastest way to learn the SDK is to read the reference checkout it's extracted from: - - - A complete, branded Next.js checkout built on the four packages. Every snippet on this page comes from it. - - -## How a payment flows - -Your gateway never holds the Engine API key in the browser. The browser talks to *your* server, and your server talks to the WalletConnect Pay Engine: - -``` -Browser Your Next.js server WalletConnect Pay -──────── ─────────────────── ───────────────── -usePaymentSession - │ Transport seam - ▼ (createHttpTransport) - fetch /api/wcp/payment/:id/* ──► Route Handler - │ createEngineClient - ▼ (holds the API key) - Engine API ──────────────► /v1/gateway/payment/:id/* -``` +This page walks through building a complete checkout in **React / Next.js** on the Headless SDK. New to the SDK? Read [How it works](/payments/psps/headless-sdk/how-it-works) first for the architecture and the role of each seam. You build four things, all shown below: @@ -43,6 +17,10 @@ You build four things, all shown below: Then `usePaymentSession` ties them together and gives you a snapshot to render. + + The browser never holds the Engine API key. It talks to *your* server, and your server talks to the WalletConnect Pay Engine — see [The Engine API key never reaches the browser](/payments/psps/headless-sdk/how-it-works#the-engine-api-key-never-reaches-the-browser). + + ## Prerequisites - **Node 18+** and a React 18/19 app. This guide uses **Next.js** (App Router). @@ -410,221 +388,6 @@ WCP_API_URL=https://staging.api.pay.walletconnect.org WCP_WALLET_API_KEY= ``` ---- - -# Package reference - -## `@walletconnect/pay-core` - -The foundation: Engine contract types, CAIP utilities, and the `Transport` seam. Zero runtime dependencies. Two entry points — a browser-safe main entry and a server-only `/server` entry that holds the key. - -### Browser entry — `@walletconnect/pay-core` - -```typescript -import { createHttpTransport, type Transport, type HttpTransportConfig } from '@walletconnect/pay-core' - -interface HttpTransportConfig { - baseUrl?: string // default '/api/wcp' - fetch?: typeof fetch - timeoutMs?: number // default 30_000 -} - -function createHttpTransport(config?: HttpTransportConfig): Transport -``` - -The `Transport` contract (the seam the runtime depends on) — five methods, each resolving to an `EngineResponse` envelope (never throwing): - -```typescript -interface Transport { - getPayment(paymentId): Promise> - getPaymentOptions(paymentId, request): Promise> - fetchOptionActions(paymentId, request): Promise> - confirmPayment(paymentId, request): Promise> - getPaymentStatus(paymentId): Promise> -} -``` - -Also exported: all Engine contract types (`GetPaymentResponse`, `PaymentOptionExtended`, `Amount`, `CollectData`, `PaymentStatus`, …), CAIP utilities (`parseCaip2`, `parseCaip10`, …), and transport error helpers (`TRANSPORT_ERROR_CODES`, `isAbortError`, `DEFAULT_TIMEOUT_MS`). - -### Server entry — `@walletconnect/pay-core/server` - -```typescript -import { createEngineClient, type EngineClient, type EngineClientConfig, DEFAULT_VERSION } from '@walletconnect/pay-core/server' - -interface EngineClientConfig { - apiUrl: string // e.g. https://staging.api.pay.walletconnect.org (no trailing slash) - apiKey: string // your Gateway Api-Key — keep secret, server-side only - version?: string // WCP API version; defaults to DEFAULT_VERSION - fetch?: typeof fetch - timeoutMs?: number // default 30_000 -} - -function createEngineClient(config: EngineClientConfig): EngineClient -``` - -`EngineClient` exposes the same five methods as `Transport`, but attaches the secret `Api-Key` and `Wcp-Version` headers and calls the Engine directly. Use it only on the server. - -## `@walletconnect/pay-state` - -The headless runtime: the payment state machine, the injectable seam contracts, the framework-agnostic `PaymentController`, the signing strategies, and the public `PaymentSnapshot` view-model. - -### `createPaymentController` - -The framework-agnostic binding (the React hook wraps this). Use it directly in a non-React host. - -```typescript -import { createPaymentController, type PaymentController, type PaymentControllerOptions } from '@walletconnect/pay-state' - -interface PaymentControllerOptions { - paymentId: string - seams: PaymentSessionSeams // { transport, clock, signer?, telemetry? } - wallet: WalletProvider - initialPayment?: GetPaymentResponse - signingTimeoutMs?: number - onMachineEvent?: MachineEventObserver // read-only analytics observer -} - -function createPaymentController(options: PaymentControllerOptions): PaymentController -``` - -```typescript -interface PaymentController { - getSnapshot(): PaymentSnapshot - subscribe(listener: () => void): () => void - start(): void - destroy(): void - // domain actions (see the React hook below for the full list) - connectWallet(wallet, namespace?, options?): void - // … -} -``` - -### Seams - -The runtime reaches the outside world only through these contracts: - -```typescript -interface PaymentSessionSeams { - transport: Transport // Engine calls (from pay-core) - clock: Clock // intervals + page visibility (for status polling) - signer?: Signer // signing capability — required for an end-to-end payment - telemetry?: Telemetry // optional analytics breadcrumbs -} - -interface WalletProvider { - getAccounts(): Partial> - getProvider(network: Caip2 & { namespace: T }): ProviderForNamespace | null - connect(wallet: WalletRef, namespace: Namespace, options?: WalletConnectOptions): Promise - disconnect(namespace?: Namespace): Promise - switchNetwork(caipNetworkId: string): Promise - subscribe(listener: () => void): () => void -} - -interface Signer { - signActions(option: PaymentOptionExtended, range?: ActionRange): Promise -} -``` - -Browser defaults are provided so you only have to inject `transport`, `wallet`, and `signer`: - -```typescript -import { browserClock, noopTelemetry, browserDefaults } from '@walletconnect/pay-state' -``` - -### Signing strategies - -```typescript -import { EvmSigningStrategy, SolanaSigningStrategy, signOptionActions } from '@walletconnect/pay-state' - -// Dispatch each of an option's actions to the matching strategy: -signOptionActions(option: PaymentOptionExtended, strategies: SigningStrategy[], range?: ActionRange): Promise -``` - -### `PaymentSnapshot` - -The public, serializable view-model. `state` is one of: - -| Group | States | -| ----------- | ---------------------------------------------------------------------------------------- | -| Loading | `Initializing` | -| Wallet | `ReadyForWallet`, `ConnectingWallet` | -| Options | `LoadingOptions`, `OptionsReady`, `NoOptions`, `OptionSelected` | -| Compliance | `InformationCapture` | -| Approval | `RequiresApproval`, `AwaitingWalletApproval`, `WaitingForConfirmation` | -| Terminal | `Succeeded`, `Failed`, `PaymentExpired`, `PaymentCancelled`, `InvalidPayment`, `SanctionedUser` | - -```typescript -interface PaymentSnapshot { - state: PaymentState - payment?: GetPaymentResponse - options: PaymentOptionExtended[] - selectedOption?: PaymentOptionExtended - collectData?: CollectData | null - wallet: { isConnected: boolean; accounts: string[] } - requiresApproval: boolean - signingError?: { code: string; message?: string } - isQuoteExpired: boolean - profileId?: string - profileNotFound: boolean - // …diagnostic fields -} -``` - -## `@walletconnect/pay-react` - -A single hook — `usePaymentSession` — a `useSyncExternalStore`-based binding over the controller. SSR-safe, tear-free, zero XState leak. - -```typescript -import { usePaymentSession, type UsePaymentSessionOptions, type PaymentSessionApi } from '@walletconnect/pay-react' - -function usePaymentSession(options: UsePaymentSessionOptions): PaymentSessionApi -``` - -`UsePaymentSessionOptions` matches `PaymentControllerOptions` (`paymentId`, `seams`, `wallet`, `initialPayment?`, `signingTimeoutMs?`, `onMachineEvent?`). The return is `{ snapshot }` plus the named actions: - -| Action | Drives | -| ----------------------------------------------- | ----------------------------------------------- | -| `connectWallet(wallet, namespace?, options?)` | Begin connecting a wallet | -| `disconnectWallet(namespace?)` | Disconnect one namespace, or all | -| `selectOption(option, rank)` | Pick a payment option | -| `confirmSelection()` | Confirm and move toward signing | -| `unselectOption()` | Return to the option list | -| `submitInfoCapture(data)` | Submit collected KYC/contact data | -| `navigateBack()` | Step back | - -Plus a **host-orchestration channel** for signals the runtime can't observe itself — `refreshOptions`, `notifyQuoteExpired`, `acknowledgeQuoteExpiry`, `markUserSanctioned`, `setProfileLookup`, `notifyPaymentExpired`, `failWalletConnection`. Most gateways won't need these to start. - -## `@walletconnect/pay-appkit` - -The Reown AppKit adapter — implements the `WalletProvider` seam over an AppKit instance, and ships a headless wallet-picker controller. The main entry is framework-neutral; the React hook lives on `/react`. - -### Main entry — `@walletconnect/pay-appkit` - -```typescript -import { - createAppKitWalletProvider, // (appKit, options?) => WalletProvider - createAppKitWalletList, // (appKit, options?) => AppKitWalletList (framework-neutral picker) - loadSolanaWeb3, // lazy @solana/web3.js codec loader for the Solana signing strategy - applyPlacements, resolvePlacements, // wallet-ordering helpers - type AppKit, // re-exported AppKit instance type — don't import @reown/appkit directly - type WalletListItem, type ConnectedWallet, type WalletListState, type WalletListOptions -} from '@walletconnect/pay-appkit' -``` - -`createAppKitWalletList` returns a controller — `wallet` (the seam), `getState()`, `subscribe()`, `fetchWallets()`, `search()`, `loadMore()`, `getWcUri()` — for non-React hosts. - -### React entry — `@walletconnect/pay-appkit/react` - -```typescript -import { useAppKitWalletProvider, type AppKitWalletProviderHandle, type UseAppKitWalletProviderOptions } from '@walletconnect/pay-appkit/react' - -function useAppKitWalletProvider(appKit: AppKit | undefined, options?: UseAppKitWalletProviderOptions): AppKitWalletProviderHandle -``` - -The handle extends `WalletListState` and adds `wallet` (the seam to hand `usePaymentSession`), `searchQuery`/`setSearchQuery` (debounced), `fetchWallets`, `loadMore`, and `getWcUri` for the pairing QR. - ---- - ## Without React (framework-neutral) Every React API here wraps a framework-agnostic core. A vanilla / Vue / Svelte host uses `createPaymentController` (pay-state) + `createAppKitWalletList` (pay-appkit) directly: @@ -657,8 +420,8 @@ controller.start() ## Next steps - - The architecture, the five seams, and when to reach for the Headless SDK. + + The full public API of pay-core, pay-state, pay-react, and pay-appkit. The Gateway and Payments endpoints behind the SDK. diff --git a/payments/psps/headless-sdk/overview.mdx b/payments/psps/headless-sdk/overview.mdx new file mode 100644 index 0000000..58e0d2e --- /dev/null +++ b/payments/psps/headless-sdk/overview.mdx @@ -0,0 +1,63 @@ +--- +title: "Headless SDK" +sidebarTitle: "Overview" + +metatags: + description: "Build your own branded crypto checkout on WalletConnect Pay with the Headless SDK — a set of framework-agnostic @walletconnect/pay-* packages that own the payment flow while you own the UI, transport, and wallet connection." +--- + +The Headless SDK is a set of framework-agnostic [`@walletconnect/pay-*`](https://www.npmjs.com/org/walletconnect) packages that own the WalletConnect Pay payment flow while you own the UI. It's the exact runtime that powers our own hosted Buyer Experience — extracted so you can build a fully branded, fully owned checkout on the same engine. + +You get the payment state machine, Engine API client, and wallet orchestration; you bring your own UI, branding, routing, and infrastructure. + + + **Beta (v0.1.x)** — The Headless SDK is under active development. Public APIs may change between minor releases until 1.0. [Talk to us](https://share.hsforms.com/1XsMCkUxFT2Cte8SCeAh89wnxw6s) before going to production. + + +## Who is it for? + +- **Payment Service Providers & Acquirers** who want crypto checkout inside their own product, with their own design system and domain — not a redirect to a third-party page. +- **Platforms & marketplaces** embedding pay-with-crypto directly into an existing checkout flow. +- **Teams that need control** over every step of the UX: wallet selection, network/token choice, compliance prompts, success and error states. + +If you just want to accept payments with the least effort, use the [hosted gateway](/payments/overview) or the [Ecommerce integration](/payments/ecommerce/overview) instead. Reach for the Headless SDK when you need to **own the experience**. + +## What you get + + + + The full payment state machine (load → connect → quote → sign → confirm → settle) as a framework-agnostic engine. No UI imposed. + + + A zero-dependency client for the WalletConnect Pay Engine, with the secret API key safely held server-side. + + + A single `usePaymentSession` hook that projects the runtime into a clean, serializable snapshot plus named actions — zero state-machine internals leak into your components. + + + A ready-made adapter over [Reown AppKit](https://reown.com/appkit) for wallet connection across EVM and Solana — or bring your own. + + + +## Supported networks & tokens + +The Headless SDK supports the full WalletConnect Pay token and network coverage — USDC, USDT, EURC, PYUSD and more across Ethereum, Polygon, Base, Optimism, Arbitrum (and Solana, rolling out). See [Token & chain coverage](/payments/token-and-chain-coverage) for the live list. + +## Full example + +The fastest way to learn the SDK is to read the reference checkout it's extracted from. Every snippet in these docs comes from it. + + + A complete, branded Next.js checkout built on the four packages. + + +## Next steps + + + + The payment lifecycle, the host + seams model, and how the API key stays server-side. + + + Build a complete checkout in React / Next.js, step by step. + + diff --git a/payments/psps/headless-sdk/packages-reference.mdx b/payments/psps/headless-sdk/packages-reference.mdx new file mode 100644 index 0000000..d7f6f60 --- /dev/null +++ b/payments/psps/headless-sdk/packages-reference.mdx @@ -0,0 +1,269 @@ +--- +title: "Packages Reference" +sidebarTitle: "Packages Reference" + +metatags: + description: "The @walletconnect/pay-* Headless SDK packages and their full public API — pay-core (Engine client + Transport seam), pay-state (runtime + seams + snapshot), pay-react (usePaymentSession), and pay-appkit (Reown AppKit wallet adapter)." +--- + +The runtime is split into layered packages. Each is independently consumable, and lower layers never depend on higher ones — so you can take only what you need. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PackageRoleDepends on
+ @walletconnect/pay-core + Engine API client — contract types, CAIP utilities, the browser Transport seam (createHttpTransport), and the server-side createEngineClient that holds your API key. The foundation.
+ @walletconnect/pay-state + The headless runtime — the payment state machine, orchestration, the injectable seam contracts, the session factory, and the public PaymentSnapshot view-model. No React, no HTTP client, no wallet SDK.pay-core
+ @walletconnect/pay-react + Thin React binding — usePaymentSession returns the snapshot plus domain actions. Zero state-machine leak.pay-state
+ @walletconnect/pay-appkit + Reown AppKit adapter — implements the WalletProvider seam over a Reown AppKit instance (plus the Solana web3 loader). A /react subpath ships the wallet-connection hook.pay-state
+ +## `@walletconnect/pay-core` + +The foundation: Engine contract types, CAIP utilities, and the `Transport` seam. Zero runtime dependencies. Two entry points — a browser-safe main entry and a server-only `/server` entry that holds the key. + +### Browser entry — `@walletconnect/pay-core` + +```typescript +import { createHttpTransport, type Transport, type HttpTransportConfig } from '@walletconnect/pay-core' + +interface HttpTransportConfig { + baseUrl?: string // default '/api/wcp' + fetch?: typeof fetch + timeoutMs?: number // default 30_000 +} + +function createHttpTransport(config?: HttpTransportConfig): Transport +``` + +The `Transport` contract (the seam the runtime depends on) — five methods, each resolving to an `EngineResponse` envelope (never throwing): + +```typescript +interface Transport { + getPayment(paymentId): Promise> + getPaymentOptions(paymentId, request): Promise> + fetchOptionActions(paymentId, request): Promise> + confirmPayment(paymentId, request): Promise> + getPaymentStatus(paymentId): Promise> +} +``` + +Also exported: all Engine contract types (`GetPaymentResponse`, `PaymentOptionExtended`, `Amount`, `CollectData`, `PaymentStatus`, …), CAIP utilities (`parseCaip2`, `parseCaip10`, …), and transport error helpers (`TRANSPORT_ERROR_CODES`, `isAbortError`, `DEFAULT_TIMEOUT_MS`). + +### Server entry — `@walletconnect/pay-core/server` + +```typescript +import { createEngineClient, type EngineClient, type EngineClientConfig, DEFAULT_VERSION } from '@walletconnect/pay-core/server' + +interface EngineClientConfig { + apiUrl: string // e.g. https://staging.api.pay.walletconnect.org (no trailing slash) + apiKey: string // your Gateway Api-Key — keep secret, server-side only + version?: string // WCP API version; defaults to DEFAULT_VERSION + fetch?: typeof fetch + timeoutMs?: number // default 30_000 +} + +function createEngineClient(config: EngineClientConfig): EngineClient +``` + +`EngineClient` exposes the same five methods as `Transport`, but attaches the secret `Api-Key` and `Wcp-Version` headers and calls the Engine directly. Use it only on the server. + +## `@walletconnect/pay-state` + +The headless runtime: the payment state machine, the injectable seam contracts, the framework-agnostic `PaymentController`, the signing strategies, and the public `PaymentSnapshot` view-model. + +### `createPaymentController` + +The framework-agnostic binding (the React hook wraps this). Use it directly in a non-React host. + +```typescript +import { createPaymentController, type PaymentController, type PaymentControllerOptions } from '@walletconnect/pay-state' + +interface PaymentControllerOptions { + paymentId: string + seams: PaymentSessionSeams // { transport, clock, signer?, telemetry? } + wallet: WalletProvider + initialPayment?: GetPaymentResponse + signingTimeoutMs?: number + onMachineEvent?: MachineEventObserver // read-only analytics observer +} + +function createPaymentController(options: PaymentControllerOptions): PaymentController +``` + +```typescript +interface PaymentController { + getSnapshot(): PaymentSnapshot + subscribe(listener: () => void): () => void + start(): void + destroy(): void + // domain actions (see the React hook below for the full list) + connectWallet(wallet, namespace?, options?): void + // … +} +``` + +### Seams + +The runtime reaches the outside world only through these contracts: + +```typescript +interface PaymentSessionSeams { + transport: Transport // Engine calls (from pay-core) + clock: Clock // intervals + page visibility (for status polling) + signer?: Signer // signing capability — required for an end-to-end payment + telemetry?: Telemetry // optional analytics breadcrumbs +} + +interface WalletProvider { + getAccounts(): Partial> + getProvider(network: Caip2 & { namespace: T }): ProviderForNamespace | null + connect(wallet: WalletRef, namespace: Namespace, options?: WalletConnectOptions): Promise + disconnect(namespace?: Namespace): Promise + switchNetwork(caipNetworkId: string): Promise + subscribe(listener: () => void): () => void +} + +interface Signer { + signActions(option: PaymentOptionExtended, range?: ActionRange): Promise +} +``` + +Browser defaults are provided so you only have to inject `transport`, `wallet`, and `signer`: + +```typescript +import { browserClock, noopTelemetry, browserDefaults } from '@walletconnect/pay-state' +``` + +### Signing strategies + +```typescript +import { EvmSigningStrategy, SolanaSigningStrategy, signOptionActions } from '@walletconnect/pay-state' + +// Dispatch each of an option's actions to the matching strategy: +signOptionActions(option: PaymentOptionExtended, strategies: SigningStrategy[], range?: ActionRange): Promise +``` + +### `PaymentSnapshot` + +The public, serializable view-model. `state` is one of: + +| Group | States | +| ----------- | ---------------------------------------------------------------------------------------- | +| Loading | `Initializing` | +| Wallet | `ReadyForWallet`, `ConnectingWallet` | +| Options | `LoadingOptions`, `OptionsReady`, `NoOptions`, `OptionSelected` | +| Compliance | `InformationCapture` | +| Approval | `RequiresApproval`, `AwaitingWalletApproval`, `WaitingForConfirmation` | +| Terminal | `Succeeded`, `Failed`, `PaymentExpired`, `PaymentCancelled`, `InvalidPayment`, `SanctionedUser` | + +```typescript +interface PaymentSnapshot { + state: PaymentState + payment?: GetPaymentResponse + options: PaymentOptionExtended[] + selectedOption?: PaymentOptionExtended + collectData?: CollectData | null + wallet: { isConnected: boolean; accounts: string[] } + requiresApproval: boolean + signingError?: { code: string; message?: string } + isQuoteExpired: boolean + profileId?: string + profileNotFound: boolean + // …diagnostic fields +} +``` + +## `@walletconnect/pay-react` + +A single hook — `usePaymentSession` — a `useSyncExternalStore`-based binding over the controller. SSR-safe, tear-free, zero XState leak. + +```typescript +import { usePaymentSession, type UsePaymentSessionOptions, type PaymentSessionApi } from '@walletconnect/pay-react' + +function usePaymentSession(options: UsePaymentSessionOptions): PaymentSessionApi +``` + +`UsePaymentSessionOptions` matches `PaymentControllerOptions` (`paymentId`, `seams`, `wallet`, `initialPayment?`, `signingTimeoutMs?`, `onMachineEvent?`). The return is `{ snapshot }` plus the named actions: + +| Action | Drives | +| ----------------------------------------------- | ----------------------------------------------- | +| `connectWallet(wallet, namespace?, options?)` | Begin connecting a wallet | +| `disconnectWallet(namespace?)` | Disconnect one namespace, or all | +| `selectOption(option, rank)` | Pick a payment option | +| `confirmSelection()` | Confirm and move toward signing | +| `unselectOption()` | Return to the option list | +| `submitInfoCapture(data)` | Submit collected KYC/contact data | +| `navigateBack()` | Step back | + +Plus a **host-orchestration channel** for signals the runtime can't observe itself — `refreshOptions`, `notifyQuoteExpired`, `acknowledgeQuoteExpiry`, `markUserSanctioned`, `setProfileLookup`, `notifyPaymentExpired`, `failWalletConnection`. Most gateways won't need these to start. + +## `@walletconnect/pay-appkit` + +The Reown AppKit adapter — implements the `WalletProvider` seam over an AppKit instance, and ships a headless wallet-picker controller. The main entry is framework-neutral; the React hook lives on `/react`. + +### Main entry — `@walletconnect/pay-appkit` + +```typescript +import { + createAppKitWalletProvider, // (appKit, options?) => WalletProvider + createAppKitWalletList, // (appKit, options?) => AppKitWalletList (framework-neutral picker) + loadSolanaWeb3, // lazy @solana/web3.js codec loader for the Solana signing strategy + applyPlacements, resolvePlacements, // wallet-ordering helpers + type AppKit, // re-exported AppKit instance type — don't import @reown/appkit directly + type WalletListItem, type ConnectedWallet, type WalletListState, type WalletListOptions +} from '@walletconnect/pay-appkit' +``` + +`createAppKitWalletList` returns a controller — `wallet` (the seam), `getState()`, `subscribe()`, `fetchWallets()`, `search()`, `loadMore()`, `getWcUri()` — for non-React hosts. + +### React entry — `@walletconnect/pay-appkit/react` + +```typescript +import { useAppKitWalletProvider, type AppKitWalletProviderHandle, type UseAppKitWalletProviderOptions } from '@walletconnect/pay-appkit/react' + +function useAppKitWalletProvider(appKit: AppKit | undefined, options?: UseAppKitWalletProviderOptions): AppKitWalletProviderHandle +``` + +The handle extends `WalletListState` and adds `wallet` (the seam to hand `usePaymentSession`), `searchQuery`/`setSearchQuery` (debounced), `fetchWallets`, `loadMore`, and `getWcUri` for the pairing QR. + +## Next steps + + + + The step-by-step React / Next.js walkthrough. + + + The Gateway and Payments endpoints behind the SDK. + + diff --git a/payments/psps/overview.mdx b/payments/psps/overview.mdx index 8102b80..2749055 100644 --- a/payments/psps/overview.mdx +++ b/payments/psps/overview.mdx @@ -1,173 +1,48 @@ --- -title: "WalletConnect Pay - PSP Integration" +title: "WalletConnect Pay for PSPs" sidebarTitle: "Overview" metatags: - description: "Build your own branded crypto checkout on WalletConnect Pay with the Headless SDK — a set of framework-agnostic @walletconnect/pay-* packages that own the payment flow while you own the UI, transport, and wallet connection." + description: "WalletConnect Pay for Payment Service Providers — accept crypto payments through the hosted gateway, or build a fully branded, fully owned checkout with the Headless SDK." --- -WalletConnect Pay ships a **hosted gateway** at `pay.walletconnect.com` that handles the full payment experience out of the box. PSPs, acquirers, and platforms that need a **fully branded, fully owned checkout** can instead build their own gateway on the same engine using the **Headless SDK**. +WalletConnect Pay lets Payment Service Providers, acquirers, and platforms accept crypto payments on the same engine that powers our hosted gateway. You choose how much of the experience you own — from a redirect to a hosted page, all the way to a fully branded checkout built inside your own product. -The Headless SDK is the exact runtime that powers our own hosted Buyer Experience — extracted into a set of framework-agnostic [`@walletconnect/pay-*`](https://www.npmjs.com/org/walletconnect) packages. You get the payment state machine, Engine API client, and wallet orchestration; you bring your own UI, branding, routing, and infrastructure. - - - **Beta (v0.1.x)** — The Headless SDK is under active development. Public APIs may change between minor releases until 1.0. [Talk to us](https://share.hsforms.com/1XsMCkUxFT2Cte8SCeAh89wnxw6s) before going to production. - - -## Who is it for? - -- **Payment Service Providers & Acquirers** who want crypto checkout inside their own product, with their own design system and domain — not a redirect to a third-party page. -- **Platforms & marketplaces** embedding pay-with-crypto directly into an existing checkout flow. -- **Teams that need control** over every step of the UX: wallet selection, network/token choice, compliance prompts, success and error states. - -If you just want to accept payments with the least effort, use the [hosted gateway](/payments/overview) or the [Ecommerce integration](/payments/ecommerce/overview) instead. Reach for the Headless SDK when you need to **own the experience**. - -## What you get +## Two ways to integrate - - The full payment state machine (load → connect → quote → sign → confirm → settle) as a framework-agnostic engine. No UI imposed. - - - A zero-dependency client for the WalletConnect Pay Engine, with the secret API key safely held server-side. + + Send buyers to `pay.walletconnect.com` and let WalletConnect Pay handle the whole experience — wallet selection, quoting, signing, and settlement. The fastest path to accepting payments, with no UI to build. - - A single `usePaymentSession` hook that projects the runtime into a clean, serializable snapshot plus named actions — zero state-machine internals leak into your components. - - - A ready-made adapter over [Reown AppKit](https://reown.com/appkit) for wallet connection across EVM and Solana — or bring your own. + + Build a **fully branded, fully owned checkout** inside your own product. You get the payment runtime, Engine client, and wallet orchestration as framework-agnostic packages; you bring the UI, branding, and domain. -## The packages - -The runtime is split into layered packages. Each is independently consumable, and lower layers never depend on higher ones — so you can take only what you need. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PackageRoleDepends on
- @walletconnect/pay-core - Engine API client — contract types, CAIP utilities, the browser Transport seam (createHttpTransport), and the server-side createEngineClient that holds your API key. The foundation.
- @walletconnect/pay-state - The headless runtime — the payment state machine, orchestration, the injectable seam contracts, the session factory, and the public PaymentSnapshot view-model. No React, no HTTP client, no wallet SDK.pay-core
- @walletconnect/pay-react - Thin React binding — usePaymentSession returns the snapshot plus domain actions. Zero state-machine leak.pay-state
- @walletconnect/pay-appkit - Reown AppKit adapter — implements the WalletProvider seam over a Reown AppKit instance (plus the Solana web3 loader). A /react subpath ships the wallet-connection hook.pay-state
+## Which one is for me? -## How it fits together +Reach for the **hosted gateway** if you want to accept payments with the least effort and are happy to send buyers to a WalletConnect-hosted page. See the [hosted gateway overview](/payments/overview) or the [Ecommerce integration](/payments/ecommerce/overview). -The Headless SDK follows a **headless runtime + host** model. The payment flow lives entirely in the SDK; your application is the **host** that consumes it. The runtime reaches the outside world only through five injectable **seams** — so you can swap in your own transport, wallet, timing, and analytics, and reuse the exact same payment machine. - -``` - Your gateway (the host) - ─ your UI, branding, routing ─ - │ consumes - ▼ - @walletconnect/pay-react · usePaymentSession - │ drives - ▼ - @walletconnect/pay-state · payment runtime - │ reaches the outside world only through seams - ▼ - ┌──────────┬───────────────┬─────────┬────────┬───────────┐ - │Transport │ WalletProvider │ Signer │ Clock │ Telemetry │ - └────┬─────┴───────┬───────┴────┬────┴───┬────┴─────┬─────┘ - │ │ │ │ │ - your server AppKit / signing timers/ analytics - → Engine API your wallet strategy polling -``` - -The five seams the runtime depends on: - -| Seam | What it abstracts | Provided by | -| ---------------- | ------------------------------------------ | ---------------------------------------------------- | -| `Transport` | Engine HTTP calls | `pay-core` `createHttpTransport` → your server route | -| `WalletProvider` | connect / accounts / provider / switch | `pay-appkit`, or your own wallet integration | -| `Signer` | sign a payment option's wallet-RPC actions | `pay-state` signing strategies over the wallet seam | -| `Clock` | intervals + page visibility (for polling) | browser timers (the SDK ships a default) | -| `Telemetry` | analytics breadcrumbs | your analytics pipeline (optional) | +Reach for the **Headless SDK** when you need to **own the experience** end to end — wallet selection, network and token choice, compliance prompts, success and error states — all inside your own design system and domain, with no redirect to a third-party page. - `@walletconnect/pay-state` ships browser-ready `Clock` and `Telemetry` defaults (`browserClock`, `noopTelemetry`). In practice a React/Next.js gateway wires three seams itself — a `Transport` (pointed at your server route), a `WalletProvider` (the AppKit adapter), and a `Signer` (built from the wallet) — and reuses `browserClock` for the rest. `Telemetry` is optional. + Both paths run on the same WalletConnect Pay Engine and share the same token and network coverage. You can start with the hosted gateway and move to the Headless SDK later without re-doing your backend integration. -## The Engine API key never reaches the browser - -WalletConnect Pay's Engine API is authenticated with a secret API key that **must stay server-side**. The SDK enforces this split: - -``` -Browser → Transport seam → your server route → Engine API - (no secret) (createEngineClient holds the key) -``` - -`pay-core` exposes two entry points for exactly this: `createHttpTransport` for the browser (talks to *your* server) and `createEngineClient` (imported from `@walletconnect/pay-core/server`) which holds the API key and talks to the Engine. In a Next.js app, your Route Handler sits in the middle. - -## The payment lifecycle - -Whatever UI you build, the runtime moves a payment through the same stages. Your job is to render each stage and call the matching action. - - - - The buyer arrives with a payment ID (from a QR code, link, or your checkout). The runtime fetches the payment intent — amount, merchant, accepted tokens. - - - The buyer connects a wallet through the `WalletProvider` seam. The runtime reads their accounts across the supported networks. - - - Given the connected accounts, the Engine returns the concrete ways to pay — token, network, amount, fees, and whether compliance data is required. - - - The buyer picks an option. The runtime builds the transaction(s) and the exact wallet-RPC actions to sign. - - - The `Signer` drives the wallet through the required signatures (e.g. a permit + the payment). - - - Signed results are submitted to confirm the payment. The runtime then polls status until the payment succeeds, fails, or expires. - - - -## Supported networks & tokens +## Who is it for? -The Headless SDK supports the full WalletConnect Pay token and network coverage — USDC, USDT, EURC, PYUSD and more across Ethereum, Polygon, Base, Optimism, Arbitrum (and Solana, rolling out). See [Token & chain coverage](/payments/token-and-chain-coverage) for the live list. +- **Payment Service Providers & Acquirers** offering crypto checkout inside their own product, with their own design system and domain. +- **Platforms & marketplaces** embedding pay-with-crypto directly into an existing checkout flow. +- **Teams that need control** over every step of the UX. ## Next steps - - Install the packages and build a checkout in React / Next.js, step by step. + + Own the full checkout experience with the `@walletconnect/pay-*` packages. - The Gateway and Payments endpoints behind the SDK. + The Gateway and Payments endpoints behind WalletConnect Pay. From d1947e3fad328323d0f3267804cff01b46424d32 Mon Sep 17 00:00:00 2001 From: Enes Date: Thu, 2 Jul 2026 12:50:42 +0300 Subject: [PATCH 3/3] docs(payments): move PSPs section after Wallets in the sidebar Co-Authored-By: Claude Opus 4.8 (1M context) --- docs.json | 60 +++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/docs.json b/docs.json index 4e7a758..b885e2e 100644 --- a/docs.json +++ b/docs.json @@ -60,21 +60,6 @@ "payments/merchant-api/logo-specification" ] }, - { - "group": "WalletConnect Pay for PSPs", - "pages": [ - "payments/psps/overview", - { - "group": "Headless SDK", - "pages": [ - "payments/psps/headless-sdk/overview", - "payments/psps/headless-sdk/how-it-works", - "payments/psps/headless-sdk/implementation", - "payments/psps/headless-sdk/packages-reference" - ] - } - ] - }, { "group": "WalletConnect Pay for Wallets", "pages": [ @@ -111,6 +96,21 @@ "payments/wallets/tap-to-pay" ] }, + { + "group": "WalletConnect Pay for PSPs", + "pages": [ + "payments/psps/overview", + { + "group": "Headless SDK", + "pages": [ + "payments/psps/headless-sdk/overview", + "payments/psps/headless-sdk/how-it-works", + "payments/psps/headless-sdk/implementation", + "payments/psps/headless-sdk/packages-reference" + ] + } + ] + }, { "group": "Ecommerce and Online Checkout", "pages": [ @@ -217,21 +217,6 @@ "payments/merchant-api/logo-specification" ] }, - { - "group": "WalletConnect Pay for PSPs", - "pages": [ - "payments/psps/overview", - { - "group": "Headless SDK", - "pages": [ - "payments/psps/headless-sdk/overview", - "payments/psps/headless-sdk/how-it-works", - "payments/psps/headless-sdk/implementation", - "payments/psps/headless-sdk/packages-reference" - ] - } - ] - }, { "group": "WalletConnect Pay for Wallets", "pages": [ @@ -268,6 +253,21 @@ "payments/wallets/tap-to-pay" ] }, + { + "group": "WalletConnect Pay for PSPs", + "pages": [ + "payments/psps/overview", + { + "group": "Headless SDK", + "pages": [ + "payments/psps/headless-sdk/overview", + "payments/psps/headless-sdk/how-it-works", + "payments/psps/headless-sdk/implementation", + "payments/psps/headless-sdk/packages-reference" + ] + } + ] + }, { "group": "Ecommerce and Online Checkout", "pages": [