feat(pay): add HTTP provider for browser/Node.js environments#7132
feat(pay): add HTTP provider for browser/Node.js environments#7132ganchoradkov wants to merge 7 commits intov2.0from
Conversation
Add HttpProvider that makes direct HTTP calls to the Pay API, enabling the Pay SDK to work in any JavaScript environment with fetch support (browsers, Node.js 18+, etc.). Changes: - Add HttpProvider implementation with full API support - Add payment ID extraction from various link formats - Add action caching and build action resolution - Add automatic polling for payment status - Update provider detection to use HTTP when native unavailable - Add comprehensive test suite with 30 tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
14babd8 to
2c7572c
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds an HTTP provider that enables the WalletConnect Pay SDK to work in browser and Node.js environments by making direct HTTP calls to the Pay API, removing the dependency on native/WASM modules for web environments.
Changes:
- Added
HttpProviderclass that usesfetchAPI for all payment operations - Updated provider auto-detection to prioritize native → HTTP → WASM
- Extended
PayProviderTypeto include "http" option
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/pay/src/providers/http.ts | New HTTP provider implementation with payment ID extraction, action caching, and automatic polling |
| packages/pay/src/providers/index.ts | Updated provider detection to include HTTP provider as second priority |
| packages/pay/src/types/provider.ts | Added "http" to PayProviderType union |
| packages/pay/test/http-provider.spec.ts | Comprehensive test suite with 30 tests covering all HTTP provider functionality |
| packages/react-native-compat/android/build.gradle | Updated yttrium-wcpay dependency version |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export class HttpProvider implements PayProvider { | ||
| private readonly baseUrl: string; | ||
| private readonly headers: Record<string, string>; | ||
| private cachedOptions: CachedPaymentOption[] = []; |
There was a problem hiding this comment.
The cachedOptions array is shared across multiple payment flows and could lead to incorrect behavior if the same HttpProvider instance is used for multiple different payments simultaneously. Consider keying the cache by paymentId (e.g., Map<string, CachedPaymentOption[]>) to isolate cache entries per payment.
| ); | ||
|
|
||
| // Cache the raw options for use by getRequiredPaymentActions | ||
| this.cachedOptions = response.options.map((o) => ({ |
There was a problem hiding this comment.
The cache is completely replaced on each getPaymentOptions call, which means if a user calls getPaymentOptions for payment A, then for payment B, the cached actions for payment A are lost. This breaks the caching mechanism if multiple payments are handled with the same provider instance. The cache should be keyed by paymentId to avoid this issue.
| this.cachedOptions = response.options.map((o) => ({ | |
| this.cachedOptions[paymentId] = response.options.map((o) => ({ |
| while (!result.isFinal) { | ||
| const delay = result.pollInMs ?? 1000; | ||
| await new Promise((resolve) => setTimeout(resolve, delay)); |
There was a problem hiding this comment.
The infinite polling loop has no timeout or maximum retry limit, which could cause the function to hang indefinitely if the server never returns isFinal: true. Consider adding a maximum retry count or total timeout to prevent infinite loops.
| const cached = this.cachedOptions.find((o) => o.optionId === params.optionId); | ||
|
|
||
| let rawActions: RawAction[]; | ||
| if (cached && cached.actions.length > 0) { |
There was a problem hiding this comment.
The condition cached.actions.length > 0 excludes valid cached options that have zero actions. If an option legitimately has no actions, it should still be served from cache rather than making an unnecessary API call. Consider removing the length check or explicitly checking for null/undefined.
| if (cached && cached.actions.length > 0) { | |
| if (cached && Array.isArray(cached.actions)) { |
| throw new PayError( | ||
| "UNKNOWN", |
There was a problem hiding this comment.
All HTTP errors are categorized as 'UNKNOWN' error code. Consider mapping HTTP error responses to more specific error codes (e.g., 404 → PAYMENT_OPTIONS, 422 → CONFIRM_PAYMENT) to provide more meaningful error information to SDK consumers.
| throw new PayError( | |
| "UNKNOWN", | |
| // Map HTTP status codes to more specific PayError codes where possible | |
| let errorCode: string; | |
| switch (response.status) { | |
| case 404: | |
| errorCode = "PAYMENT_OPTIONS"; | |
| break; | |
| case 422: | |
| errorCode = "CONFIRM_PAYMENT"; | |
| break; | |
| default: | |
| errorCode = "UNKNOWN"; | |
| break; | |
| } | |
| throw new PayError( | |
| errorCode, |
| expect(mockFetch).toHaveBeenCalledWith( | ||
| expect.stringContaining("/v1/gateway/payment/pay_headers/options"), | ||
| expect.objectContaining({ | ||
| method: "POST", | ||
| headers: expect.objectContaining({ | ||
| "Content-Type": "application/json", | ||
| "Api-Key": "test-api-key", | ||
| "Sdk-Name": "test-sdk", | ||
| "Sdk-Version": "1.0.0", | ||
| "Sdk-Platform": "test", | ||
| }), | ||
| }), | ||
| ); |
There was a problem hiding this comment.
The test validates that headers are sent but doesn't verify that the bundleId from the config is included in any header or request. If bundleId should be sent (which is part of PayProviderConfig), this should be tested, or if it's intentionally not used by HttpProvider, this should be documented.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
- Update SDK_VERSION to 1.0.2 to match package.json - Add missing account field to RawPaymentOption and transformPaymentOption - Support both apiKey and appId for authentication headers - Fix linting issues in test file
Extract nested functions to class methods, add type guards for action types, and create helper methods for transformations. Improves code organization without changing functionality. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary
HttpProviderthat makes direct HTTP calls to the Pay API, enabling the SDK to work in any JavaScript environment withfetchsupport (browsers, Node.js 18+, etc.)Changes
New
HttpProvider(src/providers/http.ts)getPaymentOptionsfor efficientgetRequiredPaymentActionscalls/fetchendpointconfirmPaymentUpdated provider detection (
src/providers/index.ts)fetchis available and native module isn'tUpdated types (
src/types/provider.ts)"http"toPayProviderTypeTest plan
test/http-provider.spec.ts)Usage
🚧 Work in Progress - Ready for initial review
🤖 Generated with Claude Code
Note
Enables Pay SDK to run in any JS environment with
fetchby adding a direct-API HTTP provider and integrating it into provider auto-detection.HttpProvider(src/providers/http.ts): direct Pay API calls, payment ID parsing from multiple link formats (URLs,wc:URIs, query params), caching of option actions, resolvingbuildactions via/fetch, and polling inconfirmPaymentsrc/providers/index.ts): prefernative, thenhttpwhenfetchis available; exports HTTP provider; updated error messagesrc/types/provider.ts): adds"http"toPayProviderTypesrc/constants/client.ts): bumpSDK_VERSIONto1.0.2test/http-provider.spec.ts): comprehensive suite covering headers, parsing, action resolution, polling, and errorsWritten by Cursor Bugbot for commit ad3db68. This will update automatically on new commits. Configure here.