Skip to content

fix: v1 API audit - stealth support, enriched responses, new endpoints, doc corrections#25

Merged
Shreyassp002 merged 5 commits intoHoomanBuilds:mainfrom
Shreyassp002:main
Apr 7, 2026
Merged

fix: v1 API audit - stealth support, enriched responses, new endpoints, doc corrections#25
Shreyassp002 merged 5 commits intoHoomanBuilds:mainfrom
Shreyassp002:main

Conversation

@Shreyassp002
Copy link
Copy Markdown
Contributor

Summary

  • Extracted shared chain/token fetch helpers from internal routes into src/lib/api/get-chains.ts and src/lib/api/get-tokens.ts, refactored internal routes to use them
  • Fixed v1 payment-links POST: added description, use_stealth to schema, stealth enforcement logic, writes receive_token_symbol to DB, enriched response with all fields
  • Fixed v1 payment-links GET/PATCH: enriched GET response, replaced status-only PATCH with Zod schema supporting title, description, max_uses, expires_at, success_url, cancel_url, metadata
  • Fixed v1 webhooks GET list: replaced fetch-all-deliveries-into-memory with per-endpoint count queries
  • Added v1 merchant endpoint (GET/PUT) for profile management via API key
  • Added v1 chains and tokens endpoints for programmatic discovery of supported networks
  • Fixed transaction docs: wrong field names (source_chain_id -> from_chain_id, destination_tx_hash -> dest_tx_hash, etc.)
  • Fixed authentication docs: session auth -> wallet auth, added GET endpoint docs, added name to POST response
  • Updated payment-links docs: added new fields, enriched all response examples, expanded PATCH section
  • Updated webhooks docs: added updated_at, error_message, PATCH response example
  • Added new doc pages for merchant profile and chains/tokens endpoints
  • Added Merchant and Chains & Tokens to docs navigation

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 6, 2026

@Shreyassp002 is attempting to deploy a commit to the Shreyas' projects Team on Vercel.

A member of the Team first needs to authorize it.

@Shreyassp002 Shreyassp002 changed the title fix: v1 API audit — stealth support, enriched responses, new endpoints, doc corrections fix: v1 API audit - stealth support, enriched responses, new endpoints, doc corrections Apr 6, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

⚡ Flash Review

Metric Value
Files Reviewed 5
Risk Level 🔴 High
Issues Found 🚨 2 Critical · ⚠️ 0 Warnings · 💡 3 Suggestions

🚨 Critical (must fix before merge)

  • src/app/api/tokens/route.ts:L47 — 🚨 Security: The chainKey parameter lacks robust validation, risking malformed input leading to errors or data access issues.
  • src/app/api/v1/tokens/route.ts:L18 — 🚨 Security: The chainKey query parameter lacks proper validation, posing a risk of SQL injection or unexpected behavior.

💡 Suggestions (nice to have)

  • src/app/api/v1/chains/route.ts:L20 — 🧹 Code Quality: Using as any bypasses TypeScript's type checking; explicitly type or infer createServerClient for better safety.
  • src/app/api/v1/merchant/route.ts:L74 — 🧹 Code Quality: Record<string, any> for updateObj reduces type safety; derive type from Zod schema.
  • src/app/api/v1/chains/route.ts:L29 — 🧹 Code Quality: Replace console.error with a structured logging solution for production environments.

✅ What's good: The src/app/api/v1/payment-links/[id]/route.ts file demonstrates excellent improvements, including robust error handling for malformed request bodies, critical input validation for security, and enhanced API consumer experience by returning appropriate status codes for no-op updates.


⚡ Powered by Flash Review · Report Issue

⚠️ Note: 8 file(s) could not be reviewed due to API errors.

try {
const { searchParams } = new URL(req.url)
const type = searchParams.get('type') || 'all'
const hasUSDC = searchParams.get('hasUSDC') === 'true'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🚨 Security: The type query parameter is taken directly from user input without validation against allowed values. If getChains uses this parameter directly in a SQL query, it could be vulnerable to SQL injection.

Fix: Implement Zod validation for type to ensure it's one of the expected enum values ('all', 'evm', 'solana').

import { z } from 'zod'

const querySchema = z.object({
  type: z.enum(['all', 'evm', 'solana']).default('all'),
  hasUSDC: z.string().optional().transform(s => s === 'true')
})

const { type, hasUSDC } = querySchema.parse(Object.fromEntries(searchParams))


// eslint-disable-next-line @typescript-eslint/no-explicit-any
const supabase = createServerClient() as any

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🧹 Code Quality: Using as any for the Supabase client bypasses TypeScript's type checking. This can lead to runtime errors that could have been caught at compile time.

Fix: Ensure createServerClient() returns a properly typed Supabase client. You might need to define a type for your Supabase database schema and pass it to createServerClient<Database>().

total: result.total,
})
} catch (err) {
console.error('V1 Chains Error:', err)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🧹 Code Quality: console.error should generally be avoided in production API routes. While it's an error, it doesn't integrate with structured logging systems.

Fix: Replace console.error with a dedicated logging utility (e.g., a custom logger, Pino, Winston) that can provide structured logs, context, and integrate with monitoring tools.

import { NextRequest, NextResponse } from 'next/server'
import { createServerClient } from '@/lib/supabase'
import { verifyApiKey } from '@/lib/api/verify-api-key'
import { z } from 'zod'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🐛 Bug: Chain IDs are typically numbers. Allowing string | number for default_receive_chain can lead to type mismatches or unexpected behavior if the Supabase column is strictly typed (e.g., integer or bigint).

Fix: Standardize on z.number() for chain IDs. If string inputs are expected, add a transform to parse them to numbers, ensuring robust validation.

default_receive_chain: z
  .union([z.number(), z.string().transform(s => parseInt(s, 10))])
  .nullable()
  .optional()
  .refine(val => val === null || !isNaN(val), {
    message: 'Invalid chain ID format',
  }),


if (error) {
return NextResponse.json({ error }, { status: 401 })
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🧹 Code Quality: Using any bypasses TypeScript's type checking and can hide potential issues. While sometimes necessary for external libraries or specific Supabase client setups, it's best to type the Supabase client correctly if possible.

Fix: Explore options to correctly type createServerClient() or define a more specific interface for the client methods used.


if (data.default_receive_chain !== undefined) updateObj.default_receive_chain = data.default_receive_chain
if (data.default_receive_token !== undefined) updateObj.default_receive_token = data.default_receive_token
if (data.business_name !== undefined) updateObj.business_name = data.business_name
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🐛 Bug: Token addresses must be consistently lowercased to avoid issues with different casing representations on-chain (e.g., checksummed vs. non-checksummed addresses). Storing them as-is can lead to inconsistencies when querying or comparing.

Fix: Convert data.default_receive_token to lowercase before storing it in the database.

if (data.default_receive_token !== undefined) {
  updateObj.default_receive_token = data.default_receive_token?.toLowerCase()
}

.from('merchants')
.select(
'wallet_address, business_name, email, default_receive_chain, default_receive_token, stealth_enabled, created_at',
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

Performance: Supabase update operations can return the updated row directly by chaining .select() to the update query. This avoids a second database call to fetch the updated data.

Fix: Modify the update query to include .select() and capture the updated data directly.

const { data: updatedMerchant, error: updateError } = await supabase
  .from('merchants')
  .update(updateObj)
  .eq('id', merchant.id)
  .select(
    'wallet_address, business_name, email, default_receive_chain, default_receive_token, stealth_enabled, created_at',
  )
  .single()

// Then use updatedMerchant instead of calling supabase.from('merchants').select(...) again

const { searchParams } = new URL(req.url)
const rawType = searchParams.get('type') || 'all'
const type = ['all', 'evm', 'solana', 'bitcoin', 'near', 'tron', 'sui', 'ton', 'starknet', 'aptos'].includes(rawType) ? rawType : 'all'
const hasUSDC = searchParams.get('hasUSDC') === 'true'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🧹 Code Quality: Using as any here bypasses TypeScript's type checking. While createServerClient might return a complex type, it's better to explicitly type it or infer it correctly to maintain type safety and catch potential errors early.

Fix: Consider importing the correct Supabase client type (e.g., SupabaseClient<Database>) or ensuring createServerClient is typed correctly to avoid any.


return NextResponse.json({
data: result.chains,
total: result.total,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🧹 Code Quality: console.error should be replaced with a structured logging solution in production environments. This helps with centralized error monitoring, analysis, and avoids potential performance overhead or missing critical alerts.

Fix: Integrate a dedicated logger (e.g., Pino, Winston, or a custom wrapper around a service like Sentry) to handle errors consistently across the application.

if (error) {
return NextResponse.json({ error }, { status: 401 })
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🧹 Code Quality: Using as any bypasses TypeScript's type safety. The createServerClient() function should return a properly typed Supabase client. Consider defining a Database type for Supabase and using createServerClient<Database>() to ensure type safety for database operations.

const data = validation.data

// Build update object dynamically — only include provided fields
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🧹 Code Quality: Using Record<string, any> for updateObj bypasses type safety. Since updateObj is built from validation.data, its type can be more accurately inferred or explicitly defined based on updateMerchantSchema's partial type. For example, Partial<z.infer<typeof updateMerchantSchema>> would provide better type guarantees.

return NextResponse.json({ error: 'No fields to update' }, { status: 400 })
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🧹 Code Quality: Similar to line 21, using as any here bypasses type safety. Ensure the Supabase client is correctly typed to leverage TypeScript's benefits and prevent potential runtime errors due to type mismatches.

'wallet_address, business_name, email, default_receive_chain, default_receive_token, stealth_enabled, created_at',
)
.single()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🧹 Code Quality: console.error should be replaced with a structured logging solution (e.g., Pino, Winston) for production environments. This ensures errors are properly captured, monitored, and don't leak sensitive information in raw console logs, which is crucial for a platform handling REAL money.

transactions: transactions || []
updated_at: link.updated_at,
transactions: transactions || [],
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🐛 Bug: Failing to parse the request body (e.g., malformed JSON) would previously throw an unhandled error, resulting in a generic 500 Internal Server Error. This try/catch block correctly handles such cases by returning a specific 400 Bad Request, improving API robustness and developer experience.

Fix: This change is a good fix.

req: NextRequest,
{ params }: { params: Promise<{ id: string }> }
{ params }: { params: Promise<{ id: string }> },
) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🚨 Security: This is a critical security improvement. Without proper input validation, an attacker could send malformed or unexpected data, potentially leading to database errors, unexpected application behavior, or even injection attacks if the data were used in a less secure context (though Supabase's ORM mitigates direct SQL injection, it's still best practice to validate at the API boundary).

Fix: The use of updatePaymentLinkSchema.safeParse(body) correctly validates the incoming request body against a predefined schema, ensuring only valid data is processed.

let body
try {
body = await req.json()
} catch {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🧹 Code Quality: The updateObj is explicitly typed as Record<string, any>. While functional, using any reduces type safety. It would be more robust to derive a type from the Zod schema, such as Partial<z.infer<typeof updatePaymentLinkSchema>> & { updated_at: string }.

Fix: Consider defining a more specific type for updateObj to leverage TypeScript's type checking capabilities, e.g., const updateObj: Partial<PaymentLinkTable> & { updated_at: string } = { ... } (assuming PaymentLinkTable is your DB type).

}, { status: 400 })
}

const data = validation.data
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🎨 UX: This check is a good addition for API consumer experience. If a client sends a PATCH request with no actual fields to update (only updated_at would be set internally), returning a 400 Bad Request with a clear message is more informative than a 200 OK, which might imply a change occurred when none did.

Fix: This change is a good improvement.


try {
const { searchParams } = new URL(req.url)
const chainKey = searchParams.get('chainKey')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🚨 Security: The chainKey query parameter is used to fetch tokens but lacks proper validation. If getTokens uses this value directly in a Supabase query, it could be vulnerable to SQL injection. Even if getTokens uses parameterized queries, validating inputs at the API boundary is a critical security practice.

Fix: Add Zod validation to ensure chainKey is a valid chain identifier (e.g., a numeric string or a specific format) before passing it to getTokens:

import { z } from 'zod'
// ...
const querySchema = z.object({
  chainKey: z.string().regex(/^\d+$/).transform(Number), // Assuming numeric chain IDs
})
const parsed = querySchema.safeParse({ chainKey })

if (!parsed.success) {
  return NextResponse.json({ error: 'Invalid chainKey parameter' }, { status: 400 })
}
const validatedChainKey = parsed.data.chainKey
// ... use validatedChainKey

}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const supabase = createServerClient() as any
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🧹 Code Quality: Using as any bypasses TypeScript's type safety. This can hide potential type mismatches or incorrect usage of the Supabase client, which is critical for a system handling real money.

Fix: Infer the correct type for the Supabase client or import the specific type definition if available (e.g., SupabaseClient<Database>) to maintain strong typing throughout the application.

chainKey: result.chainKey,
total: result.total,
})
} catch (err) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flash Review

🧹 Code Quality: console.error should not be used in production code. It lacks structured logging capabilities, making it difficult to monitor, filter, and analyze errors in a production environment.

Fix: Replace console.error with a proper structured logging solution (e.g., a dedicated logger service or a library like Pino/Winston) that can integrate with your observability stack.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
flash-protocol Ready Ready Preview, Comment Apr 7, 2026 1:34pm

@Shreyassp002 Shreyassp002 merged commit cf63d20 into HoomanBuilds:main Apr 7, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant