-
Notifications
You must be signed in to change notification settings - Fork 620
[Playground] Add payTo parameter to X402 payment API #8231
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,24 +7,25 @@ import { token } from "../../payments/x402/components/constants"; | |
| // Allow streaming responses up to 5 minutes | ||
| export const maxDuration = 300; | ||
|
|
||
| export async function GET(request: NextRequest) { | ||
| const client = createThirdwebClient({ | ||
| secretKey: process.env.THIRDWEB_SECRET_KEY as string, | ||
| }); | ||
| const client = createThirdwebClient({ | ||
| secretKey: process.env.THIRDWEB_SECRET_KEY as string, | ||
| }); | ||
|
|
||
| const BACKEND_WALLET_ADDRESS = process.env.ENGINE_BACKEND_WALLET as string; | ||
| // const BACKEND_WALLET_ADDRESS = process.env.ENGINE_BACKEND_SMART_WALLET as string; | ||
| const ENGINE_VAULT_ACCESS_TOKEN = process.env | ||
| .ENGINE_VAULT_ACCESS_TOKEN as string; | ||
| const API_URL = `https://${process.env.NEXT_PUBLIC_API_URL || "api.thirdweb.com"}`; | ||
| const BACKEND_WALLET_ADDRESS = process.env.ENGINE_BACKEND_WALLET as string; | ||
| // const BACKEND_WALLET_ADDRESS = process.env.ENGINE_BACKEND_SMART_WALLET as string; | ||
| const ENGINE_VAULT_ACCESS_TOKEN = process.env | ||
| .ENGINE_VAULT_ACCESS_TOKEN as string; | ||
| // const API_URL = `https://${process.env.NEXT_PUBLIC_API_URL || "api.thirdweb.com"}`; | ||
| const API_URL = "http://localhost:3030"; | ||
|
|
||
| const twFacilitator = facilitator({ | ||
| baseUrl: `${API_URL}/v1/payments/x402`, | ||
| client, | ||
| serverWalletAddress: BACKEND_WALLET_ADDRESS, | ||
| vaultAccessToken: ENGINE_VAULT_ACCESS_TOKEN, | ||
| }); | ||
| const twFacilitator = facilitator({ | ||
| baseUrl: `${API_URL}/v1/payments/x402`, | ||
| client, | ||
| serverWalletAddress: BACKEND_WALLET_ADDRESS, | ||
| vaultAccessToken: ENGINE_VAULT_ACCESS_TOKEN, | ||
| }); | ||
|
Comment on lines
+10
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major 🧩 Analysis chainVerify error handling for module-scope initialization. Moving the client and facilitator initialization to module scope is an appropriate optimization that reduces per-request overhead. However, ensure that initialization failures are handled gracefully. Run the following script to verify error handling when environment variables are missing: Consider adding validation at module scope to fail fast with clear error messages: if (!process.env.THIRDWEB_SECRET_KEY) {
throw new Error("THIRDWEB_SECRET_KEY environment variable is required");
}
if (!process.env.ENGINE_BACKEND_WALLET) {
throw new Error("ENGINE_BACKEND_WALLET environment variable is required");
}🏁 Script executed: #!/bin/bash
# Description: Check if there's error handling for missing environment variables
# Search for error handling around these environment variables
rg -nP -A5 -B5 'THIRDWEB_SECRET_KEY|ENGINE_BACKEND_WALLET|ENGINE_VAULT_ACCESS_TOKEN' apps/playground-web/src/app/api/paywall/route.ts
# Search for any try-catch or error boundaries in API routes
rg -nP -A3 'export.*async.*function.*(GET|POST)' apps/playground-web/src/app/api/ --type tsLength of output: 2016 Add validation for required environment variables Validate and throw clear errors if if (!process.env.THIRDWEB_SECRET_KEY) {
throw new Error("THIRDWEB_SECRET_KEY environment variable is required");
}
// …repeat for ENGINE_BACKEND_WALLET and ENGINE_VAULT_ACCESS_TOKEN🤖 Prompt for AI Agents |
||
|
|
||
| export async function GET(request: NextRequest) { | ||
| const paymentData = request.headers.get("X-PAYMENT"); | ||
| const queryParams = request.nextUrl.searchParams; | ||
|
|
||
|
|
@@ -38,6 +39,7 @@ export async function GET(request: NextRequest) { | |
| } | ||
|
|
||
| const amount = queryParams.get("amount") || "0.01"; | ||
| const payTo = queryParams.get("payTo") ?? undefined; | ||
| const tokenAddress = queryParams.get("tokenAddress") || token.address; | ||
| const decimals = queryParams.get("decimals") || token.decimals.toString(); | ||
| const waitUntil = | ||
|
|
@@ -49,6 +51,7 @@ export async function GET(request: NextRequest) { | |
| method: "GET", | ||
| paymentData, | ||
| network: defineChain(Number(chainId)), | ||
| payTo, | ||
| price: { | ||
| amount: toUnits(amount, parseInt(decimals)).toString(), | ||
| asset: { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -45,6 +45,7 @@ export function X402LeftSection(props: { | |||||||||||||||||||||||||||||||||
| const tokenId = useId(); | ||||||||||||||||||||||||||||||||||
| const amountId = useId(); | ||||||||||||||||||||||||||||||||||
| const waitUntilId = useId(); | ||||||||||||||||||||||||||||||||||
| const payToId = useId(); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const handleChainChange = (chainId: number) => { | ||||||||||||||||||||||||||||||||||
| setSelectedChain(chainId); | ||||||||||||||||||||||||||||||||||
|
|
@@ -81,6 +82,13 @@ export function X402LeftSection(props: { | |||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const handlePayToChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||||||||||||||||||||||||||||||||||
| setOptions((v) => ({ | ||||||||||||||||||||||||||||||||||
| ...v, | ||||||||||||||||||||||||||||||||||
| payTo: e.target.value as `0x${string}`, | ||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+85
to
+90
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unsafe type assertion without validation. The type assertion Consider adding validation to ensure the address:
Apply this diff to add basic validation: const handlePayToChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ const value = e.target.value;
+ // Only update if empty or valid hex address format
+ if (value === '' || /^0x[a-fA-F0-9]{0,40}$/.test(value)) {
setOptions((v) => ({
...v,
- payTo: e.target.value as `0x${string}`,
+ payTo: value as `0x${string}`,
}));
+ }
};Alternatively, use a proper address validation library like 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const handleWaitUntilChange = ( | ||||||||||||||||||||||||||||||||||
| value: "simulated" | "submitted" | "confirmed", | ||||||||||||||||||||||||||||||||||
| ) => { | ||||||||||||||||||||||||||||||||||
|
|
@@ -140,6 +148,22 @@ export function X402LeftSection(props: { | |||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| {/* Pay To input */} | ||||||||||||||||||||||||||||||||||
| <div className="flex flex-col gap-2"> | ||||||||||||||||||||||||||||||||||
| <Label htmlFor={payToId}>Pay To Address</Label> | ||||||||||||||||||||||||||||||||||
| <Input | ||||||||||||||||||||||||||||||||||
| id={payToId} | ||||||||||||||||||||||||||||||||||
| type="text" | ||||||||||||||||||||||||||||||||||
| placeholder="0x..." | ||||||||||||||||||||||||||||||||||
| value={options.payTo} | ||||||||||||||||||||||||||||||||||
| onChange={handlePayToChange} | ||||||||||||||||||||||||||||||||||
| className="bg-card" | ||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||
| <p className="text-sm text-muted-foreground"> | ||||||||||||||||||||||||||||||||||
| The wallet address that will receive the payment | ||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| {/* Wait Until selection */} | ||||||||||||||||||||||||||||||||||
| <div className="flex flex-col gap-2"> | ||||||||||||||||||||||||||||||||||
| <Label htmlFor={waitUntilId}>Wait Until</Label> | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
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.
Critical: Hardcoded localhost URL will break in production.
The API URL is hardcoded to
http://localhost:3030, which will fail in any non-local environment. This appears to be temporary test code based on the commented production URL.Apply this diff to restore the production URL:
Alternatively, if localhost testing is needed, use an environment variable to switch between environments:
🤖 Prompt for AI Agents