Enterprise SaaS · ISP Sales CRM · Founding Engineer

Building a Custom SaaS Platform
From Zero to Production
In 6 Months.

A custom sales tracking CRM built for ISP field sales teams — iOS app, AI-powered OCR, real-time payroll automation, and supporting 1,000+ user accounts. Full-stack development, deployment, and production support.

1,000+ User Accounts Supported
99+ Pull Requests Shipped
6 Months to Launch
2 Apps (Web + iOS)

December 2025. A blank repo and a list of requirements.

A USA-based founder was running an ISP sales operation — dozens of field reps selling internet services across multiple vendors, each with their own CSV exports, commission structures, and payscale quirks. Tracking commissions was a spreadsheet nightmare. Paying reps was slow, error-prone, and manual.

He needed a custom CRM built for this exact world. Not Salesforce. Not a generic tracker. Something purpose-built for the ISP sales commission lifecycle — from a rep reporting a sale on their phone to an admin processing payroll on the web.

He found me. I was a final-semester CS student in Pakistan with no professional production experience, no published apps, and $0 in freelance history. What I had was deep curiosity, a bias for shipping, and the ability to learn faster than most.

"What existed in December 2025: three basic tabs. No real functionality. No mobile app. No users."
— State of the codebase on day one

We started with a focused arrangement — daily meetings, aggressive scope, and a bias for shipping. I said yes to all of it.


ISP commission tracking is surprisingly complex.

At first glance, it sounds simple: rep sells service → vendor pays company → company pays rep. But the real world is messier.

Before Custom CRM
  • Commission calculations done manually in Excel
  • Reps emailed in to report sales, no standard format
  • Vendors sent inconsistent CSV exports with missing columns
  • No way to track holdbacks or partial pay multipliers
  • Managers couldn't see their team's pending payroll
  • Duplicate customers, missing reps, silently failed imports
  • No mobile access for field sales reps
  • Zero audit trail for who changed what
After Custom CRM
  • Automated commission calculation from vendor CSV imports
  • Reps report sales on iOS app with OCR receipt scanning
  • Smart import pipeline with alias matching and validation
  • Holdback, pay multiplier, and partial commission support
  • Manager dashboard with pre-payroll review and team stats
  • Composite-key duplicate detection with review workflow
  • Native iOS app with offline capability and push notifications
  • Full activity log and role-based access across 4 dashboards

Six months of shipping. Week by week.

This was not a slow, methodical build. It was a startup sprint — daily meetings, same-day PRs, features going live while users were actively logging in. Here's the arc of how this custom CRM platform came to exist.

December 2025
Foundation & Architecture
Established the data model: multi-tenant architecture with Supabase, row-level security policies, role-based access (Admin, Accounting, Manager, Rep), vendor and product systems. First working dashboards. First real deployments to Vercel. Understanding the business domain deeply before writing any feature code.
January – February 2026
Customer & Order Management
Built the core import pipeline — CSV ingestion from multiple vendors, alias matching system, duplicate detection using composite keys. Customer pages with pagination, filters, and date ranges. Rep management with GHL (GoHighLevel) integration for automated rep creation. Security hardening with RLS policies across all tables.
March 2026
Accounting & Payroll Engine
The most complex module. Payscale creation (per-rep, per-vendor, per-product), ledger system, holdback tracking, invoice and remittance management, "Approve to Pay" workflow, "Process Orders" payroll run, check stubs with PDF generation, and pay multiplier support for partial commission payments (e.g. 50% or 70% of standard rate).
April 2026
iOS App Launch 🎉
After three App Store rejections, the app went live on the Apple App Store. The app shipped with: rep-reported sales with GPT-4 Vision OCR scanning, offline capability, push notifications for payroll events, document uploads, force-update mechanism, and role-based navigation.
May 2026
Team Management & Manager Hierarchy
Introduced the Manager role layer — team overview, team stats by rep and vendor, team check stubs with group-by-date and group-by-rep views, team payscale management (managers can set payscales for their reps but not edit their own), pre-payroll review (read-only view of all unpaid ledgers and unprocessed orders), commission transfer, deduction, and bonus forms.
June 2026
Leaderboard, Performance Tracking & Launch
Leaderboard with regional grouping, rep rankings, and gold/silver/bronze icons. Pull-through rate tracking (installs vs. sales). Last login tracking (web and mobile separately). User-guided import error handling with column validation indicators, required field enforcement, and downloadable import templates. App Store update v1.1.5. Official launch to the first major enterprise client.

The stack that made it possible.

Every technology choice was driven by one constraint: one developer, fast iteration, production stability. No compromise on either shipping speed or system quality.

Next.js 14 (App Router)
Server components, ISR, API routes. Full-stack in one repo with zero backend servers to manage.
React Native + Expo
Cross-platform iOS app with offline sync, push notifications, and App Store publishing pipeline.
Supabase + PostgreSQL
Multi-tenant database with Row Level Security, Edge Functions for background jobs, Realtime subscriptions.
GPT-4 Vision API
AI-powered OCR for receipt scanning in the mobile app — extracts sale data automatically from photos.
TailwindCSS + shadcn/ui
Consistent design system across web and app. Mobile-first responsive layouts for all 4 dashboards.
Vercel + GitHub Actions
Continuous deployment with preview URLs per PR. Production deploys on merge — typically within 2 minutes.
TypeScript PostgreSQL RLS Supabase Edge Functions Expo EAS Build TestFlight App Store Connect Stripe GoHighLevel API PDF Generation CSV Import Pipeline Push Notifications (APNs) Background Queue Processing

The bugs that taught me the most.

Production problems are different from interview problems. They have history, politics, bad data, and consequences. These are the ones I'm most proud of solving.

Problem
The Alias Key Mismatch. Rep names in vendor CSV files never matched rep names in our database. A rep named "J. Doe" in one vendor's file was "John Doe" in another. The alias system existed but was keyed on the wrong foreign key — the UUID instead of the sales_id string — so nothing matched and all imports showed "missing reps."
Solution
Built an intermediate resolution step through salesAccountMap, added a two-pass alias lookup (exact match then fuzzy), and introduced the Manage Alias button on the import page so the operations team could add vendor-specific alias names without touching the database directly.
Problem
Supabase RLS — Service Role vs Anon Key. Server-side API routes used the service role key (bypasses RLS) while the mobile app used the anon key (enforces RLS). A feature that worked perfectly on the web silently returned empty data on mobile. Took hours of debugging to identify the policy gap.
Solution
Audited every RLS policy against both key contexts. Created test accounts at each permission level and wrote a checklist for testing new features from both server-side and client-side perspectives before shipping.
Problem
Invisible Desktop Elements on Mobile. A zero-opacity div was covering the entire touchable area on mobile screens — a Tailwind breakpoint issue where a desktop-only element wasn't hidden on mobile, just invisible. Users couldn't tap anything on the affected pages. No error in the console.
Solution
Used browser DevTools pointer inspection to find the invisible layer. Added systematic mobile testing to every PR: test on actual device, not just browser resize. Created a responsive testing checklist for all new pages.
Problem
Vercel Timeout on Large CSV Imports. A Sparklight import with 5,000+ rows would hit Vercel's 10-second function limit mid-processing, leaving data in a half-imported, silent-failure state. Users saw "import complete" but only 30% of rows had processed.
Solution
Moved to chunked processing — 500 rows per chunk via a queue-based background system using Supabase Edge Functions. Added a result file (Result_{filename}.csv) that shows the import status of every row. Tested up to 10K rows successfully.
Problem
Pending Pay Data Discrepancy. The manager's Pending Pay screen in the app showed 36 records. The web showed 49. The discrepancy was systematic — not random. The app was missing 35% of actual pending records.
Solution
Root cause: app fetched orders via order_to_rep joins only, missing orders where the rep match came from the Vendor_SalesRep column fallback. Fixed with a two-pass order lookup (exact join → name-based fallback), added three-signal approval filter, null-status ledger inclusion, and pay multiplier table integration. All metrics now match between web and app.

The Pending Pay screen — a technical deep dive.

The Pending Pay module is the most architecturally complex feature in this platform. It merges two entirely different types of "money owed to reps" into a single unified view. Here's how the data flows:

Manager opens Pending Pay
        │
        ├── Step 1: Auth check (manager = true)
        ├── Step 2: Fetch team reps where payroll = true
        ├── Step 3: Build alias map (alias table → sales_id fallback → company-wide)
        │
        ├── Step 4: Fetch Ledger Entries
        │     └── ledgers WHERE paid_date IS NULL
        │           AND (status = 'Posted' OR status IS NULL)   ← critical: null = old records
        │           AND sales_id IN (payrollRepIds)
        │
        ├── Step 5: Fetch Active Payscales for team reps
        │
        ├── Step 6: Fetch payscale_logs → resolve who created each payscale
        │     └── manager_view payscales → show manager as trigger
        │     └── admin_only payscales → show admin creator as trigger
        │
        ├── Step 7: Fetch Approved Unprocessed Orders
        │     └── Order WHERE vendor IN (payscale vendor IDs)
        │           AND (approved_topay = true
        │                OR approved_to_pay_date IS NOT NULL
        │                OR approved_topay_at IS NOT NULL)       ← 3-signal filter
        │           AND pay_processed IS NULL
        │
        ├── Step 8: Build orderRepMap
        │     └── Pass 1: order_to_rep joins
        │     └── Pass 2: Vendor_SalesRep column name match     ← the missing fallback
        │
        ├── Step 9: Fetch pay_multiplier table
        │     └── amount = base_payscale_amount × multiplier
        │
        └── Merge → buildEntries() → groupByRep() / groupByVendor()
              └── Render: 3-level accordion (Rep → Vendor → Product → Customer)
        

The key insight that took longest to find: the web app used a Vendor_SalesRep column as a secondary fallback for rep matching when no order_to_rep record existed. The mobile app didn't implement this fallback, silently dropping those orders from the view.

Lesson: In production systems, the edge case is the case. Test data always matched perfectly — the bug only appeared on real production data that had gone through legacy import paths. Always test with production-representative data.

Full-stack ownership across all systems.

Web Platform
Full Next.js application. 4 role-based dashboards. 99+ GitHub PRs merged. Feature development, bug fixes, and performance optimization.
iOS App
React Native/Expo app shipped to App Store. Full pipeline from development to production releases.
AI Integration
GPT-4 Vision API for OCR receipt scanning. Prompting, error handling, form auto-fill, and mobile camera integration.
Import Pipeline
Multi-vendor CSV ingestion, alias matching, composite-key duplicate detection, chunked background processing, result file export.
Payroll Engine
Payscale system, holdbacks, pay multipliers, ledger entries, invoices, remittances, check stubs (PDF), pre-payroll manager review.
Team Hierarchy
Manager → Rep hierarchy with team stats, team check stubs, team payscale management, commission transfer/deduction/bonus forms.
Security
Row Level Security across all tables, multi-tenant data isolation, role-based permissions, accounting-only edit locks, GHL API key rotation.
Customer Support
Direct support to operations team. Rapid issue diagnosis and fixes ensuring minimal downtime for users.
99+
Pull Requests Merged
1,000+
User Accounts Supported
10K+
CSV Rows / Import (1.5 min)
<24h
App Store Approval Speed
4
Role-Based Dashboards
2+
Enterprise Clients Onboarded

What six months of daily production work actually teaches you.

You can read every blog post about distributed systems and multi-tenancy. Nothing replaces the feeling of a production bug at 2 AM with a real user waiting. Here is what I actually internalized:

1. Ship, then protect.

The fastest way to understand what matters is to watch real users break things. The import pipeline had three complete rewrites because actual production data was nothing like our test data. The spec was wrong. The users were right.

2. The edge case is the case.

Placeholder phone numbers (value: "0"), null ledger statuses from legacy entries, orders matched via Vendor_SalesRep instead of order_to_rep — every major bug was something that "shouldn't happen" but did. Defensive data handling is not optional in production.

3. The database is the source of truth. Trust it.

A rule that became mine early on: "When it comes to ledgers, match what is in the database. Always." Don't programmatically change the sign of a value. Don't assume format. Read what's there and display it faithfully.

4. User-guided errors beat silent failures.

The biggest quality-of-life improvement was adding column validation indicators, required field markers, and downloadable import templates. The operations team stopped messaging at 2 AM about failed imports. Users fixing their own data beats developers debugging their data every time.

5. Think, then agree, then build.

A principle I internalized on this project: "Let's think and talk then agree then make changes." I learned to slow down before complex features, confirm understanding explicitly, and build once rather than build, revert, rebuild.


Competing with a 2-year head start.

During development, a competitor CRM was identified in the same ISP sales space — operational since 2023, priced at $33–$100/month per account.

"This is competition to our platform." — USA Founder, after six months of development
He framed it as the competitor catching up — not the other way around. That framing matters.

The competitor had a two-year head start and an established team. This platform was built by one developer in six months with full-stack ownership. The differences emerge in how we serve this specific market:

Multi-company per rep
A single sales rep can be linked to multiple ISP vendors with one login — a core differentiator. The competitor requires separate accounts per company.
Commission depth
Holdback, pay multiplier, manager bonuses/deductions, connected ledgers, payscale inheritance — built for the actual complexity of ISP commission structures.
Native mobile app
AI-powered OCR, offline mode, push notifications — a field rep experience built for reps who are literally in the field.
Custom to your workflow
When enterprise clients needed new features, they shipped within days instead of quarters — directly benefiting from close collaboration with the engineer who built the system.

Moving from engineering outputs to business outcomes.

The real value of this platform isn't measured in lines of code or pull requests — it's measured in operational impact.

Operational Efficiency
Reduced manual payroll workload and centralized vendor reporting, eliminating spreadsheet dependency across teams.
Data Accuracy
Standardized sales submission process with built-in validation, reducing commission calculation errors to near-zero.
Manager Visibility
Real-time team performance tracking and pre-payroll review capabilities for better decision-making.
Enterprise Readiness
Full auditability, role-based access control, and compliance-grade security across all financial operations.

The journey from student to production engineer.

While the founder drove the business vision, domain expertise, and client relationships, I owned the full-stack development, system architecture, deployment pipeline, and day-to-day evolution of the platform. We moved at startup speed because enterprise clients were waiting and reps needed to be paid accurately.

This meant designing the database schema, building role-based dashboards, publishing the iOS app, integrating AI systems, and debugging production issues at any hour. It meant shipping 99+ pull requests while supporting real users in a real business.

I started this project as a final-semester student with no production experience. I'm finishing it with the kind of hands-on SaaS engineering knowledge that typically takes three to five years to accumulate. Not because I'm exceptional — but because I said yes to hard problems, shipped before I felt ready, and learned from every production bug rather than treating them as failures.

6 Months to Production
99+ Features Shipped
Full-Stack End-to-End Ownership

Other systems I've built.

Each project pushed me into different engineering territory. Here's what else I've shipped — all production, all end-to-end.

Need someone who can build your entire technical stack?

I work best with founders who value speed, ownership, and long-term collaboration. Let's talk.