-
Notifications
You must be signed in to change notification settings - Fork 3
feat: populate USD payment currencies from the API #104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughReplaced a hardcoded USD payment-currency select with a new TRPC-backed Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant InvoiceForm
participant PaymentCurrencySelector
participant TRPC_Client
participant currencyRouter
participant ExternalAPI
User->>InvoiceForm: open form
InvoiceForm->>PaymentCurrencySelector: render(targetCurrency="USD", network="sepolia")
PaymentCurrencySelector->>TRPC_Client: getConversionCurrencies(targetCurrency, network)
TRPC_Client->>currencyRouter: RPC call
currencyRouter->>ExternalAPI: GET /currencies/{id}/conversion-paths
ExternalAPI-->>currencyRouter: conversion routes
currencyRouter-->>TRPC_Client: routes
TRPC_Client-->>PaymentCurrencySelector: routes
PaymentCurrencySelector-->>User: render dropdown/options
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Assessment against linked issues
Out-of-scope changes(No out-of-scope functional changes detected in this diff.) Possibly related PRs
Suggested reviewers
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (5)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
40ed9a1
to
4c1ace2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/server/routers/currency.ts (1)
24-27
: Consider adding input validation for network parameterThe network parameter accepts any string, but the API likely expects specific network values like "sepolia", "mainnet", etc.
.input( z.object({ targetCurrency: z.string(), - network: z.string(), + network: z.enum(["sepolia", "mainnet", "polygon"]), // Add supported networks }), )src/components/invoice-form/blocks/payment-currency-selector.tsx (1)
73-73
: Handle empty conversion routes gracefullyConsider adding a check for empty conversion routes to provide better user feedback when no currencies are available.
const conversionRoutes = conversionData?.conversionRoutes || []; +if (conversionRoutes.length === 0) { + return ( + <div className="space-y-2"> + <Label htmlFor="paymentCurrency">Payment Currency</Label> + <Select disabled> + <SelectTrigger> + <SelectValue placeholder="No payment currencies available" /> + </SelectTrigger> + </Select> + <p className="text-sm text-gray-500"> + No payment currencies found for {targetCurrency} on {network} + </p> + </div> + ); +}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/components/invoice-creator.tsx
(1 hunks)src/components/invoice-form/blocks/payment-currency-selector.tsx
(1 hunks)src/components/invoice-form/invoice-form.tsx
(2 hunks)src/server/index.ts
(2 hunks)src/server/routers/currency.ts
(1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: bassgeta
PR: RequestNetwork/easy-invoice#83
File: src/components/create-recurring-payment/blocks/create-recurring-payment-form.tsx:127-138
Timestamp: 2025-06-23T09:14:42.979Z
Learning: In the RequestNetwork/easy-invoice codebase, when suggesting improvements like error handling for transaction approvals, the user bassgeta prefers consistency over isolated improvements. They prefer not to implement error handling in one place unless it's done consistently across all similar transaction flows in the codebase.
Learnt from: rodrigopavezi
PR: RequestNetwork/easy-invoice#45
File: src/components/invoice-form.tsx:316-319
Timestamp: 2025-05-19T13:00:48.790Z
Learning: The handleFormSubmit function in src/components/invoice-form.tsx correctly uses data.clientEmail from the form submission data to find matching payers, which is the proper implementation.
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#42
File: src/components/payment-section.tsx:534-544
Timestamp: 2025-03-25T09:31:47.519Z
Learning: In the PaymentSection component of EasyInvoice, selectedRoute is automatically set to the first route when payment routes are loaded, and users can only switch between routes, not deselect them entirely.
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#2
File: src/server/routers/invoice.ts:88-109
Timestamp: 2025-02-12T12:40:14.742Z
Learning: The payRequest endpoint in src/server/routers/invoice.ts is intentionally kept public (using publicProcedure) to allow invoice sharing and payment by anyone with the payment reference, similar to how payment links work in other payment systems.
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#2
File: src/server/routers/invoice.ts:0-0
Timestamp: 2025-02-12T13:04:46.135Z
Learning: The `payRequest` function in the invoice router retrieves payment transactions calldata from the Request API and does not process the payment itself. The actual payment status update happens through the webhook handler.
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#8
File: src/app/invoice-me/page.tsx:21-21
Timestamp: 2025-02-14T12:48:42.125Z
Learning: In the easy-invoice codebase, error handling for TRPC queries is not required at the component level as confirmed by the maintainer.
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#82
File: src/components/create-recurring-payment/blocks/create-recurring-payment-form.tsx:131-131
Timestamp: 2025-07-11T11:17:32.659Z
Learning: In the RequestNetwork/easy-invoice codebase, the user aimensahnoun prefers to keep recurring payment functionality limited to Sepolia only. Hardcoding the chain to "sepolia" in recurring payment components is intentional and acceptable, rather than making it dynamic based on the connected network.
src/server/index.ts (1)
Learnt from: aimensahnoun
PR: #2
File: src/server/routers/invoice.ts:88-109
Timestamp: 2025-02-12T12:40:14.742Z
Learning: The payRequest endpoint in src/server/routers/invoice.ts is intentionally kept public (using publicProcedure) to allow invoice sharing and payment by anyone with the payment reference, similar to how payment links work in other payment systems.
src/components/invoice-creator.tsx (3)
Learnt from: rodrigopavezi
PR: #45
File: src/components/invoice-form.tsx:316-319
Timestamp: 2025-05-19T13:00:48.790Z
Learning: The handleFormSubmit function in src/components/invoice-form.tsx correctly uses data.clientEmail from the form submission data to find matching payers, which is the proper implementation.
Learnt from: aimensahnoun
PR: #2
File: src/app/invoices/[ID]/page.tsx:160-175
Timestamp: 2025-02-12T12:42:40.076Z
Learning: Invoice items are validated at creation time through the schema validation in src/lib/schemas/invoice.ts, ensuring that items array is non-empty and each item has required description, quantity, and price fields. Runtime validation in display components is not necessary.
Learnt from: aimensahnoun
PR: #21
File: src/app/invoices/[ID]/page.tsx:113-148
Timestamp: 2025-02-20T10:27:02.993Z
Learning: The easy-invoice project prefers simpler, direct implementations over abstract utilities. For example, using .toFixed(2)
directly instead of creating separate number formatting utilities.
src/server/routers/currency.ts (1)
Learnt from: aimensahnoun
PR: #2
File: src/server/routers/invoice.ts:88-109
Timestamp: 2025-02-12T12:40:14.742Z
Learning: The payRequest endpoint in src/server/routers/invoice.ts is intentionally kept public (using publicProcedure) to allow invoice sharing and payment by anyone with the payment reference, similar to how payment links work in other payment systems.
src/components/invoice-form/blocks/payment-currency-selector.tsx (2)
Learnt from: rodrigopavezi
PR: #45
File: src/components/invoice-form.tsx:316-319
Timestamp: 2025-05-19T13:00:48.790Z
Learning: The handleFormSubmit function in src/components/invoice-form.tsx correctly uses data.clientEmail from the form submission data to find matching payers, which is the proper implementation.
Learnt from: aimensahnoun
PR: #42
File: src/components/payment-section.tsx:534-544
Timestamp: 2025-03-25T09:31:47.519Z
Learning: In the PaymentSection component of EasyInvoice, selectedRoute is automatically set to the first route when payment routes are loaded, and users can only switch between routes, not deselect them entirely.
src/components/invoice-form/invoice-form.tsx (4)
Learnt from: rodrigopavezi
PR: #45
File: src/components/invoice-form.tsx:316-319
Timestamp: 2025-05-19T13:00:48.790Z
Learning: The handleFormSubmit function in src/components/invoice-form.tsx correctly uses data.clientEmail from the form submission data to find matching payers, which is the proper implementation.
Learnt from: aimensahnoun
PR: #21
File: src/app/invoices/[ID]/page.tsx:113-148
Timestamp: 2025-02-20T10:27:02.993Z
Learning: The easy-invoice project prefers simpler, direct implementations over abstract utilities. For example, using .toFixed(2)
directly instead of creating separate number formatting utilities.
Learnt from: rodrigopavezi
PR: #45
File: src/components/invoice-form.tsx:451-453
Timestamp: 2025-05-20T12:59:44.665Z
Learning: In the Easy Invoice project, setTimeout is required when submitting a form after modal state changes in the crypto-to-fiat payment flow. Directly calling handleFormSubmit without setTimeout after closing modals and updating state causes issues.
Learnt from: aimensahnoun
PR: #42
File: src/components/payment-section.tsx:534-544
Timestamp: 2025-03-25T09:31:47.519Z
Learning: In the PaymentSection component of EasyInvoice, selectedRoute is automatically set to the first route when payment routes are loaded, and users can only switch between routes, not deselect them entirely.
🧬 Code Graph Analysis (3)
src/server/index.ts (1)
src/server/routers/currency.ts (1)
currencyRouter
(21-39)
src/server/routers/currency.ts (2)
src/server/trpc.ts (2)
router
(11-11)publicProcedure
(12-12)src/lib/axios.ts (1)
apiClient
(3-8)
src/components/invoice-form/invoice-form.tsx (1)
src/components/invoice-form/blocks/payment-currency-selector.tsx (1)
PaymentCurrencySelector
(23-92)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build
🔇 Additional comments (10)
src/server/routers/currency.ts (1)
6-13
: LGTM: Well-defined TypeScript typesThe
ConversionCurrency
type andGetConversionCurrenciesResponse
interface are clearly defined with appropriate type constraints for the currency type enum.src/components/invoice-creator.tsx (1)
3-3
: LGTM: Import path correctly updatedThe import path update reflects the new file structure where the InvoiceForm component has been moved to a subfolder.
src/server/index.ts (1)
3-3
: LGTM: Currency router properly integratedThe
currencyRouter
is correctly imported and integrated into theappRouter
following the established pattern of other routers in the codebase.Also applies to: 19-19
src/components/invoice-form/invoice-form.tsx (2)
42-43
: LGTM: Clean import updatesThe import path adjustments and new
PaymentCurrencySelector
import are correct and maintain consistency with the new file structure.
899-903
: Confirm network configuration for PaymentCurrencySelectorI only found one place where
PaymentCurrencySelector
is rendered with a hardcodednetwork="sepolia"
:
- src/components/invoice-form/invoice-form.tsx:899
- Component definition in src/components/invoice-form/blocks/payment-currency-selector.tsx
Please verify whether one-off invoice payments should indeed be locked to Sepolia or if
network
ought to be driven by the user’s connected network (e.g. via Web3 context). If dynamic behavior is required, update the prop accordingly.src/components/invoice-form/blocks/payment-currency-selector.tsx (5)
17-21
: LGTM: Well-defined component interfaceThe props interface is clear and properly typed, using
InvoiceCurrency
for type safety on thetargetCurrency
parameter.
28-37
: LGTM: Proper TRPC query implementationThe query setup correctly uses the currency router with proper TypeScript generics and passes the required parameters.
39-55
: Excellent loading state UXThe loading state provides clear visual feedback with a spinner and descriptive text, while keeping the select disabled to prevent user interaction.
57-71
: Good error state handlingThe error state appropriately disables the select and displays a helpful error message to the user.
75-91
: LGTM: Clean currency selection implementationThe select implementation properly maps over conversion routes and uses
formatCurrencyLabel
for consistent currency display formatting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approved ✅ Pending comment resolution 🚧 No blocking issues 🚀
Overall: Clean implementation of dynamic payment currency selection. Good separation of concerns with the new PaymentCurrencySelector
component and lightweight backend router.
Comments posted: Minor UX improvements for your consideration, but none are blocking.
Dependencies: Requires Request API PR #264 for full functionality.
src/components/invoice-form/blocks/payment-currency-selector.tsx
Outdated
Show resolved
Hide resolved
3e0277c
to
cfe9718
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/invoice-form/invoice-form.tsx (1)
481-493
: Effect does not re-run when paymentCurrency changes (stale dependency).The effect depends only on form, so it runs once on mount and won’t flip isCryptoToFiatAvailable to false when paymentCurrency changes later. That leaves stale state (toggle stays true on disallowed currencies) and can block submission. Subscribe to form changes or depend on the watched fields.
Contained fix using react-hook-form’s watch subscription:
-// Effect to disable Crypto-to-fiat when payment currency is not Sepolia USDC -useEffect(() => { - const paymentCurrency = form.watch("paymentCurrency"); - const isCryptoToFiatEnabled = form.watch("isCryptoToFiatAvailable"); - - if (paymentCurrency !== "fUSDC-sepolia" && isCryptoToFiatEnabled) { - form.setValue("isCryptoToFiatAvailable", false); - // Clear payment details when disabling crypto-to-fiat - form.setValue("paymentDetailsId", ""); - form.clearErrors("paymentDetailsId"); - } -}, [form]); +// Effect to disable Crypto-to-fiat when payment currency is not Sepolia USDC +useEffect(() => { + const subscription = form.watch((value, { name }) => { + if (name !== "paymentCurrency" && name !== "isCryptoToFiatAvailable") return; + const paymentCurrency = value.paymentCurrency; + const isCryptoToFiatEnabled = value.isCryptoToFiatAvailable; + if (paymentCurrency !== "fUSDC-sepolia" && isCryptoToFiatEnabled) { + form.setValue("isCryptoToFiatAvailable", false); + // Clear payment details when disabling crypto-to-fiat + form.setValue("paymentDetailsId", ""); + form.clearErrors("paymentDetailsId"); + } + }); + return () => subscription.unsubscribe(); +}, [form]);Alternatively, hoist const paymentCurrency = form.watch("paymentCurrency") and const isCryptoToFiatEnabled = form.watch("isCryptoToFiatAvailable") above, and set the effect deps to [paymentCurrency, isCryptoToFiatEnabled, form].
♻️ Duplicate comments (1)
src/components/invoice-form/invoice-form.tsx (1)
900-904
: On change, also clear errors and force-disable crypto-to-fiat when non-fUSDC is selected.Right now, choosing a non-fUSDC currency leaves isCryptoToFiatAvailable potentially true until another effect runs. That can create inconsistent state and block submission. Make the toggle-off immediate here and clear related errors.
Apply this diff:
<PaymentCurrencySelector - onChange={(value) => form.setValue("paymentCurrency", value)} + onChange={(value) => { + form.setValue("paymentCurrency", value, { shouldValidate: true }); + form.clearErrors("paymentCurrency"); + if (value !== "fUSDC-sepolia") { + form.setValue("isCryptoToFiatAvailable", false); + form.setValue("paymentDetailsId", ""); + form.clearErrors("paymentDetailsId"); + } + }} targetCurrency="USD" network={DEFAULT_NETWORK} />Optional (acknowledging prior discussion): If PaymentCurrencySelector later supports a controlled value, pass value={form.watch("paymentCurrency")} to preserve the visual selection across remounts. This was raised previously; keeping it out of scope here is fine.
🧹 Nitpick comments (1)
src/components/invoice-form/invoice-form.tsx (1)
48-48
: Hardcoding DEFAULT_NETWORK to "sepolia" — centralize for consistency.This matches prior product intent to use Sepolia for fiat off-ramp flows (noted in team learnings). Still, consider promoting this to a shared config/constants module so future network changes don’t require scattered edits. Minor typing nit: mark as a literal.
Proposed tweak:
-const DEFAULT_NETWORK = "sepolia"; +const DEFAULT_NETWORK = "sepolia" as const;If you agree to centralize it, I can prep a follow-up PR to expose something like NETWORKS.DEFAULT in a shared constants file and migrate references.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
src/components/invoice-creator.tsx
(1 hunks)src/components/invoice-form/blocks/payment-currency-selector.tsx
(1 hunks)src/components/invoice-form/invoice-form.tsx
(2 hunks)src/server/index.ts
(2 hunks)src/server/routers/currency.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- src/components/invoice-creator.tsx
- src/server/routers/currency.ts
- src/server/index.ts
- src/components/invoice-form/blocks/payment-currency-selector.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-11T11:17:32.659Z
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#82
File: src/components/create-recurring-payment/blocks/create-recurring-payment-form.tsx:131-131
Timestamp: 2025-07-11T11:17:32.659Z
Learning: In the RequestNetwork/easy-invoice codebase, the user aimensahnoun prefers to keep recurring payment functionality limited to Sepolia only. Hardcoding the chain to "sepolia" in recurring payment components is intentional and acceptable, rather than making it dynamic based on the connected network.
Applied to files:
src/components/invoice-form/invoice-form.tsx
🧬 Code graph analysis (1)
src/components/invoice-form/invoice-form.tsx (1)
src/components/invoice-form/blocks/payment-currency-selector.tsx (1)
PaymentCurrencySelector
(22-116)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build
🔇 Additional comments (1)
src/components/invoice-form/invoice-form.tsx (1)
42-44
: Good modularization: imports updated and dropdown decoupled.Switching Alert to the new relative path and introducing PaymentCurrencySelector is a clean separation of concerns and aligns with the PR goal to fetch options from the API. No issues spotted with the import paths or named exports.
… line with what we do in other routers
… handling in router
cfe9718
to
b9a7a27
Compare
Problem
Our crypto-to-fiat currencies dropdown was using app defined constants for the payment currencies.
Solution
Use our new
/v2/currencies/{currencyId}/conversion-routes
endpoint!Changes
Testing
Please test it with this API PR
Resolves #92
Summary by CodeRabbit
New Features
Improvements
Bug Fixes