walletconnect-pay

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

WalletConnect Pay — Wallet Integration

WalletConnect Pay — 钱包集成指南

Goal

目标

Help wallet developers integrate WalletConnect Pay so their users can pay at any compatible POS terminal or online checkout using USDC. The integration enables wallets to detect payment links, fetch payment options, collect signatures, and confirm transactions.
帮助钱包开发者集成WalletConnect Pay,使其用户能够使用USDC在任何兼容的POS终端或在线结账页面完成支付。集成后,钱包将具备检测支付链接、获取支付选项、收集签名及确认交易的能力。

When to use

适用场景

  • Adding WalletConnect Pay support to a mobile wallet
  • Implementing payment link (QR code) detection and handling
  • Debugging WC Pay SDK initialization or API errors
  • Choosing between WalletKit, Standalone SDK, or API-First approaches
  • Implementing data collection (KYC/KYT) WebView flow
  • Setting up CAIP-10 account formatting for multi-chain payment options
  • 为移动钱包添加WalletConnect Pay支持
  • 实现支付链接(二维码)的检测与处理
  • 调试WC Pay SDK初始化或API错误
  • 在WalletKit、独立SDK或API优先方案中做选择
  • 实现数据收集(KYC/KYT)WebView流程
  • 为多链支付选项设置CAIP-10账户格式

When not to use

不适用场景

  • Building a merchant POS terminal (use the POS SDK instead — separate product)
  • Building an e-commerce checkout (coming soon, different SDK)
  • General WalletConnect pairing/session management unrelated to payments
  • 搭建商家POS终端(请使用POS SDK——独立产品)
  • 搭建电商结账系统(即将推出,使用不同SDK)
  • 与支付无关的通用WalletConnect配对/会话管理

Supported Platforms & Assets

支持的平台与资产

Frameworks: Kotlin (Android), Swift (iOS), React Native, Flutter Assets: USDC only (currently) Networks: Ethereum (
eip155:1
), Base (
eip155:8453
), Optimism (
eip155:10
), Polygon (
eip155:137
), Arbitrum (
eip155:42161
)
开发框架: Kotlin(Android)、Swift(iOS)、React Native、Flutter 支持资产: 目前仅支持USDC 支持网络: Ethereum (
eip155:1
)、Base (
eip155:8453
)、Optimism (
eip155:10
)、Polygon (
eip155:137
)、Arbitrum (
eip155:42161
)

Choose Your Integration Path

选择集成方案

PathWhen to useComplexity
WalletKit (recommended)Already using WalletConnect WalletKitLowest — Pay initializes automatically
Standalone SDKNo WalletKit dependency, want SDK convenienceMedium
API-FirstFull control, no SDK, direct Gateway callsHighest
Jump to the right reference:
  • Kotlin (WalletKit)
  • Swift (WalletKit)
  • React Native (WalletKit)
  • Flutter (WalletKit)
  • Kotlin Standalone SDK
  • Swift Standalone SDK
  • React Native Standalone SDK
  • Flutter Standalone SDK
  • API-First (all platforms)
方案适用场景复杂度
WalletKit(推荐)已在使用WalletConnect WalletKit最低——Pay将自动初始化
独立SDK无WalletKit依赖,希望使用SDK便捷能力中等
API优先需要完全控制权,不使用SDK,直接调用网关最高
跳转至对应参考文档:
  • Kotlin (WalletKit)
  • Swift (WalletKit)
  • React Native (WalletKit)
  • Flutter (WalletKit)
  • Kotlin 独立SDK
  • Swift 独立SDK
  • React Native 独立SDK
  • Flutter 独立SDK
  • API优先(全平台)

Prerequisites

前置条件

  1. Project ID — create a project at dashboard.walletconnect.com
  2. App ID — from the same dashboard; required for Pay initialization
  3. Android min SDK 23 / iOS 13+ / Node 16+ / Flutter 3+
  4. For Standalone: contact WalletConnect team to obtain an API key
  1. 项目ID — 在dashboard.walletconnect.com创建项目获取
  2. App ID — 从同一控制台获取;Pay初始化必需
  3. Android最低SDK版本23 / iOS 13+ / Node 16+ / Flutter 3+
  4. 若使用独立SDK:联系WalletConnect团队获取API密钥

Universal Payment Flow (all platforms)

通用支付流程(全平台)

Every integration follows these 6 steps:
QR Scan / Deep Link
1. isPaymentLink(uri)       → branch: Pay vs. standard WC pairing
2. getPaymentOptions(link, accounts)  → list of options + merchant info
3. User selects option
4. getRequiredPaymentActions(paymentId, optionId)  → signing actions
5a. Sign actions (EIP-712 typed data, preserve order)
5b. [If collectData present] Show WebView → await IC_COMPLETE
6. confirmPayment(paymentId, optionId, signatures)
所有集成均遵循以下6个步骤:
二维码扫描 / 深度链接
1. isPaymentLink(uri)       → 分支:Pay 或 标准WC配对
2. getPaymentOptions(link, accounts)  → 支付选项列表 + 商家信息
3. 用户选择支付选项
4. getRequiredPaymentActions(paymentId, optionId)  → 签名操作
5a. 签名操作(EIP-712类型数据,需保持顺序)
5b. [若存在collectData] 显示WebView → 等待IC_COMPLETE
6. confirmPayment(paymentId, optionId, signatures)

Step 1 — Detect payment links

步骤1 — 检测支付链接

Always check before routing to standard WalletConnect pairing.
kotlin
// Kotlin (WalletKit)
if (WalletKit.Pay.isPaymentLink(uri)) {
    processPayment(uri)
} else {
    WalletKit.pair(Wallet.Params.Pair(uri))
}
swift
// Swift (WalletKit)
if WalletKit.isPaymentLink(scannedString) {
    startPaymentFlow(paymentLink: scannedString)
}
js
// React Native
import { isPaymentLink } from "@reown/walletkit";
if (isPaymentLink(uri)) { await processPayment(uri); }
dart
// Flutter
if (walletKit.isPaymentLink(uri)) { /* process */ }
在路由至标准WalletConnect配对前,务必先进行检测。
kotlin
// Kotlin (WalletKit)
if (WalletKit.Pay.isPaymentLink(uri)) {
    processPayment(uri)
} else {
    WalletKit.pair(Wallet.Params.Pair(uri))
}
swift
// Swift (WalletKit)
if WalletKit.isPaymentLink(scannedString) {
    startPaymentFlow(paymentLink: scannedString)
}
js
// React Native
import { isPaymentLink } from "@reown/walletkit";
if (isPaymentLink(uri)) { await processPayment(uri); }
dart
// Flutter
if (walletKit.isPaymentLink(uri)) { /* 处理支付 */ }

Step 2 — Get payment options

步骤2 — 获取支付选项

Accounts must use CAIP-10 format:
eip155:{chainId}:{address}
Provide accounts on all supported chains to maximize option availability.
kotlin
val result = WalletKit.Pay.getPaymentOptions(
    paymentLink = uri,
    accounts = listOf("eip155:1:0x...", "eip155:8453:0x...", "eip155:137:0x...")
)
Response contains:
  • paymentId
    — use in all subsequent calls
  • options[]
    — each has
    id
    ,
    amount
    ,
    account
    ,
    collectData?
  • info
    — merchant name, amount, expiry (
    expiresAt
    )
账户必须使用CAIP-10格式:
eip155:{chainId}:{address}
提供所有支持链上的账户以获取最多支付选项。
kotlin
val result = WalletKit.Pay.getPaymentOptions(
    paymentLink = uri,
    accounts = listOf("eip155:1:0x...", "eip155:8453:0x...", "eip155:137:0x...")
)
返回结果包含:
  • paymentId
    — 后续所有调用均需使用
  • options[]
    — 每个选项包含
    id
    amount
    account
    collectData?
  • info
    — 商家名称、支付金额、过期时间(
    expiresAt

Step 3 — Get required signing actions

步骤3 — 获取所需签名操作

kotlin
val actions = WalletKit.Pay.getRequiredPaymentActions(
    Wallet.Params.RequiredPaymentActions(paymentId, selectedOption.id)
)
Each action is a
WalletRpcAction
with
chainId
,
method
(
eth_signTypedData_v4
), and
params
.
kotlin
val actions = WalletKit.Pay.getRequiredPaymentActions(
    Wallet.Params.RequiredPaymentActions(paymentId, selectedOption.id)
)
每个操作均为
WalletRpcAction
,包含
chainId
method
eth_signTypedData_v4
)及
params

Step 4 — Sign actions

步骤4 — 执行签名操作

Sign all actions. Order must match the actions array exactly.
kotlin
// Sign EIP-712 typed data — Kotlin example
val paramsArray = JSONArray(rpc.params)
val typedData = paramsArray.getString(1)
val encoder = StructuredDataEncoder(typedData)
val hash = encoder.hashStructuredData()
val signature = Sign.signMessage(hash, keyPair, false)
需对所有操作进行签名。顺序必须与操作数组完全一致
kotlin
// 签名EIP-712类型数据 — Kotlin示例
val paramsArray = JSONArray(rpc.params)
val typedData = paramsArray.getString(1)
val encoder = StructuredDataEncoder(typedData)
val hash = encoder.hashStructuredData()
val signature = Sign.signMessage(hash, keyPair, false)

Step 5 — Data collection (conditional)

步骤5 — 数据收集(可选)

Only show WebView when
selectedOption.collectData != null
. Never build native forms — the WebView handles compliance, validation, and T&C.
kotlin
selectedOption.collectData?.let { collectAction ->
    // Optional: prefill known user data
    val prefillBase64 = Base64.encode(JSONObject(userData).toString().toByteArray())
    val url = Uri.parse(collectAction.url)
        .buildUpon()
        .appendQueryParameter("prefill", prefillBase64)
        .build().toString()
    showWebView(url) // Listen for IC_COMPLETE / IC_ERROR
}
WebView JavaScript bridge messages:
  • IC_COMPLETE
    → proceed to confirm
  • IC_ERROR
    → show error, allow retry
仅当
selectedOption.collectData != null
时才显示WebView。 禁止构建原生表单 — WebView将处理合规性、验证及条款与条件。
kotlin
selectedOption.collectData?.let { collectAction ->
    // 可选:预填充已知用户数据
    val prefillBase64 = Base64.encode(JSONObject(userData).toString().toByteArray())
    val url = Uri.parse(collectAction.url)
        .buildUpon()
        .appendQueryParameter("prefill", prefillBase64)
        .build().toString()
    showWebView(url) // 监听IC_COMPLETE / IC_ERROR事件
}
WebView JavaScript桥接消息:
  • IC_COMPLETE
    → 继续执行确认支付
  • IC_ERROR
    → 显示错误信息,允许重试

Step 6 — Confirm payment

步骤6 — 确认支付

kotlin
val result = WalletKit.Pay.confirmPayment(
    Wallet.Params.ConfirmPayment(paymentId, selectedOption.id, signatures)
)
// Status: SUCCEEDED | PROCESSING | FAILED | EXPIRED | REQUIRES_ACTION
If
isFinal == false
and
pollInMs
is present, poll again after the delay.
kotlin
val result = WalletKit.Pay.confirmPayment(
    Wallet.Params.ConfirmPayment(paymentId, selectedOption.id, signatures)
)
// 状态:SUCCEEDED | PROCESSING | FAILED | EXPIRED | REQUIRES_ACTION
isFinal == false
且存在
pollInMs
,则需在延迟后再次轮询。

Validation checklist

验证检查清单

  • isPaymentLink()
    called before any WalletConnect pairing attempt
  • All accounts formatted as CAIP-10 (
    eip155:{chainId}:{address}
    )
  • Accounts provided for all 5 supported networks
  • Signatures array order matches actions array order
  • collectData
    checked per-option, not globally
  • WebView used for data collection (no native form built)
  • expiresAt
    monitored; user warned before expiry
  • Loading states shown during API calls and signing
  • Users shown WalletConnect Terms & Privacy Policy before data submission
  • All 5 payment statuses handled in UI
  • 在任何WalletConnect配对尝试前调用
    isPaymentLink()
  • 所有账户均采用CAIP-10格式(
    eip155:{chainId}:{address}
  • 提供所有5个支持网络的账户
  • 签名数组顺序与操作数组顺序完全匹配
  • 按单个选项检查
    collectData
    ,而非全局检查
  • 使用WebView进行数据收集(不构建原生表单)
  • 监控
    expiresAt
    ;在过期前提醒用户
  • 在API调用及签名过程中显示加载状态
  • 在提交数据前向用户展示WalletConnect条款与隐私政策
  • 在UI中处理所有5种支付状态

Common errors

常见错误

ErrorCauseFix
PaymentNotFound
Invalid or deleted payment linkShow "payment not found" to user
PaymentExpired
User took too longShow "payment expired, ask merchant to retry"
InvalidAccount
Bad CAIP-10 formatVerify
eip155:{chainId}:{address}
format
ComplianceFailed
KYC/KYT blocked the paymentShow message; do not retry automatically
InvalidSignature
Wrong signing order or methodEnsure signatures match actions array order
Init failsMissing
appId
or
packageName
Check dashboard for App ID
错误原因修复方案
PaymentNotFound
支付链接无效或已被删除向用户显示“未找到该支付”
PaymentExpired
用户操作超时向用户显示“支付已过期,请联系商家重试”
InvalidAccount
CAIP-10格式错误验证格式是否为
eip155:{chainId}:{address}
ComplianceFailed
KYC/KYT审核未通过显示提示信息;请勿自动重试
InvalidSignature
签名顺序或方法错误确保签名顺序与操作数组完全一致
初始化失败缺少
appId
packageName
检查控制台获取正确的App ID

Examples

示例

Example 1 — QR scan handler (React Native)

示例1 — 二维码扫描处理器(React Native)

js
async function handleScan(uri) {
  if (isPaymentLink(uri)) {
    const options = await walletkit.pay.getPaymentOptions({
      paymentLink: uri,
      accounts: ["eip155:1:0xABC", "eip155:8453:0xABC"],
    });
    const option = options.options[0];
    const actions = await walletkit.pay.getRequiredPaymentActions({
      paymentId: options.paymentId,
      optionId: option.id,
    });
    const signatures = await Promise.all(
      actions.map(a => wallet.signTypedData(a.walletRpc.chainId, a.walletRpc.params))
    );
    const result = await walletkit.pay.confirmPayment({
      paymentId: options.paymentId,
      optionId: option.id,
      signatures,
    });
    showStatus(result.status); // "succeeded" | "processing" | etc.
  } else {
    await walletkit.pair({ uri });
  }
}
js
async function handleScan(uri) {
  if (isPaymentLink(uri)) {
    const options = await walletkit.pay.getPaymentOptions({
      paymentLink: uri,
      accounts: ["eip155:1:0xABC", "eip155:8453:0xABC"],
    });
    const option = options.options[0];
    const actions = await walletkit.pay.getRequiredPaymentActions({
      paymentId: options.paymentId,
      optionId: option.id,
    });
    const signatures = await Promise.all(
      actions.map(a => wallet.signTypedData(a.walletRpc.chainId, a.walletRpc.params))
    );
    const result = await walletkit.pay.confirmPayment({
      paymentId: options.paymentId,
      optionId: option.id,
      signatures,
    });
    showStatus(result.status); // "succeeded" | "processing" | etc.
  } else {
    await walletkit.pair({ uri });
  }
}

Example 2 — Data collection gating (Swift)

示例2 — 数据收集触发逻辑(Swift)

swift
// After getting actions and signing them:
if let collectData = selectedOption.collectData {
    // Must show WebView before confirmPayment
    let url = buildPrefillURL(base: collectData.url, userData: knownUserData)
    showWebView(url) { [weak self] in
        // IC_COMPLETE callback
        self?.confirmPayment(paymentId, selectedOption.id, signatures)
    }
} else {
    // No data collection needed → confirm directly
    confirmPayment(paymentId, selectedOption.id, signatures)
}
swift
// 获取操作并签名后:
if let collectData = selectedOption.collectData {
    // 必须在confirmPayment前显示WebView
    let url = buildPrefillURL(base: collectData.url, userData: knownUserData)
    showWebView(url) { [weak self] in
        // IC_COMPLETE回调
        self?.confirmPayment(paymentId, selectedOption.id, signatures)
    }
} else {
    // 无需数据收集 → 直接确认支付
    confirmPayment(paymentId, selectedOption.id, signatures)
}

Evaluations

场景评估

  1. Activation — "I'm building an Android wallet and want to support WalletConnect Pay. Walk me through the Kotlin WalletKit integration."
  2. Non-activation — "How do I set up WalletConnect session management for my dApp?" (standard WC, not Pay)
  3. Edge case — "My wallet only has Ethereum accounts. Will WC Pay still work?" (Answer: yes, but fewer options; recommend adding multi-chain accounts)
  4. Edge case — "When should I show the WebView for data collection?" (Answer: only when
    collectData
    is non-null on the selected option)
  5. Framework choice — "I don't use WalletKit. How do I integrate WC Pay on Flutter?" (Answer: use Flutter Standalone SDK)
  6. Standalone Swift — "I'm building an iOS wallet without WalletKit. How do I add WC Pay?" (Answer: use Swift Standalone SDK with SPM)
  7. Standalone React Native — "How do I add WC Pay to my React Native wallet without WalletKit?" (Answer: use
    @walletconnect/pay
    standalone package)
  1. 激活场景 — “我正在开发Android钱包,想了解如何通过Kotlin WalletKit集成WalletConnect Pay。”
  2. 非激活场景 — “如何为我的dApp设置WalletConnect会话管理?”(标准WC功能,非Pay)
  3. 边缘场景 — “我的钱包仅支持Ethereum账户,WC Pay还能正常工作吗?”(答案:可以,但支付选项会减少;建议添加多链账户)
  4. 边缘场景 — “我应该在什么时候显示数据收集的WebView?”(答案:仅当所选选项的
    collectData
    不为空时)
  5. 框架选择 — “我不使用WalletKit,如何在Flutter中集成WC Pay?”(答案:使用Flutter独立SDK)
  6. Swift独立SDK场景 — “我正在开发不使用WalletKit的iOS钱包,如何添加WC Pay?”(答案:通过SPM使用Swift独立SDK)
  7. React Native独立SDK场景 — “如何在不使用WalletKit的React Native钱包中添加WC Pay?”(答案:使用
    @walletconnect/pay
    独立包)