WalletConnect Pay SDK - React Native - WalletConnect Pay Docs
Sample Wallet
For a complete working example, check out our sample wallet implementation:
Requirements
- React Native 0.70+
@walletconnect/react-native-compatinstalled and linked
Installation
Install the WalletConnect Pay SDK using npm or yarn:
- npm
npm install @walletconnect/pay
- yarn
yarn add @walletconnect/pay
React Native Setup This SDK requires the WalletConnect React Native native module. Make sure you have @walletconnect/react-native-compat installed and linked in your React Native project:
npm install @walletconnect/react-native-compat
Architecture
The SDK uses a provider abstraction that allows different implementations:
- NativeProvider: Uses React Native uniffi module (current)
- WasmProvider: Uses WebAssembly module (coming soon for web browsers)
The SDK auto-detects the best available provider for your environment.
Initialization
Initialize the WalletConnect Pay client with your credentials:
import { WalletConnectPay } from "@walletconnect/pay";
const client = new WalletConnectPay({
appId: "your-wcp-id",
// OR use apiKey instead:
// apiKey: "your-api-key",
});
Configuration Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
appId |
string |
No* | WCP ID for authentication |
apiKey |
string |
No* | API key for authentication |
clientId |
string |
No | Client ID for tracking |
baseUrl |
string |
No | Custom API base URL |
logger |
Logger |
No | Custom logger instance or level |
Either appId or apiKey must be provided for authentication.
Supported Networks & Tokens
WalletConnect Pay currently supports the following tokens and networks:
| Token | Network | Chain ID | CAIP-10 Format |
|---|---|---|---|
| USDC | Arbitrum | 42161 | eip155:42161:{address} |
| USDC | Base | 8453 | eip155:8453:{address} |
| USDC | Polygon | 137 | eip155:137:{address} |
| USDC | Ethereum | 1 | eip155:1:{address} |
| USDC | Optimism | 10 | eip155:10:{address} |
| USDC | Monad | 143 | eip155:143:{address} |
| USDC | Celo | 42220 | eip155:42220:{address} |
| USDC | BSC | 56 | eip155:56:{address} |
| EURC | Ethereum | 1 | eip155:1:{address} |
| EURC | Base | 8453 | eip155:8453:{address} |
| USDT0 | Arbitrum | 42161 | eip155:42161:{address} |
| PYUSD | Ethereum | 1 | eip155:1:{address} |
| PYUSD | Arbitrum | 42161 | eip155:42161:{address} |
| USDG | Ethereum | 1 | eip155:1:{address} |
| USDT | Ethereum | 1 | eip155:1:{address} |
| USDT | Polygon | 137 | eip155:137:{address} |
| USDT | BSC | 56 | eip155:56:{address} |
Include accounts for all supported networks to maximize payment options for your users.
Payment Flow
The payment flow consists of five main steps:Get Options -> Collect Data (if required) -> Get Actions -> Sign Actions -> Confirm Payment
Get Payment Options
When a user scans a payment QR code or opens a payment link, fetch available payment options:
const options = await client.getPaymentOptions({
paymentLink: "https://pay.walletconnect.com/pay_123",
accounts: [
`eip155:1:${walletAddress}`, // Ethereum Mainnet
`eip155:8453:${walletAddress}`, // Base
],
includePaymentInfo: true,
});
console.log("Payment ID:", options.paymentId);
console.log("Options:", options.options);
// Display merchant info
if (options.info) {
console.log("Merchant:", options.info.merchant.name);
console.log("Amount:", options.info.amount.display.assetSymbol, options.info.amount.value);
}
// Check which options require data collection
for (const option of options.options) {
if (option.collectData) {
console.log(`Option ${option.id} requires info capture`);
}
}
Collect User Data (If Required)
After the user selects an option, check for collectData on it. If present, collect the data before fetching the required actions.
Embedded Data Collection Form
When a payment requires user information (e.g., for Travel Rule compliance), the SDK returns a collectData field on individual payment options. Each option may independently require data collection — some options may require it while others don’t. The form is loaded from selectedOption.collectData.url and embedded in your wallet (a WebView on mobile, an iframe on web). It handles field rendering, validation, Terms & Conditions and Privacy Policy acceptance, and submits data directly to the backend. Collect this data before fetching the required actions.
Recommended Flow
The recommended approach is to display all payment options upfront, then handle data collection only when the user selects an option that requires it:
- Call
getPaymentOptionsand display all available options to the user - Show a visual indicator (e.g., “Info required” badge) on options where
option.collectDatais present - When the user selects an option, check
selectedOption.collectData - If present, load
selectedOption.collectData.urlin the embedded form - Optionally append query parameters to the form URL —
prefill(known user data),theme, andthemeVariables(appearance). - Listen for completion messages:
IC_COMPLETE(success) orIC_ERROR(failure) - On
IC_COMPLETE, continue the flow — fetch the required actions, sign, and confirm the payment.
Form URL parameters
The form URL accepts the following optional query parameters. Append them to selectedOption.collectData.url before loading it, preserving any existing query parameters.
| Parameter | Format | Description |
|---|---|---|
prefill |
base64url-encoded JSON | Pre-populates known user fields so the user doesn’t re-enter them. |
theme |
light or dark |
Sets the form’s base color mode. |
themeVariables |
base64url-encoded JSON | Overrides design tokens to match your brand. |
WebView Message Types
The WebView communicates with your wallet through JavaScript bridge messages. The message payload is a JSON string with the following structure:
| Message Type | Payload | Description |
|---|---|---|
IC_COMPLETE |
{ "type": "IC_COMPLETE", "success": true } |
User completed the form successfully. Proceed to payment confirmation. |
IC_ERROR |
{ "type": "IC_ERROR", "error": "..." } |
An error occurred. Display the error message and allow the user to retry. |
Get Required Actions
After the user selects a payment option, get the wallet RPC actions required to complete the payment:
const actions = await client.getRequiredPaymentActions({
paymentId: options.paymentId,
optionId: options.options[0].id,
});
// Each action contains wallet RPC data to sign
for (const action of actions) {
console.log("Chain:", action.walletRpc.chainId);
console.log("Method:", action.walletRpc.method);
console.log("Params:", action.walletRpc.params);
}
Sign Actions
Sign each action with your wallet’s signing implementation:
const signatures = await Promise.all(
actions.map(async (action) => {
const { chainId, method, params } = action.walletRpc;
const parsedParams = JSON.parse(params);
switch (method) {
case "eth_signTypedData_v4":
return await wallet.signTypedData(chainId, parsedParams);
case "eth_sendTransaction":
return await wallet.sendTransaction(chainId, parsedParams[0]);
case "personal_sign":
return await wallet.personalSign(chainId, parsedParams);
default:
throw new Error(`Unsupported RPC method: ${method}`);
}
})
);
Confirm Payment
Submit the signatures and collected data to complete the payment:
const result = await client.confirmPayment({
paymentId: options.paymentId,
optionId: options.options[0].id,
signatures,
});
if (result.status === "succeeded") {
console.log("Payment successful!");
} else if (result.status === "processing") {
console.log("Payment is processing...");
} else if (result.status === "failed") {
console.log("Payment failed");
}
Complete Example
Here’s a complete implementation example:
import { WalletConnectPay } from "@walletconnect/pay";
class PaymentManager {
private client: WalletConnectPay;
constructor() {
this.client = new WalletConnectPay({
appId: "your-wcp-id",
});
}
async processPayment(paymentLink: string, walletAddress: string) {
try {
const options = await this.client.getPaymentOptions({
paymentLink,
accounts: [
`eip155:1:${walletAddress}`,
`eip155:137:${walletAddress}`,
`eip155:8453:${walletAddress}`,
],
includePaymentInfo: true,
});
if (options.options.length === 0) {
throw new Error("No payment options available");
}
const selectedOption = options.options[0];
// Check for data collection
if (selectedOption.collectData?.url) {
await this.showDataCollectionWebView(selectedOption.collectData.url);
}
const actions = await this.client.getRequiredPaymentActions({
paymentId: options.paymentId,
optionId: selectedOption.id,
});
const signatures = await Promise.all(
actions.map((action) => this.signAction(action, walletAddress))
);
const result = await this.client.confirmPayment({
paymentId: options.paymentId,
optionId: selectedOption.id,
signatures,
});
return result;
} catch (error) {
console.error("Payment failed:", error);
throw error;
}
}
}