@walletconnect/pay-* packages and never touch @reown/*, wagmi, or viem directly.
You build three things:
- A server proxy — routes that forward to the Engine with your secret key.
- A browser transport — points the runtime at those routes.
- The AppKit provider — one component (
<PayAppKitProvider>in React) or one factory call (createPayAppKitin JavaScript).
usePaymentSession (React) or createPaymentController (JavaScript) ties everything 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.
Prerequisites
- Node 18+. The React example uses Next.js (App Router); the JavaScript example is framework-neutral.
- A Reown Project ID — create one at dashboard.reown.com. Enable the headless feature on the project.
- A WalletConnect Pay Gateway API key for the Engine (server-side). Talk to us to get onboarded.
Install
Install only the Headless SDK. Wallet connectivity (@reown/appkit, wagmi, viem, @solana/web3.js, @tanstack/react-query) comes transitively through @walletconnect/pay-appkit — you don’t add or configure any of it.
@walletconnect/pay-react is the React hook binding — omit it if you’re not using React.
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. This is framework-agnostic — any server works; the example uses Next.js Route Handlers.lib/server/engine.ts
/api/wcp/payment/[id]. The browser transport (Step 2) calls exactly these paths:
app/api/wcp/payment/[id]/options/route.ts
app/api/wcp/payment/[id]/status/route.ts
Step 2 — Browser transport
On the client, point the runtime at your proxy.createHttpTransport issues requests to ${baseUrl}/payment/:id/..., matching the routes above.
Transport seam. It speaks the same five methods as the server client, but routes through your origin — no key, no CORS.
Step 3 — Set up AppKit (zero-config)
The SDK constructs the AppKit instance, the Wagmi/Solana adapters, and the WalletConnect-owned network set for you, in headless mode (no built-in modal — you render your own wallet picker). You supply only yourprojectId and metadata.
In React, render <PayAppKitProvider> once near the root. It owns AppKit’s client-only construction, the WagmiProvider + QueryClientProvider tree, and an SSR-safe context. In JavaScript, call createPayAppKit and await its async construction.
<PayAppKitProvider> accepts an optional queryClient (a host with its own passes it to share one cache; omit it for a fully internal one) and optional themeVariables (e.g. a host font). Both createPayAppKit and the provider load the Reown modules through a client-only dynamic import, so AppKit’s UI never enters your SSR bundle.
Step 4 — Build the checkout
Assemble the seams and drive the session. The wallet seam comes from the SDK’s wallet-list hook/controller, and the signer is a single built-in call —createAppKitSigner(wallet) — so you no longer wire up signing strategies by hand. clock is browserClock.
In React, useAppKitWalletProvider turns the AppKit instance into the WalletProvider seam and a ready-made picker controller (list, search, pagination, the pairing QR URI). Read the instance from getPayAppKitInstance() once usePayAppKit().isReady is true. In JavaScript, createAppKitWalletList is the framework-neutral equivalent.
app/[paymentId]/page.tsx
Step 5 — 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. The logic is the same for React and JavaScript — the only difference is where the snapshot comes from (usePaymentSession vs controller.getSnapshot()).
disconnectWallet(namespace?) drops one namespace or all of them.
Environment variables
.env.local
In a Vite / non-Next.js host, expose the project ID under that toolchain’s client env convention (e.g.
VITE_APPKIT_PROJECT_ID) and keep WCP_WALLET_API_KEY on the server only.Reference apps
headless-checkout (Next.js)
The full React checkout —
<PayAppKitProvider> + usePaymentSession, no @reown/* in the app.headless-checkout-vanilla
The same checkout with no framework —
createPaymentController + manual subscribe + imperative render.Next steps
Packages Reference
The full public API of pay-core, pay-state, pay-react, and pay-appkit.
API Reference
The Gateway and Payments endpoints behind the SDK.