Packages Reference
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.
| Package | Role | Depends on |
|---|---|---|
@walletconnect/pay-core | WCP 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: WCP 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
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): TransportThe Transport contract (the seam the runtime depends on) — five methods, each resolving to a WcpResponse<T> envelope (never throwing):
interface Transport {
getPayment(paymentId): Promise<WcpResponse<GetPaymentResponse>>
getPaymentOptions(paymentId, request): Promise<WcpResponse<GetPaymentOptionsResponseExtended>>
fetchOptionActions(paymentId, request): Promise<WcpResponse<WcpBuildData>>
confirmPayment(paymentId, request): Promise<WcpResponse<unknown>>
getPaymentStatus(paymentId): Promise<WcpResponse<GetPaymentStatusResponse>>
}Also exported: all WCP 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
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): EngineClientEngineClient exposes the same five methods as Transport, but attaches the secret Api-Key and Wcp-Version headers and calls WCP 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.
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): PaymentControllerinterface 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:
interface PaymentSessionSeams {
transport: Transport // WCP 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<Record<Namespace, NamespaceAccount | undefined>>
getProvider<T extends Namespace>(network: Caip2 & { namespace: T }): ProviderForNamespace<T> | null
connect(wallet: WalletRef, namespace: Namespace, options?: WalletConnectOptions): Promise<void>
disconnect(namespace?: Namespace): Promise<void>
switchNetwork(caipNetworkId: string): Promise<void>
subscribe(listener: () => void): () => void
}
interface Signer {
signActions(option: PaymentOptionExtended, range?: ActionRange): Promise<SignPaymentResult[]>
}Browser defaults are provided so you only have to inject transport, wallet, and signer:
import { browserClock, noopTelemetry, browserDefaults } from '@walletconnect/pay-state'Signing strategies
AppKit hosts don't need these directly — @walletconnect/pay-appkit exports a zero-config createAppKitSigner(wallet) that wraps them with the bundled Solana codec. These are the low-level primitives, for a custom wallet integration.
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<SignPaymentResult[]>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 |
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.
import { usePaymentSession, type UsePaymentSessionOptions, type PaymentSessionApi } from '@walletconnect/pay-react'
function usePaymentSession(options: UsePaymentSessionOptions): PaymentSessionApiUsePaymentSessionOptions 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 — it owns the entire AppKit setup so a host stays @reown/*-free. It constructs the instance, implements the WalletProvider seam over it, provides a zero-config Signer, and ships a headless wallet-picker controller. The main entry is framework-neutral; the React provider + hooks live on /react.
Main entry — @walletconnect/pay-appkit
import {
createPayAppKit, // ({ projectId, metadata, themeVariables? }) => PayAppKit (one-call setup)
createAppKitSigner, // (wallet) => Signer (zero-config, bundles the Solana codec)
createAppKitWalletList, // (appKit, options?) => AppKitWalletList (framework-neutral picker)
createAppKitWalletProvider, // (appKit, options?) => WalletProvider
SUPPORTED_NETWORKS, EVM_NETWORKS, // the WC-owned network set (baked in, not host config)
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 PayAppKit, type CreatePayAppKitOptions, type PayAppKitMetadata,
type WalletListItem, type ConnectedWallet, type WalletListState, type WalletListOptions
} from '@walletconnect/pay-appkit'createPayAppKit builds the instance with the WC-owned networks + adapters in headless mode. It's client-only and async — await payAppKit.whenReady(), then read getInstance() / getWagmiConfig() / getHooks() / getError() / isInitialized().
createAppKitWalletList returns a controller — wallet (the seam), getState(), subscribe(), fetchWallets(), search(), loadMore(), getWcUri() — for non-React hosts.
React entry — @walletconnect/pay-appkit/react
The zero-config provider that owns AppKit's construction + the Wagmi/Query tree + an SSR-safe context, plus the state hook and the imperative instance accessor:
import {
PayAppKitProvider, // <PayAppKitProvider projectId metadata themeVariables? queryClient?>
usePayAppKit, // SSR-safe hook: isReady / account / network / walletConnection / connect / disconnect / …
getPayAppKitInstance, // module-level accessor to the same AppKit instance (for non-hook consumers)
useAppKitWalletProvider,
type AppKitWalletProviderHandle,
type UseAppKitWalletProviderOptions
} from '@walletconnect/pay-appkit/react'
function useAppKitWalletProvider(appKit: AppKit | undefined, options?: UseAppKitWalletProviderOptions): AppKitWalletProviderHandleuseAppKitWalletProvider's handle extends WalletListState and adds wallet (the seam to hand usePaymentSession), searchQuery/setSearchQuery (debounced), fetchWallets, loadMore, and getWcUri for the pairing QR.