feat(pay): add WebView for hosted collect data form#367
feat(pay): add WebView for hosted collect data form#367ganchoradkov wants to merge 11 commits intomainfrom
Conversation
- Add CollectDataWebView component for WalletConnect Pay hosted form - Use fullscreen WebView when collectData.url is available - Handle IC_COMPLETE/IC_ERROR postMessage events from WebView - X button in WebView navigates to confirm step (not closes modal) - Add proper cleanup with isMountedRef to prevent memory leaks - Add react-native-webview dependency
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Claude finished @ganchoradkov's task in 1m 7s —— View job Code ReviewFound 3 issue(s)Issue 1: WebView security - missing origin whitelistID: collectdatawebview-origin-whitelist-a4f2 Context:
Recommendation: Add origin whitelist to restrict allowed domains: <WebView
ref={webViewRef}
source={{ uri: url }}
originWhitelist={['https://pay.walletconnect.com', 'https://pay.reown.com']}
// ... rest of props
/>Issue 2: postMessage validation insufficientID: collectdatawebview-message-validation-7bc3 Context:
Recommendation: Validate message origin before processing: const handleMessage = useCallback((event: { nativeEvent: { data: string; url: string } }) => {
if (!isMountedRef.current) return;
const messageOrigin = new URL(event.nativeEvent.url).origin;
const allowedOrigins = ['https://pay.walletconnect.com', 'https://pay.reown.com'];
if (!allowedOrigins.includes(messageOrigin)) {
LogStore.warn('Blocked postMessage from untrusted origin', 'CollectDataWebView', 'handleMessage', { origin: messageOrigin });
return;
}
// ... rest of handler
}, [onComplete, onError]);Issue 3: Type safety - unsafe type castingID: index-collectdata-type-cast-9e1a Context:
Recommendation: Extend type properly or add runtime check: // Option 1: Type extension (preferred)
interface CollectDataWithUrl extends CollectData {
url?: string;
}
const collectDataUrl = (paymentData?.collectData as CollectDataWithUrl | undefined)?.url;
// Option 2: Add comment explaining temporary nature
const collectDataUrl = (
// TEMP: url not yet in @walletconnect/pay types v1.x
paymentData?.collectData as { url?: string } | undefined
)?.url;SummaryFunctional implementation with good error handling and UX flow. Critical security concern: WebView lacks origin restrictions allowing arbitrary URLs. Add |
wallets/rn_cli_wallet/src/modals/PaymentOptionsModal/CollectDataWebView.tsx
Show resolved
Hide resolved
wallets/rn_cli_wallet/src/modals/PaymentOptionsModal/CollectDataWebView.tsx
Show resolved
Hide resolved
| const hasCollectData = | ||
| paymentData?.collectData && paymentData.collectData.fields.length > 0; | ||
| // The `url` property is a new addition to the API - cast to access it | ||
| const collectDataUrl = ( |
There was a problem hiding this comment.
🤖 Auto Review Issue: Type safety - unsafe type casting
Severity: LOW
Category: code_quality
Tool: Claude Auto Review
Context:
- Pattern: Type cast to access undocumented
urlproperty:(paymentData?.collectData as { url?: string } | undefined)?.url - Risk: Runtime errors if API shape changes, no compile-time safety
- Impact: Silent failures, hard-to-debug issues when property added to types
- Trigger: When
@walletconnect/paypackage addsurlto CollectData type
Recommendation: Extend type properly or add runtime check:
// Option 1: Type extension (preferred)
interface CollectDataWithUrl extends CollectData {
url?: string;
}
const collectDataUrl = (paymentData?.collectData as CollectDataWithUrl | undefined)?.url;
// Option 2: Add comment explaining temporary nature
const collectDataUrl = (
// TEMP: url not yet in @walletconnect/pay types v1.x
paymentData?.collectData as { url?: string } | undefined
)?.url;Summary
Functional implementation with good error handling and UX flow. Critical security concern: WebView lacks origin restrictions allowing arbitrary URLs. Add originWhitelist and message origin validation before merging.
There was a problem hiding this comment.
Addressed. Added a TODO comment to update the type when @walletconnect/pay adds url to the CollectData type. The current cast is temporary until the SDK types are updated.
- Add getCurrencySymbol() utility to convert currency codes to symbols - Support 35+ currencies (USD, EUR, GBP, JPY, etc.) - Add prefill parameter to WebView URL with base64-encoded user data - Skip collectedData validation when data was collected via WebView - Use dynamic currency symbols in MerchantInfo and ConfirmPaymentView Co-authored-by: Cursor <cursoragent@cursor.com>
|
@ganchoradkov can you also check how it behaves if you press the terms and conditions link from the webview checkbox? I think we could make the app open the native browser in that case so we dont break the flow |
…10.30 Add git source to Podfile to resolve CDN propagation delay for new pod versions. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@ganchoradkov also check how the soft keyboard behaves when the user presses the inputs inside the webview 🙏 |
Updates YttriumWrapper to 0.10.31. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
This PR adds WebView support for WalletConnect Pay's hosted data collection forms, providing a more seamless UX when the payment API returns a collectData.url. The implementation allows users to complete payment data collection in a fullscreen WebView instead of native forms.
Changes:
- Adds
CollectDataWebViewcomponent that renders hosted forms in a fullscreen WebView with postMessage event handling - Introduces new
collectDataWebViewstep in payment flow whencollectData.urlis available - Updates currency display formatting with
getCurrencySymbolutility function for internationalization - Upgrades
@walletconnect/react-native-compatpackage to version 2.23.4-canary.2
Reviewed changes
Copilot reviewed 10 out of 12 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| package.json | Adds react-native-webview dependency and updates @walletconnect/react-native-compat to 2.23.4-canary.2 |
| yarn.lock | Updates lock file with new dependency versions and checksums |
| ios/Podfile | Adds CocoaPods Specs source repository |
| ios/Podfile.lock | Updates iOS native dependencies including react-native-webview and YttriumWrapper |
| utils.ts | Adds getCurrencySymbol utility function with mapping for 40+ currency codes |
| reducer.ts | Adds 'collectDataWebView' step to payment flow state machine |
| index.tsx | Integrates WebView flow with conditional rendering based on collectData.url presence, adds completion/error handlers |
| ViewWrapper.tsx | Updates UI to support fullscreen WebView step with modified header button behavior |
| MerchantInfo.tsx | Uses getCurrencySymbol for dynamic currency symbol display instead of hardcoded $ |
| ConfirmPaymentView.tsx | Uses getCurrencySymbol for dynamic currency symbol display |
| CollectDataWebView.tsx | New component implementing WebView with prefill data, postMessage handling, and URL-based navigation detection |
| usePairing.ts | Adds console.log for debugging payment options response |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
wallets/rn_cli_wallet/src/modals/PaymentOptionsModal/CollectDataWebView.tsx
Show resolved
Hide resolved
wallets/rn_cli_wallet/src/modals/PaymentOptionsModal/CollectDataWebView.tsx
Show resolved
Hide resolved
| // Closing WebView means form was completed, move to confirm step | ||
| dispatch({ type: 'SET_STEP', payload: 'confirm' }); |
There was a problem hiding this comment.
The comment states "Closing WebView means form was completed, move to confirm step", but clicking the back button (X) in the WebView should typically allow the user to go back to the previous step, not assume completion. According to the PR description, "X button in WebView navigates to confirm step (not closes entire modal)" which suggests this is intentional. However, this conflates two different user actions: (1) successfully completing the form (IC_COMPLETE postMessage) and (2) wanting to go back/cancel (pressing X). Consider navigating back to 'intro' step when X is pressed instead of assuming the form was completed, and only proceed to 'confirm' when IC_COMPLETE message is received.
| // Closing WebView means form was completed, move to confirm step | |
| dispatch({ type: 'SET_STEP', payload: 'confirm' }); | |
| // Closing WebView (X/back) should behave like cancel/back: return to intro | |
| dispatch({ type: 'SET_STEP', payload: 'intro' }); |
There was a problem hiding this comment.
This is by design. The X/back button on the WebView navigates to the confirm step so the user can continue the payment flow without rescanning the payment link. The IC_COMPLETE postMessage also transitions to confirm. Going back to intro would force the user to redo the entire flow.
wallets/rn_cli_wallet/src/modals/PaymentOptionsModal/CollectDataWebView.tsx
Outdated
Show resolved
Hide resolved
wallets/rn_cli_wallet/src/modals/PaymentOptionsModal/CollectDataWebView.tsx
Show resolved
Hide resolved
Prevents navigation issues by opening links from different domains in the system browser while keeping same-domain navigation in WebView. Co-authored-by: Cursor <cursoragent@cursor.com>
wallets/rn_cli_wallet/src/modals/PaymentOptionsModal/CollectDataWebView.tsx
Outdated
Show resolved
Hide resolved
…move checkbox - Wrap WebView in SafeAreaView to prevent status bar overlap - Replace ActivityIndicator with WalletConnectLoading component - Remove native checkbox from IntroView (handled in WebView) - Reduce padding in WebView container for better layout Co-authored-by: Cursor <cursoragent@cursor.com>
|
Addressed the feedback in commit eddd779:
|
SafeAreaView wasn't working correctly with react-native-modal's statusBarTranslucent. Using explicit paddingTop from insets instead. Co-authored-by: Cursor <cursoragent@cursor.com>
- Simplify flow: show intro → WebView when collectData.url exists, otherwise go directly to confirm step - Add origin whitelist for WebView (dev/staging/prod WalletConnect Pay) - Remove step pills from payment modal - Replace console.log with LogStore.log in usePairing - Add comment to CollectDataView noting it's for custom UI implementations - Fix about:blank URL handling in WebView - Update back button logic to only show when there's a previous step - Rely on IC_COMPLETE postMessage for WebView completion detection Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Resolve merge conflicts in package.json and yarn.lock. Keep @walletconnect/react-native-compat at 2.23.4-canary.2 and add @zoontek/react-native-navigation-bar from main. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes build error caused by duplicate `import LogStore` in usePairing.ts. Applies prettier formatting to affected files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>


Summary
CollectDataWebViewcomponent that displays WalletConnect Pay's hosted form in a fullscreen WebViewcollectData.urlis present in payment options, use WebView instead of native formIC_COMPLETE/IC_ERRORpostMessage events from the WebViewChanges
flowchart TD A[Payment Link Received] --> B[Show Intro] B --> C{collectData.url exists?} C -->|Yes| D[Open WebView - Fullscreen] C -->|No| E[Show Native Form] D --> F{IC_COMPLETE message?} F -->|Yes| G[Move to Confirm Step] F -->|No - X pressed| G E --> G G --> H[Select Payment Option] H --> I[Sign & Confirm Payment]Test Plan