WalletConnect Pay SDK - React Native - WalletConnect Pay Docs

Sample Wallet

For a complete working example, check out our sample wallet implementation:

Sample Wallet - React Native

Requirements

Installation

Install the WalletConnect Pay SDK using npm or yarn:

npm install @walletconnect/pay
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:

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:

  1. Call getPaymentOptions and display all available options to the user
  2. Show a visual indicator (e.g., “Info required” badge) on options where option.collectData is present
  3. When the user selects an option, check selectedOption.collectData
  4. If present, load selectedOption.collectData.url in the embedded form
  5. Optionally append query parameters to the form URL — prefill (known user data), theme, and themeVariables (appearance).
  6. Listen for completion messages: IC_COMPLETE (success) or IC_ERROR (failure)
  7. 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;
    }
  }
}