Privy embedded wallets give every user a wallet at signup, with no extensions or seed phrases required. WalletConnect Pay lets merchants generate payment links that any wallet can fulfill. This recipe shows how to combine them so users can pay for anything in-app, with a single tap.Using Privy and WalletConnect Pay together, your app can:
Let users paste a WalletConnect Pay link and complete the payment with their Privy wallet
Support multiple chains (Ethereum, Base, Polygon, Arbitrum, Optimism, and more)
Handle ERC-20 token transfers (like USDC) alongside native ETH payments
WalletConnect Pay offers interchange revenue for wallets and $WCT cashback for users. See the
WalletConnect Pay overview for details
on earning revenue through your integration.
Setting createOnLogin to 'users-without-wallets' means Privy automatically provisions an
Ethereum wallet the first time a user logs in. No extra steps needed.
Use isPaymentLink from @reown/walletkit to verify the link before making API calls:
import {isPaymentLink} from '@reown/walletkit';const uri = 'https://pay.walletconnect.com/...';if (!isPaymentLink(uri)) { throw new Error('Not a valid WalletConnect Pay link');}
Build the user’s account list from supported chain prefixes and their wallet address, then call getPaymentOptions:
import {useWallets} from '@privy-io/react-auth';declare function getWalletKit(): Promise<any>;declare const uri: string;const SUPPORTED_CHAINS = [ 'eip155:1', // Ethereum 'eip155:8453', // Base 'eip155:137', // Polygon 'eip155:42161', // Arbitrum 'eip155:10' // Optimism];const {wallets} = useWallets();const embeddedWallet = wallets.find((w) => w.walletClientType === 'privy');const walletkit = await getWalletKit();const address = embeddedWallet.address;const accounts = SUPPORTED_CHAINS.map((prefix) => `${prefix}:${address}`);try { const options = await walletkit.pay.getPaymentOptions({ paymentLink: uri, accounts, includePaymentInfo: true });} catch (error) { if (error.message.includes('payment not found')) { // The payment link is invalid or has been cancelled } else if (error.message.includes('expired')) { // The payment has expired } else { throw error; }}
The response contains:
paymentId — unique identifier for this payment session
options — array of payment methods (different tokens and chains the user can pay with)
info — merchant name, requested amount, and expiry (expiresAt)
Check options.info.expiresAt and warn users when time is running low. Payments expire after a
set period, and signing after expiry results in a failed payment.
Some payment options require additional user information (e.g. shipping address). Check the selected option for a collectData object and render the provided URL in an iframe:
const collectData = selectedOption.collectData;if (collectData?.url) { // Render collectData.url in an iframe. // WalletConnect handles the form UI inside the iframe. // Listen for postMessage events from the iframe: // { type: 'IC_COMPLETE' } → data collected successfully, proceed to signing // { type: 'IC_ERROR' } → collection failed, show an error // To pre-populate known user data, append a base64-encoded JSON query param: // const prefill = btoa(JSON.stringify({ email: '[email protected]' })); // const url = `${collectData.url}?prefill=${prefill}`;}
The iframe submits collected data directly to WalletConnect. Do not pass collectedData to
confirmPayment() when using this flow — it is handled automatically.
Once the user selects a payment option, fetch the transaction actions and sign them with the Privy wallet. The API can return multiple actions (e.g. a token approval followed by a Permit2 signature), and different RPC methods require different parameter handling:
The three methods above (eth_sendTransaction, eth_signTypedData_v4, personal_sign) are the
most common, but the API can return any wallet RPC method. Add additional cases to the switch
statement as needed for your integration.
Configure which chains your app supports. The SUPPORTED_CHAINS array in step 3 determines which payment options WalletConnect Pay returns.
Chain
Chain ID
CAIP-2 prefix
USDC address
Ethereum
1
eip155:1
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Base
8453
eip155:8453
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Polygon
137
eip155:137
0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359
Arbitrum
42161
eip155:42161
0xaf88d065e77c8cC2239327C5EDb3A432268e5831
Optimism
10
eip155:10
0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85
WalletConnect Pay matches available payment options to the chains your app lists. Add more chains
to SUPPORTED_CHAINS to offer users additional payment methods.