Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@
"pages": [
"payments/wallets/walletkit/kotlin",
"payments/wallets/walletkit/swift",
"payments/wallets/walletkit/flutter",
"payments/wallets/walletkit/react-native",
"payments/wallets/walletkit/flutter",
"payments/wallets/walletkit/web"
]
},
Expand Down Expand Up @@ -221,8 +221,8 @@
"pages": [
"payments/wallets/walletkit/kotlin",
"payments/wallets/walletkit/swift",
"payments/wallets/walletkit/flutter",
"payments/wallets/walletkit/react-native",
"payments/wallets/walletkit/flutter",
"payments/wallets/walletkit/web"
]
},
Expand Down
11 changes: 10 additions & 1 deletion payments/wallets/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ Wallets can earn interchange-like revenue on eligible WalletConnect Pay payments
</Accordion>

<Accordion title="What if my wallet already has the required data?">
Wallets that already have verified user PII (e.g. a neobank, a card issuing wallet) can prefill the WebView form by appending a `?prefill=<base64-json>` query parameter to the WebView URL. The `required` list from the `collectDataAction.schema` tells you which fields the form expects (e.g., `fullName`, `dob`, `pobAddress`). The user will still see the form but with pre-populated fields, reducing friction.
Wallets that already have verified user PII (e.g. a neobank, a card issuing wallet) can prefill the WebView form by appending a `?prefill=<base64url-json>` query parameter to the WebView URL. The `required` list from the `collectDataAction.schema` tells you which fields the form expects (e.g., `fullName`, `dob`, `pobAddress`). The user will still see the form but with pre-populated fields, reducing friction.
</Accordion>

<Accordion title="How does this work technically?">
Expand Down Expand Up @@ -198,4 +198,13 @@ Wallets can earn interchange-like revenue on eligible WalletConnect Pay payments
- [WalletConnect Terms and Conditions](https://walletconnect.com/terms)
- [WalletConnect Privacy Policy](https://walletconnect.com/privacy)
</Accordion>

<Accordion title="Can wallets customize the look of the form?">
Yes. The hosted form accepts two optional appearance parameters on its URL:

- `theme=light` or `theme=dark` — sets the form's base color mode. Match it to your wallet's active mode.
- `themeVariables=<base64url>` — overrides design tokens (font, font size, some colors, button border radius, and input border radius). Generate and export this value from the [WalletConnect Pay Dashboard](https://dashboard.walletconnect.com) and append it to the form URL verbatim.

Both are optional and independent — when omitted, the form uses its default styling. See your platform's integration guide for the URL-building code.
</Accordion>
</AccordionGroup>
136 changes: 90 additions & 46 deletions payments/wallets/standalone/flutter.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Add `walletconnect_pay` package to your `pubspec.yaml` or simply run:
flutter pub add walletconnect_pay
```

## Configuration
## Initialization

Initialize the `WalletConnectPay` client with your WCP ID and client ID or API key:

Expand All @@ -55,7 +55,7 @@ try {
}
```

### Configuration Parameters
**Configuration Parameters**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
Expand Down Expand Up @@ -170,47 +170,54 @@ if (response.info != null) {
print('Merchant: ${response.info!.merchant.name}');
}

// Check if data collection is required
if (response.collectData != null) {
print('Data collection required: ${response.collectData!.fields.length} fields');
// Check which options require data collection (per-option)
for (final option in response.options) {
if (option.collectData != null) {
print('Option ${option.id} requires info capture');
}
}
```

</Step>

<Step title="Collect User Data (If Required)" titleSize="h3">

Some payments may require additional user data. After the user selects an option, check for `collectData` in the payment options response and run this step **before** fetching the required payment actions — the backend rejects the actions request with `400 IC data required` for options needing Information Capture if data has not yet been collected:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the backend error info was moved to a shared snippet, still present in the docs

After the user selects an option, check for `collectData` on it. If present, collect the data **before** fetching the required actions.

<WebViewOverview />

```dart
if (response.collectData?.url != null) {
// Use the "required" list from response.collectData.schema to determine which fields to prefill
if (selectedOption.collectData?.url != null) {
// Use the "required" list from selectedOption.collectData.schema to determine which fields to prefill
final prefillData = {
'fullName': 'John Doe',
'dob': '1990-01-15',
'pobAddress': '123 Main St, New York, NY 10001',
};
final prefillJson = jsonEncode(prefillData);
final prefillBase64 = base64Url.encode(utf8.encode(prefillJson));
final uri = Uri.parse(response.collectData!.url);
// Encode prefill as base64url
final prefillBase64 = base64Url.encode(utf8.encode(jsonEncode(prefillData))).replaceAll('=', '');
final uri = Uri.parse(selectedOption.collectData!.url);
final webViewUrl = uri.replace(
queryParameters: {...uri.queryParameters, 'prefill': prefillBase64},
queryParameters: {
...uri.queryParameters,
'prefill': prefillBase64,
// Optional appearance params (see "Form URL parameters"):
'theme': 'dark', // "light" | "dark"
// themeVariables is a base64url string exported from the Pay Dashboard:
// 'themeVariables': themeVariables,
},
).toString();

// Show WebView — see WebView Implementation section below
// Show WebView — see Data Collection Implementation section below
showDataCollectionWebView(webViewUrl);
}
```

<WebViewMessageTypes />

The WebView submits the collected data directly to the backend, so you do **not** pass `collectedData` to `confirmPayment` later.

</Step>

<Step title="Get Required Payment Actions" titleSize="h3">
<Step title="Get Required Actions" titleSize="h3">

Get the required wallet actions (e.g., transactions to sign) for a selected payment option:

Expand All @@ -234,10 +241,41 @@ for (final action in actions) {
}
```

</Step>

<Step title="Sign Actions" titleSize="h3">

Sign each action using your wallet's signing implementation, dispatching on the RPC method:

```dart
// Sign each action based on its RPC method
final signatures = <String>[];
for (final action in actions) {
final rpc = action.walletRpc;
switch (rpc.method) {
case 'eth_signTypedData_v4':
signatures.add(await signTypedData(rpc.chainId, rpc.params));
break;
case 'eth_sendTransaction':
signatures.add(await sendTransaction(rpc.chainId, rpc.params));
break;
case 'personal_sign':
signatures.add(await personalSign(rpc.chainId, rpc.params));
break;
default:
throw UnimplementedError('Unsupported RPC method: ${rpc.method}');
}
}
```

<Note>
Payment options may include multiple actions with different RPC methods. For example, a Permit2 payment where the user lacks sufficient allowance returns two actions: an `eth_sendTransaction` to approve the token allowance, followed by an `eth_signTypedData_v4` to sign the Permit2 transfer. Your wallet must check `action.walletRpc.method` and dispatch to the appropriate handler. For full implementation guidance, see [USDT support](/payments/wallets/token-chain-support/usdt-support).
</Note>

<Warning>
Signatures must be in the same order as the actions array.
</Warning>

</Step>

<Step title="Confirm Payment" titleSize="h3">
Expand Down Expand Up @@ -272,16 +310,18 @@ When using the WebView data-collection approach, you do **not** pass `collectedD

</Steps>

## WebView Implementation
## Data Collection Implementation

When `collectData.url` is present, display the URL in a WebView using the `webview_flutter` package (v4.10.0+). Add it to your `pubspec.yaml`:
When `selectedOption.collectData.url` is present, display the URL in a WebView using the `webview_flutter` package (v4.10.0+). Add it to your `pubspec.yaml`:

```yaml
dependencies:
webview_flutter: ^4.10.0
url_launcher: ^6.1.0
```

<WebViewBestPractices />

```dart
import 'dart:convert';
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -367,14 +407,20 @@ class _PayDataCollectionWebViewState extends State<PayDataCollectionWebView> {
}
}

String buildPrefillUrl(String baseUrl, Map<String, String> prefillData) {
if (prefillData.isEmpty) return baseUrl;
final json = jsonEncode(prefillData);
final base64 = base64Url.encode(utf8.encode(json));
String buildFormUrl(
String baseUrl, {
Map<String, String> prefillData = const {},
String? theme, // "light" or "dark"
String? themeVariables, // base64url string exported from the Pay Dashboard
}) {
final uri = Uri.parse(baseUrl);
return uri.replace(
queryParameters: {...uri.queryParameters, 'prefill': base64},
).toString();
final params = {...uri.queryParameters};
if (prefillData.isNotEmpty) {
params['prefill'] = base64Url.encode(utf8.encode(jsonEncode(prefillData))).replaceAll('=', '');
}
if (theme != null) params['theme'] = theme;
if (themeVariables != null) params['themeVariables'] = themeVariables;
return uri.replace(queryParameters: params).toString();
}
```

Expand Down Expand Up @@ -421,9 +467,9 @@ class PaymentService {
// for options needing Information Capture if data wasn't collected first.
// The WebView submits the data directly to the backend, so it is NOT
// passed to confirmPayment later.
if (optionsResponse.collectData?.url != null) {
if (selectedOption.collectData?.url != null) {
// Show WebView and wait for IC_COMPLETE
await showDataCollectionWebView(optionsResponse.collectData!.url);
await showDataCollectionWebView(selectedOption.collectData!.url);
}

// Step 4: Get required payment actions
Expand Down Expand Up @@ -485,11 +531,11 @@ class PaymentService {

## API Reference

### WalletConnectPay
**WalletConnectPay**

The main class for interacting with the WalletConnect Pay SDK.

#### Constructor
**Constructor**

```dart
WalletConnectPay({
Expand All @@ -500,7 +546,7 @@ WalletConnectPay({
})
```

#### Methods
**Methods**

| Method | Description |
|--------|-------------|
Expand All @@ -509,9 +555,9 @@ WalletConnectPay({
| `Future<List<Action>> getRequiredPaymentActions({required GetRequiredPaymentActionsRequest request})` | Gets the required wallet actions for a selected option (to be called if the selected option does not have actions included) |
| `Future<ConfirmPaymentResponse> confirmPayment({required ConfirmPaymentRequest request})` | Confirms a payment |

## Models
## Data Models

### GetPaymentOptionsRequest
**GetPaymentOptionsRequest**

```dart
GetPaymentOptionsRequest({
Expand All @@ -521,7 +567,7 @@ GetPaymentOptionsRequest({
})
```

### PaymentOptionsResponse
**PaymentOptionsResponse**

```dart
PaymentOptionsResponse({
Expand All @@ -533,7 +579,7 @@ PaymentOptionsResponse({
})
```

### PaymentResultInfo
**PaymentResultInfo**

```dart
class PaymentResultInfo {
Expand All @@ -542,7 +588,7 @@ class PaymentResultInfo {
}
```

### PaymentInfo
**PaymentInfo**

```dart
PaymentInfo({
Expand All @@ -554,7 +600,7 @@ PaymentInfo({
})
```

### PaymentOption
**PaymentOption**

```dart
PaymentOption({
Expand All @@ -563,10 +609,11 @@ PaymentOption({
required PayAmount amount,
@JsonKey(name: 'etaS') required int etaSeconds,
required List<Action> actions,
CollectDataAction? collectData, // Per-option data collection (null if not required)
})
```

### ConfirmPaymentRequest
**ConfirmPaymentRequest**

```dart
ConfirmPaymentRequest({
Expand All @@ -578,7 +625,7 @@ ConfirmPaymentRequest({
})
```

### ConfirmPaymentResponse
**ConfirmPaymentResponse**

```dart
ConfirmPaymentResponse({
Expand All @@ -588,7 +635,7 @@ ConfirmPaymentResponse({
})
```

### PaymentStatus
**PaymentStatus**

```dart
enum PaymentStatus {
Expand All @@ -600,7 +647,7 @@ enum PaymentStatus {
}
```

### Action & WalletRpcAction
**Action & WalletRpcAction**

```dart
class Action {
Expand All @@ -614,7 +661,7 @@ class WalletRpcAction {
}
```

### CollectDataAction
**CollectDataAction**

```dart
class CollectDataAction {
Expand Down Expand Up @@ -651,7 +698,7 @@ All errors include:
- `details`: Additional error details
- `stacktrace`: Stack trace

### Example Error Handling
**Example Error Handling**

```dart
try {
Expand Down Expand Up @@ -691,8 +738,5 @@ try {

8. **User Data**: Only collect data when `collectData` is present in the response and you don't already have the required user data. If you already have the required data, you can submit this without collecting from the user. You must make sure the user accepts WalletConnect Terms and Conditions and Privacy Policy before submitting user information to WalletConnect.

9. **WebView Data Collection**: When `collectData.url` is present, display the URL in a WebView using `webview_flutter` rather than building native forms. The WebView handles form rendering, validation, and T&C acceptance.

## Examples
9. **WebView Data Collection**: When `selectedOption.collectData.url` is present, display the URL in a WebView using `webview_flutter` rather than building native forms. The WebView handles form rendering, validation, and T&C acceptance.

For a complete example implementation, see the [reown_walletkit example](https://github.com/reown-com/reown_flutter/tree/master/packages/reown_walletkit/example/lib/walletconnect_pay).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this was redundant with the wallet sample link at top)

Loading