DEV Community

Cover image for Building PhotoTech AI: A Unified Platform for 20 AI Image Processing Tools
shenghui wang
shenghui wang

Posted on

Building PhotoTech AI: A Unified Platform for 20 AI Image Processing Tools

Building PhotoTech AI: A Unified Platform for 20 AI Image Processing Tools

As a developer, I've always been frustrated with the fragmented landscape of AI image processing tools. Need to remove a background? Go to Remove.bg. Want to upscale an image? Use Upscayl. Need to generate anime art? Find another service. Each tool requires a separate account, different pricing models, and switching between multiple tabs.

So I built PhotoTech AI โ€“ a unified platform that brings together 20 AI-powered image processing tools in one place. In this post, I'll share the technical architecture, implementation details, and lessons learned.

๐ŸŽฏ What We Built

PhotoTech AI (https://phototech.shop) is a full-stack web application that provides:

  • 20 AI Image Tools: Background removal, upscaling, 2D-to-3D conversion, photo restoration, style transfer, AI generation (anime, characters, comics), and more
  • Tiered Membership System: Free, Pro ($9.9/mo), Pro+ ($19.9/mo), Ultra ($39.9/mo) with different tool access and credit consumption rates
  • Credit-Based Billing: Flexible credit system where higher-tier members consume fewer credits per operation
  • Payment Integration: Seamless payment processing with Creem payment gateway
  • Watermark Management: Automatic watermarking for free users, watermark-free for paid members

๐Ÿ› ๏ธ Tech Stack

  • Frontend: Next.js 16, React 19, TypeScript, Tailwind CSS
  • Backend: Next.js API Routes, Supabase (Auth + Database)
  • Database: PostgreSQL with Prisma ORM
  • AI Services: OpenRouter API (GPT-4o, Gemini 2.0 Flash)
  • Payment: Creem payment gateway
  • Deployment: Vercel
  • Analytics: Vercel Analytics + Speed Insights

๐Ÿ—๏ธ Architecture Overview

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   Next.js App   โ”‚
โ”‚  (Frontend +    โ”‚
โ”‚   API Routes)   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚
    โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚         โ”‚              โ”‚             โ”‚
โ”Œโ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ” โ”Œโ”€โ”€โ–ผโ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”
โ”‚Supabaseโ”‚ โ”‚Prismaโ”‚    โ”‚OpenRouterโ”‚  โ”‚  Creem   โ”‚
โ”‚  Auth  โ”‚ โ”‚  DB  โ”‚    โ”‚   API    โ”‚  โ”‚ Payment  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
Enter fullscreen mode Exit fullscreen mode

Key Design Decisions

  1. Unified API Route: Single /api/ai/process-image endpoint that routes to different AI processors based on tool type
  2. Tool Processor Pattern: Abstract base processor with specific implementations for each AI service
  3. Membership-Based Access Control: Server-side validation to prevent frontend bypassing
  4. Credit System: Tiered consumption rates (Free: 5 credits, Pro: 4, Pro+: 3, Ultra: 2 per operation)

๐Ÿ’ป Core Implementation

1. Unified Image Processing API

The heart of the system is a single API route that handles all AI image processing:

export async function POST(request: NextRequest) {
  // 1. Authenticate user
  const supabase = await createClient()
  const { data: { user } } = await supabase.auth.getUser()

  if (!user?.email) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
  }

  const { imageBase64, textPrompt, feature, options } = await request.json()

  // 2. Validate tool access (server-side)
  const membershipLevel = await getUserMembershipLevel(user.email)
  const allTools = getEnabledTools()
  const toolIndex = allTools.findIndex(tool => tool.id === feature)

  if (!canUseTool(membershipLevel, toolIndex, allTools.length)) {
    return NextResponse.json(
      { error: "Tool not available for your membership level" },
      { status: 403 }
    )
  }

  // 3. Check credits
  const creditsCost = getCreditsCostForMembership(membershipLevel)
  const currentCredits = await getUserCredits(user.email)

  if (currentCredits < creditsCost) {
    return NextResponse.json(
      { error: "Insufficient credits" },
      { status: 402 }
    )
  }

  // 4. Process image
  const result = await processImage({
    toolId: feature,
    imageBase64,
    textPrompt,
    options,
  })

  // 5. Deduct credits
  await deductCredits(user.email, creditsCost, `Used ${feature}`)

  // 6. Apply watermark if needed
  const needsWatermark = requiresWatermark(membershipLevel)

  return NextResponse.json({
    imageUrl: result.imageUrl,
    needsWatermark,
    creditsUsed: creditsCost,
    creditsRemaining: currentCredits - creditsCost,
  })
}
Enter fullscreen mode Exit fullscreen mode

2. Tool Processor Pattern

We use a processor pattern to abstract different AI services:

export abstract class BaseProcessor {
  abstract process(
    imageBase64: string,
    options?: Record<string, any>
  ): Promise<ProcessResult>

  protected async callOpenRouter(
    model: string,
    messages: any[],
    imageBase64?: string
  ): Promise<string> {
    // Unified OpenRouter API call
    const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${process.env.OPENROUTER_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        model,
        messages,
        // ... image handling
      }),
    })

    return await response.json()
  }
}
Enter fullscreen mode Exit fullscreen mode

Each tool has its own processor implementation:

export class OpenRouterImageProcessor extends BaseProcessor {
  async process(imageBase64: string, options?: Record<string, any>) {
    const model = this.getModel() // e.g., "google/gemini-2.0-flash-exp"

    const messages = [
      {
        role: "user",
        content: [
          { type: "text", text: this.getPrompt() },
          { type: "image_url", image_url: { url: `data:image/png;base64,${imageBase64}` } }
        ]
      }
    ]

    const result = await this.callOpenRouter(model, messages, imageBase64)
    return this.extractImageUrl(result)
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Tiered Credit System

One interesting challenge was implementing tiered credit consumption. Higher-tier members get better value:

export function getCreditsCostForMembership(
  membershipLevel: MembershipLevel | null | undefined
): number {
  if (!membershipLevel || membershipLevel === "free") return 5
  if (membershipLevel === "pro") return 4
  if (membershipLevel === "proplus") return 3
  if (membershipLevel === "ultra") return 2
  return 5
}
Enter fullscreen mode Exit fullscreen mode

This creates a natural incentive to upgrade: Ultra members get 2.5x more operations per credit than free users.

4. Membership-Based Tool Access

Tools are ordered by complexity/value, and access is controlled by membership level:

export function canUseTool(
  membershipLevel: MembershipLevel | null | undefined,
  toolIndex: number,
  totalTools: number
): boolean {
  const limit = getToolLimitForMembership(membershipLevel)

  // Ultra members can use all tools
  if (membershipLevel === "ultra") {
    return true
  }

  // Others can only use first N tools
  return toolIndex < limit
}
Enter fullscreen mode Exit fullscreen mode

Tool Distribution:

  • Free: First 3 tools (with watermark)
  • Pro: First 8 tools
  • Pro+: First 15 tools
  • Ultra: All 20 tools (no watermark)

5. Payment Integration with Creem

We integrated Creem payment gateway with webhook support:

export async function POST(request: NextRequest) {
  const event = JSON.parse(await request.text())

  switch (event.type) {
    case "payment.succeeded":
    case "checkout.session.completed":
      const { customerEmail, amountTotal } = extractPaymentData(event)

      // Calculate credits (1:10 ratio)
      const credits = calculateCreditsFromAmount(amountTotal)
      await addCredits(customerEmail, credits, "Payment")

      // Set membership level based on product
      const membershipLevel = mapProductToMembership(event.product_id)
      await setUserMembershipLevel(customerEmail, membershipLevel)

      return NextResponse.json({ success: true })
  }
}
Enter fullscreen mode Exit fullscreen mode

6. Client-Side Watermark Application

For free users, we apply watermarks client-side to reduce server load:

const applyWatermark = async (imageUrl: string) => {
  if (!data.needsWatermark) return imageUrl

  // Dynamically import watermark module (client-side only)
  const { addWatermarkToImage } = await import("@/lib/watermark")

  const watermarkedImage = await addWatermarkToImage(
    imageUrl,
    "PhotoTech AI",
    { fontSize: 24, opacity: 0.7 }
  )

  return watermarkedImage
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿšง Challenges & Solutions

Challenge 1: Handling Multiple AI Providers

Problem: Different AI services have different APIs, response formats, and capabilities.

Solution: Abstract processor pattern with a unified interface. Each tool can use the best AI model for its purpose (GPT-4o for analysis, Gemini 2.0 for generation, etc.).

Challenge 2: Server-Side Rendering with useSearchParams

Problem: Next.js 16 requires useSearchParams() to be wrapped in Suspense boundaries.

Solution: Wrap components using useSearchParams() in Suspense:

export default function Home() {
  return (
    <main>
      <Suspense fallback={null}>
        <PaymentCallbackHandler />
      </Suspense>
      {/* ... */}
    </main>
  )
}
Enter fullscreen mode Exit fullscreen mode

Challenge 3: Test Environment Configuration

Problem: Need to disable credit consumption and tool limits during development.

Solution: Environment-based flags:

const isTestMode = 
  process.env.NODE_ENV === 'development' || 
  process.env.ENABLE_TEST_MODE === 'true'

if (isTestMode) {
  // Skip credit checks, allow all tools, no watermarks
}
Enter fullscreen mode Exit fullscreen mode

Challenge 4: Payment Webhook Reliability

Problem: Payment webhooks might fail or arrive out of order.

Solution: Implemented client-side payment verification as a fallback:

useEffect(() => {
  const verifyPayment = async () => {
    const sessionId = searchParams.get("session_id")
    if (sessionId) {
      await fetch("/api/creem/verify-payment", {
        method: "POST",
        body: JSON.stringify({ sessionId }),
      })
    }
  }
  verifyPayment()
}, [])
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“Š Database Schema

model User {
  id              String          @id @default(uuid())
  email           String          @unique
  name            String?
  image           String?
  credits         Int             @default(20)
  membershipLevel MembershipLevel @default(FREE)
  createdAt       DateTime        @default(now())
  updatedAt       DateTime        @updatedAt
}

enum MembershipLevel {
  FREE
  PRO
  PROPLUS
  ULTRA
}
Enter fullscreen mode Exit fullscreen mode

๐ŸŽจ Frontend Architecture

  • Component Structure: Feature-based organization (components/image-upload.tsx, components/pricing/)
  • State Management: React hooks + Supabase session management
  • Styling: Tailwind CSS with shadcn/ui components
  • Image Handling: Next.js Image component with client-side watermarking

๐Ÿš€ Performance Optimizations

  1. Client-Side Watermarking: Reduces server load for free users
  2. Dynamic Imports: Lazy load watermark module only when needed
  3. Image Optimization: Next.js Image component with automatic optimization
  4. API Route Caching: Cache tool configurations and membership data
  5. Vercel Analytics: Monitor performance and user behavior

๐Ÿ”ฎ Future Improvements

  1. Batch Processing: Allow users to process multiple images at once
  2. API Access: Provide REST API for developers
  3. More AI Models: Integrate additional providers (Stability AI, Midjourney API)
  4. Image History: Save processed images for users
  5. Collaboration Features: Share and collaborate on images

๐Ÿ“š Key Takeaways

  1. Unified API Design: Single endpoint for all tools simplifies client code and maintenance
  2. Server-Side Validation: Always validate permissions and credits on the server
  3. Flexible Credit System: Tiered consumption creates natural upgrade incentives
  4. Processor Pattern: Abstracting AI providers makes it easy to add new tools
  5. Test Mode: Environment-based feature flags are essential for development

๐Ÿ”— Resources

  • Live Site: https://phototech.shop
  • Tech Stack: Next.js, React, Supabase, Prisma, OpenRouter
  • Payment: Creem payment gateway

๐Ÿ’ฌ Feedback Welcome!

I'd love to hear your thoughts on the architecture, implementation, or suggestions for improvements. Feel free to reach out or check out the live site!


Tags: #nextjs #react #typescript #ai

Top comments (1)

Collapse
 
shenghui_wang_b9f67a3de4f profile image
shenghui wang

Live Site: phototech.shop