-
Notifications
You must be signed in to change notification settings - Fork 0
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
feat: add direct payment page. #23
base: main
Are you sure you want to change the base?
Conversation
WalkthroughThe changes update the project version and add a new dependency for Ethereum address validation. A new direct payment page and corresponding React component have been introduced to enable cryptocurrency payments with a two-step wallet connection and payment submission process. The background wrapper now includes a dynamic gradient color mapping function. Additionally, existing validation schemas have been improved and extended with a new payment schema, and the server routing has been expanded with a protected payment router that integrates with an external API for processing payments. Changes
Suggested reviewers
✨ Finishing Touches
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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: 3
🧹 Nitpick comments (8)
src/server/routers/payment.ts (1)
6-35
: Well-structured router implementation with room for improvement.The implementation follows tRPC patterns well with protected routes, input validation, and error handling. A few suggestions to enhance this implementation:
- The authentication check at lines 12-17 may be redundant since
protectedProcedure
should already ensure the user is authenticated.- Consider enhancing the error message at line 29 to include more details from the response for better debugging.
- Add logging for successful/failed payment attempts to aid in monitoring and troubleshooting.
export const paymentRouter = router({ pay: protectedProcedure .input(paymentFormSchema) .mutation(async ({ ctx, input }) => { const { user } = ctx; - if (!user) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You must be logged in to pay", - }); - } + // Authentication is already handled by protectedProcedure const response = await apiClient.post("v1/pay", { amount: input.amount.toString(), payee: input.payee, invoiceCurrency: input.invoiceCurrency, paymentCurrency: input.paymentCurrency, }); if (response.status !== 201) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", - message: "Failed to pay", + message: `Failed to pay: ${response.status} ${response.statusText}`, }); } + // Log successful payment + console.log(`Payment successful: ${input.amount} ${input.invoiceCurrency} to ${input.payee}`); return response.data; }), });src/lib/schemas/payment.ts (1)
1-17
: Well-structured schema with appropriate validations.The payment form schema properly validates all fields with meaningful error messages. Good use of the validator library for Ethereum address validation.
Consider enhancing the validation for
paymentCurrency
to ensure it's a valid currency code or from a predefined list, similar to howinvoiceCurrency
is validated:- paymentCurrency: z.string().min(1, "Payment currency is required"), + paymentCurrency: z.string().min(1, "Payment currency is required") + .refine( + (value) => ["ETH", "DAI", /* add other supported currencies */].includes(value), + "Invalid payment currency" + ),src/components/background-wrapper.tsx (1)
26-49
: Good implementation of color mapping function.The
getTailwindColor
function provides a clean way to convert Tailwind color names to hex values, addressing the PR objective of replacing string interpolation with explicit values.Consider extracting this color mapping to a separate utility file to avoid duplication if other components need the same functionality:
+ // src/lib/utils/tailwind-colors.ts + export const tailwindColors: Record<string, string> = { + // Orange colors + "orange-100": "#ffedd5", + "orange-200": "#fed7aa", + // Blue colors + "blue-100": "#dbeafe", + "blue-200": "#bfdbfe", + // Indigo colors + "indigo-100": "#e0e7ff", + "indigo-200": "#c7d2fe", + // Zinc colors + "zinc-100": "#f4f4f5", + "zinc-200": "#e4e4e7", + }; + + export function getTailwindColor(colorName: string): string { + return tailwindColors[colorName] || tailwindColors["zinc-100"]; // Default to zinc-100 + } // Then in background-wrapper.tsx + import { getTailwindColor } from "@/lib/utils/tailwind-colors"; - // Convert Tailwind color names to CSS variables or hex values - const getTailwindColor = (colorName: string): string => { - const colors: Record<string, string> = { - // Orange colors - "orange-100": "#ffedd5", - "orange-200": "#fed7aa", - - // Blue colors - "blue-100": "#dbeafe", - "blue-200": "#bfdbfe", - - // Indigo colors - "indigo-100": "#e0e7ff", - "indigo-200": "#c7d2fe", - - // Zinc colors - "zinc-100": "#f4f4f5", - "zinc-200": "#e4e4e7", - - // Add any other colors you need here - }; - - return colors[colorName] || "#f4f4f5"; // Default to zinc-100 if color not found - };src/components/direct-payment.tsx (4)
91-97
: Clarify the step reset logic when the wallet disconnects.
Switching back to step 1 clears the form context if the user has partially filled the payment fields. Consider preserving filled data in local state if you anticipate partial disconnection followed by reconnection.
167-177
: Revisit automatic form reset after three seconds.
Automatically resetting the form can be surprising if users want to confirm the transaction details again or reference the final data. Consider letting the user reset manually or providing a visual countdown.
378-389
: Expand on the security notice for higher user confidence.
While this section mentions blockchain transparency, consider also reminding users to double-check the payee address, confirm gas fees, and stay attentive to the wallet’s prompts to avoid potential phishing attacks.
397-400
: Validate displayed address substring to improve UX.
Showing only a short substring of the connected address can be confusing if users deal with multiple wallets or addresses. You might supplement this display with a tooltip or button to copy the full address.src/app/direct-payment/page.tsx (1)
27-39
: Consider adding loading state during authenticationWhile the authentication logic is sound, there's no loading state displayed while checking the user session.
Consider adding a loading state to improve user experience:
export default async function DirectPaymentPage() { const { user } = await getCurrentSession(); // Redirect to home if not logged in if (!user) { redirect("/"); } return ( <BackgroundWrapper topGradient={{ from: "blue-100", to: "indigo-200" }} bottomGradient={{ from: "zinc-100", to: "zinc-200" }} > <Header user={user} /> <main className="flex-grow flex flex-col max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 z-10"> + {/* Page content */} <h1 className="mb-2 text-4xl font-bold tracking-tight"> Direct Payment </h1> <p className="mb-8 text-lg text-muted-foreground"> Send payments quickly without having to create a request first. </p> <DirectPayment /> </main> <Footer /> </BackgroundWrapper> ); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (9)
package.json
(3 hunks)src/app/direct-payment/page.tsx
(1 hunks)src/components/background-wrapper.tsx
(1 hunks)src/components/direct-payment.tsx
(1 hunks)src/components/header.tsx
(2 hunks)src/lib/schemas/invoice.ts
(2 hunks)src/lib/schemas/payment.ts
(1 hunks)src/server/index.ts
(1 hunks)src/server/routers/payment.ts
(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/components/background-wrapper.tsx (1)
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#21
File: src/components/background-wrapper.tsx:31-31
Timestamp: 2025-02-20T10:26:33.809Z
Learning: Dynamic Tailwind class interpolation for gradient classes (from-${color} and to-${color}) in the BackgroundWrapper component is intentionally implemented this way and doesn't require a mapping object solution.
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Build
🔇 Additional comments (16)
src/lib/schemas/invoice.ts (2)
2-2
: LGTM! Validator import added.The validator library is correctly imported for Ethereum address validation.
27-27
: Improved wallet address validation.The validation for wallet addresses has been enhanced to verify that input is a valid Ethereum address rather than just a non-empty string, which significantly improves input validation and prevents potential errors.
src/components/header.tsx (2)
9-9
: Clean function signature.Simplified the function signature while maintaining the same functionality.
38-43
: Well-integrated navigation link.The new Direct Payment link follows the same pattern and styling as other navigation items, maintaining UI consistency.
src/components/background-wrapper.tsx (2)
56-59
: Clean implementation of gradient using explicit hex values.Using the
getTailwindColor
function to convert Tailwind color names to hex values for the gradient is a good approach.
64-67
: Consistent implementation for bottom gradient.The bottom gradient follows the same pattern as the top gradient, ensuring consistency.
src/components/direct-payment.tsx (1)
120-147
: Handle potential edge cases during transaction approval.
Ifmetadata.approvalTransactionIndex
is invalid or the user rejects the approval, the code simply falls back to the catch block. For a clearer UX, consider handling the rejection case explicitly and providing a more user-friendly error message or state update.src/server/index.ts (2)
4-4
: Clean integration of new payment routerThe import of the payment router is well-structured and follows the existing pattern in the file.
11-11
: Proper router integrationThe payment router is correctly added to the appRouter configuration, maintaining consistency with the existing architecture.
package.json (3)
4-4
: Version increment aligns with feature additionThe version bump from 0.2.2 to 0.3.0 appropriately follows semantic versioning for the addition of a new feature (direct payment page).
59-59
: New dependency for Ethereum address validationThe validator package addition aligns with the PR objective of enhancing address validation functionality.
70-70
:✅ Verification successful
Type definitions for validator package
Good practice including TypeScript type definitions alongside the validator package. Note the slight version mismatch between the package (13.12.0) and types (13.12.2), but this is common and unlikely to cause issues.
🌐 Web query:
Are there any compatibility issues between validator 13.12.0 and @types/validator 13.12.2?
💡 Result:
Validator.js 13.12.0 and its corresponding TypeScript definitions (@types/validator 13.12.2) are designed to work together without compatibility issues when properly aligned in versions[1][7][10]. Here's the breakdown:
Version Alignment
- validator.js 13.12.0 (released May 2024) introduced updates like
isUUID
v7 support andisDate
timezone fixes[1][7].- @types/validator 13.12. follows semantic versioning where minor/patch updates (e.g., 13.12.2) only add type fixes without breaking changes[10].
Key Considerations
Type Definition Coverage
The TypeScript types explicitly track validator.js' API surface[10]. Version 13.x types include:
- All validators/sanitizers from validator.js ≤13.x
- Optional configuration parameters for methods like
isEmail
- Locale support typing
Potential Mismatch Scenarios
Issues could theoretically occur if:
- A validator.js method was added/modified in 13.12. but not reflected in @types/validator (no evidence found in sources)
- Using deprecated validators removed in validator.js but still present in types (not indicated for this version range)
Installation Verification
Always confirm:npm list validator @types/validatorShould show:
├── [email protected].**0** └── @types/[email protected].**2**
The latest available data shows no reported compatibility issues between these specific versions[1][9][10]. For critical production use, test key validators like
isUUID
v7[1] andisDate
with timezones[1][7] to confirm type coverage matches runtime behavior[10].Citations:
- 1: https://github.com/validatorjs/validator.js/releases
- 2: https://dev.to/raphaelbadia/fixing-class-validator-issues-in-a-new-nest-js-project-2547
- 3: https://docs.pydantic.dev/2.3/usage/validators/
- 4: https://www.npmjs.com/package/validator
- 5: https://community.prismic.io/t/authentication-issues-when-pushing-data-contract-changes-for-a-repository/18115
- 6: https://validator-collection.readthedocs.io/en/latest/
- 7: https://github.com/validatorjs/validator.js/blob/master/CHANGELOG.md
- 8: Can't install dependencies after nx migrate to angular 19 nrwl/nx#29290
- 9: https://socket.dev/npm/package/validator
- 10: https://www.npmjs.com/package/@types/validator
Compatibility Confirmed for Validator Type Definitions
The slight version mismatch between
[email protected]
and@types/[email protected]
has been verified via the latest compatibility analysis. The TypeScript definitions accurately track the API surface of validator.js, and the minor patch difference is common practice without any known issues.
- Versions are intentionally aligned:
[email protected]
with@types/[email protected]
.- No compatibility issues were reported, so no changes are required.
src/app/direct-payment/page.tsx (4)
9-12
: Well-defined metadata for SEOThe metadata is clearly defined with an appropriate title and descriptive text that accurately reflects the page's purpose.
14-21
: Authentication protection properly implementedThe authentication check and redirect logic are correctly implemented to ensure only authenticated users can access the direct payment functionality.
23-26
: Explicit gradient values match PR objectiveThe explicit gradient color mapping aligns with the PR objective of replacing Tailwind class string interpolation with explicit values.
28-37
: Well-structured layout with clear component hierarchyThe page layout follows good design practices with appropriate spacing, typography hierarchy, and component organization. The DirectPayment component is correctly integrated into the page.
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 (4)
src/components/direct-payment.tsx (4)
112-185
: Consider breaking down the onSubmit function for better maintainability.The current implementation is functional but quite lengthy. Consider extracting specific operations like approval handling, transaction execution, and success/error handling into separate helper functions.
- const onSubmit = async (data: PaymentFormValues) => { + const handleApprovalTransaction = async (signer: ethers.providers.JsonRpcSigner, paymentData: any) => { + toast.info("Approval required", { + description: "Please approve the transaction in your wallet", + }); + + const approvalIndex = paymentData.metadata.approvalTransactionIndex; + const approvalTransaction = await signer.sendTransaction( + paymentData.transactions[approvalIndex], + ); + + await approvalTransaction.wait(); + return true; + }; + + const handlePaymentTransaction = async (signer: ethers.providers.JsonRpcSigner, paymentData: any, isApprovalNeeded: boolean) => { + toast.info("Sending payment", { + description: "Please confirm the transaction in your wallet", + }); + + const paymentTransaction = await signer.sendTransaction( + paymentData.transactions[isApprovalNeeded ? 1 : 0], + ); + + await paymentTransaction.wait(); + }; + + const resetFormAfterSuccess = (data: PaymentFormValues) => { + toast.success("Payment successful", { + description: `You've paid ${data.amount} ${formatCurrencyLabel( + data.invoiceCurrency, + )} to ${data.payee.substring(0, 6)}...${data.payee.substring( + data.payee.length - 4, + )}`, + }); + + setPaymentStatus("success"); + + // Reset form after successful payment + setTimeout(() => { + form.reset({ + payee: "", + amount: 0, + invoiceCurrency: "USD", + paymentCurrency: "ETH-sepolia-sepolia", + }); + setPaymentStatus("idle"); + }, 3000); + }; + + const onSubmit = async (data: PaymentFormValues) => {
168-177
: Extract the timeout duration as a constant.The magic number 3000ms could be extracted to improve code readability and maintainability.
+const FORM_RESET_DELAY_MS = 3000; + export function DirectPayment() { // Other code... // In the onSubmit function - }, 3000); + }, FORM_RESET_DELAY_MS);
397-400
: Consider creating a reusable helper for address formatting.The address truncation logic appears in multiple places (here and in the success toast). Creating a helper function would improve code maintainability.
+// Add to an appropriate utility file +const formatAddress = (address: string | undefined): string => { + if (!address) return ''; + return `${address.substring(0, 6)}...${address.substring(address.length - 4)}`; +}; // Then use in the component <span className="font-mono mr-2"> - {address?.substring(0, 6)}... - {address?.substring(address?.length - 4)} + {formatAddress(address)} </span> // And in the success toast (line 158-163) - `You've paid ${data.amount} ${formatCurrencyLabel( - data.invoiceCurrency, - )} to ${data.payee.substring(0, 6)}...${data.payee.substring( - data.payee.length - 4, - )}`, + `You've paid ${data.amount} ${formatCurrencyLabel(data.invoiceCurrency)} to ${formatAddress(data.payee)}`,
177-185
: Improve error handling with more specific error messages.The current error handling doesn't distinguish between different types of errors. Consider adding more granular error handling to provide better feedback to users.
} catch (error) { console.error("Payment error:", error); + + // More specific error messages based on error type + let errorMessage = "There was an error processing your payment. Please try again."; + + if (error instanceof Error) { + if (error.message.includes("user rejected")) { + errorMessage = "Transaction was rejected. Please try again when ready."; + } else if (error.message.includes("insufficient funds")) { + errorMessage = "Insufficient funds in your wallet to complete this transaction."; + } else if (error.message.includes("network")) { + errorMessage = "Network error. Please check your connection and try again."; + } + } + toast.error("Payment failed", { - description: - "There was an error processing your payment. Please try again.", + description: errorMessage, }); setPaymentStatus("error"); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/direct-payment.tsx
(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/components/direct-payment.tsx (1)
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#23
File: src/components/direct-payment.tsx:63-89
Timestamp: 2025-02-27T13:58:33.590Z
Learning: The AppKit wallet module requires initialization time before it's ready for use. A 2-second delay is intentionally used in the DirectPayment component to ensure the wallet is fully initialized before allowing interactions.
🔇 Additional comments (2)
src/components/direct-payment.tsx (2)
63-89
: The AppKit initialization approach is appropriate.The 2-second delay is intentionally used to ensure the wallet module is fully initialized before allowing interactions, as noted in the retrieved learnings.
403-429
: Button state handling is correctly implemented.The submit button is appropriately disabled during processing to prevent multiple submissions, and the UI provides clear feedback on the current payment status.
Resolves #15
Changes:
Example Video:
CleanShot.2025-02-27.at.16.46.02.mp4
Summary by CodeRabbit
New Features
Refactor
Chores