## Sample Wallet

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

[**Sample Wallet - React Native**](https://github.com/reown-com/react-native-examples/tree/main/wallets/rn_cli_wallet)

## Requirements

- React Native 0.70+
- `@walletconnect/react-native-compat` installed 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:

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:

```javascript
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:

```javascript
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;
    }
  }
}
```
