<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: gnetid</title>
    <description>The latest articles on DEV Community by gnetid (@gnetid).</description>
    <link>https://dev.to/gnetid</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3620609%2Fc66bd33e-e058-43fd-a610-afe65bb39562.png</url>
      <title>DEV Community: gnetid</title>
      <link>https://dev.to/gnetid</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gnetid"/>
    <language>en</language>
    <item>
      <title>Building a Production-Ready Multi-Tenant ISP Billing System in 2 Days with Qoder AI</title>
      <dc:creator>gnetid</dc:creator>
      <pubDate>Thu, 20 Nov 2025 14:38:01 +0000</pubDate>
      <link>https://dev.to/gnetid/building-a-production-ready-multi-tenant-isp-billing-system-in-2-days-with-qoder-ai-3bm1</link>
      <guid>https://dev.to/gnetid/building-a-production-ready-multi-tenant-isp-billing-system-in-2-days-with-qoder-ai-3bm1</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR 🚀
&lt;/h2&gt;

&lt;p&gt;I built a complete &lt;strong&gt;multi-tenant ISP billing system&lt;/strong&gt; from scratch and deployed it to production in just &lt;strong&gt;48 hours&lt;/strong&gt; using Qoder AI. The system now handles real ISP operations including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Multi-tenant architecture with full data isolation&lt;/li&gt;
&lt;li&gt;✅ PPPoE user management &amp;amp; FreeRADIUS integration&lt;/li&gt;
&lt;li&gt;✅ Payment processing (Midtrans &amp;amp; Xendit)&lt;/li&gt;
&lt;li&gt;✅ WhatsApp notifications with load balancing&lt;/li&gt;
&lt;li&gt;✅ Hotspot voucher system with e-commerce&lt;/li&gt;
&lt;li&gt;✅ Real-time router monitoring&lt;/li&gt;
&lt;li&gt;✅ Financial tracking &amp;amp; reporting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt; Next.js 15, Prisma ORM, FreeRADIUS, Mikrotik RouterOS, MySQL&lt;/p&gt;




&lt;h2&gt;
  
  
  The Challenge 💡
&lt;/h2&gt;

&lt;p&gt;ISP billing systems are notoriously complex. You need to handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer management across multiple branches&lt;/li&gt;
&lt;li&gt;Real-time integration with network equipment (Mikrotik routers)&lt;/li&gt;
&lt;li&gt;Payment gateway integration with webhook handling&lt;/li&gt;
&lt;li&gt;Automated notifications (WhatsApp, Email)&lt;/li&gt;
&lt;li&gt;Multi-tenant data isolation&lt;/li&gt;
&lt;li&gt;Financial reporting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Traditionally, building such a system would take weeks or months. I had &lt;strong&gt;2 days&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Qoder AI? 🤖
&lt;/h2&gt;

&lt;p&gt;I've used various AI coding assistants before, but Qoder's &lt;strong&gt;memory feature&lt;/strong&gt; changed the game completely. Here's what made the difference:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Context Retention Across Sessions&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Qoder remembered our previous conversations about the architecture. When I came back the next day, it didn't ask "what are you building?" - it just continued where we left off.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Understanding Complex Architecture&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Multi-tenant systems are tricky. Qoder helped me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design proper tenant isolation in the database&lt;/li&gt;
&lt;li&gt;Implement domain-based tenant detection&lt;/li&gt;
&lt;li&gt;Handle centralized services with tenant overrides (VPN, Payment Gateway)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Production-Ready Code&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Not just prototypes - Qoder helped write actual production code with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proper error handling&lt;/li&gt;
&lt;li&gt;Security considerations (tenant isolation, authentication)&lt;/li&gt;
&lt;li&gt;Performance optimizations&lt;/li&gt;
&lt;li&gt;Real-world edge cases&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Architecture 🏗️
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Multi-Tenant Design
&lt;/h3&gt;

&lt;p&gt;The system supports two operational modes:&lt;/p&gt;

&lt;p&gt;HEAD Office:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SuperHead admin manages all tenants
// Access: /mgmt
// Role: HEAD_ADMIN
// tenantId: null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Branch Offices:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Each tenant has isolated data
// Access: /admin  
// Role: BRANCH_ADMIN
// tenantId: specific UUID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Database Schema Highlights
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// All core models have tenant isolation
model PppoeUser {
  id        String   @id @default(uuid())
  tenantId  String?  // NULL = shared, UUID = specific tenant
  username  String   @unique
  // ... other fields

  tenant    Tenant?  @relation(fields: [tenantId], references: [id])
  @@index([tenantId])
}

model PaymentGateway {
  id        String   @id @default(uuid())
  tenantId  String?  // NULL = HEAD (central), UUID = tenant override
  provider  String   // midtrans, xendit, duitku
  // ... credentials

  @@unique([tenantId, provider])
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Technical Decisions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Centralized Services with Override Capability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some services (VPN Server, Payment Gateway) are shared by default but can be overridden per tenant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Payment Gateway Selection Logic
async function getActivePaymentGateway(provider: string, tenantId: string | null) {
  // Check tenant override flag
  const tenant = await prisma.tenant.findUnique({
    where: { id: tenantId },
    select: { useCustomPaymentGw: true }
  })

  if (tenant?.useCustomPaymentGw) {
    // Use tenant-specific gateway
    return await prisma.paymentGateway.findFirst({
      where: { provider, tenantId, isActive: true }
    })
  }

  // Fallback to HEAD (central) gateway
  return await prisma.paymentGateway.findFirst({
    where: { provider, tenantId: null, isActive: true }
  })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Domain-Based Tenant Detection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Public-facing pages (e-voucher store, customer portal) auto-detect tenant from domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export async function GET(req: NextRequest) {
  const host = req.headers.get('host') || ''
  const domain = host.split(':')[0].toLowerCase()

  const tenant = await prisma.tenant.findFirst({
    where: {
      OR: [
        { domain: domain },
        { alternativeDomain: domain }
      ],
      isActive: true
    }
  })

  // Filter data by detected tenant
  const profiles = await prisma.hotspotProfile.findMany({
    where: { 
      tenantId: tenant?.id,
      eVoucherAccess: true 
    }
  })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Webhook Handler with Tenant Auto-Detection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Single webhook endpoint serves all tenants - auto-detects from invoice/order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Centralized webhook: /api/payment/webhook
export async function POST(req: NextRequest) {
  // 1. Detect payment gateway
  const gateway = detectGateway(req)

  // 2. Extract order ID
  const orderId = extractOrderId(body, gateway)

  // 3. Auto-detect tenant from invoice/order
  let tenantId: string | null = null

  if (orderId.startsWith('EV')) {
    // E-voucher order
    const order = await prisma.hotspotVoucherOrder.findFirst({
      where: { orderNumber: orderId },
      select: { tenantId: true }
    })
    tenantId = order?.tenantId || null
  } else {
    // Invoice payment
    const invoice = await prisma.invoice.findUnique({
      where: { paymentToken: orderId },
      select: { tenantId: true }
    })
    tenantId = invoice?.tenantId || null
  }

  // 4. Get tenant-aware payment gateway config
  const gatewayConfig = await getActivePaymentGateway(gateway, tenantId)

  // 5. Verify signature &amp;amp; process payment
  // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Development Journey 📅
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Day 1: Core Foundation (12 hours)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Morning (4 hours):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Set up Next.js 15 project with Prisma&lt;/li&gt;
&lt;li&gt;✅ Design multi-tenant database schema&lt;/li&gt;
&lt;li&gt;✅ Implement authentication (NextAuth with dual admin types)&lt;/li&gt;
&lt;li&gt;✅ Create SuperHead dashboard for tenant management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Afternoon (4 hours):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ PPPoE user management UI&lt;/li&gt;
&lt;li&gt;✅ FreeRADIUS integration for authentication&lt;/li&gt;
&lt;li&gt;✅ Mikrotik RouterOS API integration&lt;/li&gt;
&lt;li&gt;✅ Real-time session monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Evening (4 hours):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Payment gateway integration (Midtrans &amp;amp; Xendit)&lt;/li&gt;
&lt;li&gt;✅ Invoice generation system&lt;/li&gt;
&lt;li&gt;✅ Webhook handler (centralized, tenant-aware)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Day 2: Integration &amp;amp; Polish (12 hours)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Morning (4 hours):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ WhatsApp notification system with load balancing&lt;/li&gt;
&lt;li&gt;✅ Hotspot voucher generation&lt;/li&gt;
&lt;li&gt;✅ E-voucher public store with payment&lt;/li&gt;
&lt;li&gt;✅ VPN client management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Afternoon (4 hours):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Financial tracking (Keuangan module)&lt;/li&gt;
&lt;li&gt;✅ Multi-tenant payment gateway override&lt;/li&gt;
&lt;li&gt;✅ Domain-based tenant detection&lt;/li&gt;
&lt;li&gt;✅ Tenant-specific redirect URLs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Evening (4 hours):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Production deployment&lt;/li&gt;
&lt;li&gt;✅ Testing with real Mikrotik routers&lt;/li&gt;
&lt;li&gt;✅ Live payment testing&lt;/li&gt;
&lt;li&gt;✅ Bug fixes &amp;amp; optimizations&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How Qoder AI Accelerated Development 🚀
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Architectural Guidance&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When designing the multi-tenant architecture, I asked Qoder about the VPN server pattern from a previous project. It remembered the implementation and suggested:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Om, untuk VPN server bisa pakai pattern centralized dengan tenant override capability. VPN Server dibuat shared (tenantId = null), tapi setiap tenant punya VPN Client sendiri."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This saved hours of architectural decision-making.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Code Generation with Context&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Qoder didn't just generate isolated code snippets. It understood the entire codebase structure:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Me:&lt;/strong&gt; "Add payment gateway override toggle in admin panel"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Qoder generated:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✅ API endpoint: &lt;code&gt;/api/tenant/payment-gateway-override&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;✅ Database query with proper tenant filter&lt;/li&gt;
&lt;li&gt;✅ UI component with toggle switch&lt;/li&gt;
&lt;li&gt;✅ Integration with existing payment flow&lt;/li&gt;
&lt;li&gt;✅ Updated helper functions to respect override flag&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All in one response, fully integrated with existing code!&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Bug Hunting &amp;amp; Fixes&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When I encountered a Prisma error with compound unique keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Type '{ provider: string; }' is not assignable to type 'PaymentGatewayWhereUniqueInput'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Qoder immediately identified the issue and provided the fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Before (broken)
where: { provider: 'midtrans' }

// After (fixed)
where: {
  tenantId_provider: {
    tenantId: tenantId,
    provider: 'midtrans'
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. &lt;strong&gt;Production Best Practices&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Qoder proactively suggested production improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Add &lt;a&gt;tenantId&lt;/a&gt; index for faster queries"&lt;/li&gt;
&lt;li&gt;"Use transaction for payment webhook to avoid race conditions"&lt;/li&gt;
&lt;li&gt;"Implement exponential backoff for WhatsApp API retries"&lt;/li&gt;
&lt;li&gt;"Add domain validation before tenant creation"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These weren't things I asked for - Qoder anticipated production needs!&lt;/p&gt;




&lt;h2&gt;
  
  
  Code Examples 💻
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Example 1: Tenant-Aware API Endpoint
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/api/pppoe/users/route.ts
export async function GET() {
  const session = await getServerSession(authOptions)

  // Build tenant filter
  const tenantWhere = session.user.role === 'HEAD_ADMIN'
    ? {}  // HEAD sees all tenants
    : { tenantId: session.user.tenantId }  // Branch sees only their data

  const users = await prisma.pppoeUser.findMany({
    where: {
      ...tenantWhere,
      status: 'ACTIVE'
    },
    include: {
      profile: true,
      router: true
    }
  })

  return NextResponse.json({ users })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example 2: WhatsApp Load Balancing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// lib/whatsapp-service.ts
async function selectProvider(tenantId: string | null): Promise&amp;lt;WhatsAppProvider&amp;gt; {
  const providers = await prisma.whatsappProvider.findMany({
    where: {
      tenantId: tenantId,
      isActive: true
    }
  })

  // Calculate current usage for each provider
  const now = new Date()
  const oneMinuteAgo = new Date(now.getTime() - 60000)

  const providerUsage = await Promise.all(
    providers.map(async (provider) =&amp;gt; {
      const recentCount = await prisma.whatsAppHistory.count({
        where: {
          providerId: provider.id,
          createdAt: { gte: oneMinuteAgo }
        }
      })

      return {
        provider,
        usage: recentCount,
        capacity: provider.maxPerMinute
      }
    })
  )

  // Select provider with lowest usage ratio
  return providerUsage
    .filter(p =&amp;gt; p.usage &amp;lt; p.capacity)
    .sort((a, b) =&amp;gt; (a.usage / a.capacity) - (b.usage / b.capacity))[0]
    ?.provider
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Results &amp;amp; Impact 📊
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Production Metrics (After 1 Month)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;6 Tenants&lt;/strong&gt; actively using the system&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;500+ PPPoE Users&lt;/strong&gt; managed across tenants&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1,000+ Invoices&lt;/strong&gt; processed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;10,000+ Hotspot Vouchers&lt;/strong&gt; generated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;100% Payment Success Rate&lt;/strong&gt; (webhook handling)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;99.9% Uptime&lt;/strong&gt; (real-time monitoring with alerts)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Business Value
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Rapid Deployment:&lt;/strong&gt; ISP branches can onboard in &amp;lt; 5 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Savings:&lt;/strong&gt; No per-tenant infrastructure needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized Management:&lt;/strong&gt; HEAD office monitors all branches&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility:&lt;/strong&gt; Branches can customize payment gateway &amp;amp; WhatsApp&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; Add unlimited tenants without code changes&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Technical Achievements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Complete tenant isolation (data, domains, services)&lt;/li&gt;
&lt;li&gt;✅ Real-time FreeRADIUS integration&lt;/li&gt;
&lt;li&gt;✅ Multi-gateway payment processing&lt;/li&gt;
&lt;li&gt;✅ Automated WhatsApp notifications with load balancing&lt;/li&gt;
&lt;li&gt;✅ VPN-based router management&lt;/li&gt;
&lt;li&gt;✅ Public e-voucher store with payment&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Lessons Learned 🎓
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Worked Amazingly Well
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Qoder's Memory Feature&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The ability to continue conversations across sessions was a game-changer. I could work in short bursts (2-3 hours) and Qoder always picked up exactly where we left off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Multi-Tenant from Day 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Designing for multi-tenancy from the start (even for a single client) proved invaluable. When new branches wanted to use the system, it was just "create tenant" - done!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Centralized Services with Override&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The VPN Server and Payment Gateway pattern (shared by default, override per tenant) gave perfect balance between simplicity and flexibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Domain-Based Tenant Detection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Auto-detecting tenant from domain eliminated configuration complexity for public-facing features (e-voucher store, customer portal).&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenges &amp;amp; Solutions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Challenge 1: Prisma Compound Unique Keys&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Issue:&lt;/strong&gt; Couldn't use &lt;code&gt;where: { provider }&lt;/code&gt; due to &lt;code&gt;@@unique([tenantId, provider])&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Use compound key syntax or &lt;code&gt;findFirst&lt;/code&gt; with full where clause&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lesson:&lt;/strong&gt; Always check Prisma schema for compound uniques!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Challenge 2: FreeRADIUS Restart Required&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Issue:&lt;/strong&gt; Router changes didn't reflect until FreeRADIUS restarted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Automated &lt;code&gt;sudo systemctl restart freeradius&lt;/code&gt; after NAS changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lesson:&lt;/strong&gt; External service integration needs automation!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Challenge 3: Webhook Tenant Detection&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Issue:&lt;/strong&gt; Single webhook URL for all tenants - how to know which tenant?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Auto-detect from invoice/order → get tenant-specific config&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lesson:&lt;/strong&gt; Design webhooks for multi-tenancy from the start!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Qoder Pro Tips
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use Memory Wisely:&lt;/strong&gt; Tell Qoder to remember important architectural decisions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Show Context:&lt;/strong&gt; Share relevant code when asking for changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterate in Chunks:&lt;/strong&gt; Break features into smaller tasks for better results&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask for Explanations:&lt;/strong&gt; Understanding the "why" helps catch issues early&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  5. &lt;strong&gt;Production Mindset:&lt;/strong&gt; Ask about error handling, edge cases, security
&lt;/h2&gt;

&lt;h2&gt;
  
  
  What's Next? 🚀
&lt;/h2&gt;

&lt;p&gt;The system is live and stable, but there's always room for improvement:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Planned Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📊 Advanced analytics dashboard (revenue trends, user growth)&lt;/li&gt;
&lt;li&gt;🤖 Automated customer onboarding via chatbot&lt;/li&gt;
&lt;li&gt;📱 Mobile app for admins and agents&lt;/li&gt;
&lt;li&gt;🔔 Telegram bot for real-time alerts&lt;/li&gt;
&lt;li&gt;🌐 Multi-language support&lt;/li&gt;
&lt;li&gt;💾 Automated database backups with Telegram integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Technical Improvements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis caching for session data&lt;/li&gt;
&lt;li&gt;Elasticsearch for invoice search&lt;/li&gt;
&lt;li&gt;Webhook retry queue with Bull&lt;/li&gt;
&lt;li&gt;API rate limiting per tenant&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion 🎯
&lt;/h2&gt;

&lt;p&gt;Building a production-ready ISP billing system in 2 days seemed impossible. With Qoder AI, it became reality.&lt;/p&gt;

&lt;p&gt;The key wasn't just AI-generated code - it was having an AI assistant that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Remembered&lt;/strong&gt; our architectural decisions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understood&lt;/strong&gt; the business context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Suggested&lt;/strong&gt; production-ready patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caught&lt;/strong&gt; errors before they hit production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stayed consistent&lt;/strong&gt; across long development sessions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For developers building complex, multi-tenant systems - Qoder's memory feature is a superpower. It's like pair programming with someone who never forgets, never gets tired, and always has production best practices in mind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Huge thanks to Nathan Steel and the Qoder team for building such an incredible tool!&lt;/strong&gt; 🙏&lt;/p&gt;




&lt;h2&gt;
  
  
  Links &amp;amp; Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project Type:&lt;/strong&gt; ISP Billing &amp;amp; Management System&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tech Stack:&lt;/strong&gt; Next.js 15, Prisma, FreeRADIUS, Mikrotik&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment:&lt;/strong&gt; Production (6 active tenants)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development Time:&lt;/strong&gt; 48 hours&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Assistant:&lt;/strong&gt; Qoder AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Want to discuss multi-tenant architecture or Qoder tips?&lt;/strong&gt; Drop a comment below! 👇&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; #qoder #nextjs #typescript #prisma #multitenant #casestudy #isp #billing #production&lt;/p&gt;

</description>
      <category>qoder</category>
      <category>nextjs</category>
      <category>typescript</category>
      <category>casestudy</category>
    </item>
  </channel>
</rss>
