Loading...
Loading...
Guides wallet developers through integrating WalletConnect Pay SDK into mobile wallets (Kotlin, Swift, React Native, Flutter). Use when adding WC Pay payment acceptance, implementing pay link detection, handling USDC payment flows, or troubleshooting Pay SDK integration issues.
npx skill4agent add walletconnect/skills walletconnect-payeip155:1eip155:8453eip155:10eip155:137eip155:42161| Path | When to use | Complexity |
|---|---|---|
| WalletKit (recommended) | Already using WalletConnect WalletKit | Lowest — Pay initializes automatically |
| Standalone SDK | No WalletKit dependency, want SDK convenience | Medium |
| API-First | Full control, no SDK, direct Gateway calls | Highest |
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)// Kotlin (WalletKit)
if (WalletKit.Pay.isPaymentLink(uri)) {
processPayment(uri)
} else {
WalletKit.pair(Wallet.Params.Pair(uri))
}// Swift (WalletKit)
if WalletKit.isPaymentLink(scannedString) {
startPaymentFlow(paymentLink: scannedString)
}// React Native
import { isPaymentLink } from "@reown/walletkit";
if (isPaymentLink(uri)) { await processPayment(uri); }// Flutter
if (walletKit.isPaymentLink(uri)) { /* process */ }eip155:{chainId}:{address}val result = WalletKit.Pay.getPaymentOptions(
paymentLink = uri,
accounts = listOf("eip155:1:0x...", "eip155:8453:0x...", "eip155:137:0x...")
)paymentIdoptions[]idamountaccountcollectData?infoexpiresAtval actions = WalletKit.Pay.getRequiredPaymentActions(
Wallet.Params.RequiredPaymentActions(paymentId, selectedOption.id)
)WalletRpcActionchainIdmethodeth_signTypedData_v4params// 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)selectedOption.collectData != nullselectedOption.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
}IC_COMPLETEIC_ERRORval result = WalletKit.Pay.confirmPayment(
Wallet.Params.ConfirmPayment(paymentId, selectedOption.id, signatures)
)
// Status: SUCCEEDED | PROCESSING | FAILED | EXPIRED | REQUIRES_ACTIONisFinal == falsepollInMsisPaymentLink()eip155:{chainId}:{address}collectDataexpiresAt| Error | Cause | Fix |
|---|---|---|
| Invalid or deleted payment link | Show "payment not found" to user |
| User took too long | Show "payment expired, ask merchant to retry" |
| Bad CAIP-10 format | Verify |
| KYC/KYT blocked the payment | Show message; do not retry automatically |
| Wrong signing order or method | Ensure signatures match actions array order |
| Init fails | Missing | Check dashboard for App ID |
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 });
}
}// 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)
}collectData@walletconnect/pay