Project deep dive
Belles & Beaux Platform
A production QuickBooks Online integration and student registration system built for a real performing arts organization — not a demo, not a tutorial. Designed, architected, and shipped to handle real dues payments and automate the entire signup-to-paid workflow against a fixed real-world deadline.
Overview
The Odessa Symphony Guild's Belles & Beaux program needed a way for incoming students and their parents to register and pay dues online. The existing process was manual — paper forms, chased invoices, no automation. The replacement needed to be live and working for one specific event: the program's parent and student meeting, where signups were expected to happen that night.
That deadline wasn't a soft target. It was a room full of parents with phones in their hands.
The system handles the full workflow end to end. A student fills out a multi-step registration form, a QuickBooks invoice is created automatically in the background, the parent is redirected to QuickBooks' hosted payment page, and a signed webhook fires when payment clears — marking the student as paid in the roster without anyone touching a dashboard. If a parent exits before paying and returns days later via the invoice email, the webhook still fires and the roster still updates automatically.
Features
What it does
Technical Highlights
Under the hood
Security
- —AES-256-GCM encryption for QuickBooks OAuth tokens at rest
- —HMAC-SHA256 webhook signature verification with timingSafeEqual (timing-safe comparison)
- —Web Crypto API (crypto.subtle) for Edge-compatible admin session validation
- —httpOnly, sameSite=strict, secure session cookies with 8-hour lifetime
- —All /admin/* routes protected via Next.js middleware with Edge-compatible session verification
- —Secure response headers: no-cache, nosniff, DENY framing on all sensitive API routes
Database
- —Supabase migration set covering students, encrypted QB tokens, settings, guardian expansion, and media-release fields
- —Flexible 1–4 guardian arrays mapped to 36 relational DB columns with zero data loss for existing records
- —Student records scoped by school year for clean multi-year data isolation
- —Backward compatibility: supports both new guardian_N_* columns and legacy mom_*/dad_* records in display
- —Supabase service role key for trusted server-side operations; RLS preserved for public routes
QuickBooks
- —Dual-format webhook handler: runtime detection of legacy eventNotifications vs CloudEvents envelope — both paths normalize to the same downstream logic, making the Intuit portal toggle the deployment mechanism rather than a code deployment
- —Customer find-or-create flow keyed by primary email to avoid duplicate payer records
- —Invoices with due dates, line items, and ACH/credit-card online payment enabled (AllowOnlinePayment: true)
- —Multi-recipient invoice delivery for additional guardian email addresses
- —intuit_tid correlation IDs logged on all QB API calls for production debugging
- —MOCK_PAYMENT_MODE feature flag with singleton mock QB client — full local dev without live credentials
Frontend
- —React Hook Form + Zod with step-aware validation and progressive completion UX
- —useFieldArray for dynamic guardian management with add/remove and conditional field copying
- —Zod superRefine for cross-field business rules across dynamic arrays
- —Live phone number formatting during keypress for student and guardian inputs
- —Live dues loaded from settings API with resilient hardcoded fallback
Engineering Challenges
Where it got interesting
Navigating Intuit's production approval process against a hard deadline
The deadline wasn't a project milestone — it was a parent and student meeting where the system needed to be live and working that night. Intuit's production app review process, however, is sequential: one issue is surfaced per round, a rejection is sent, and the reviewer waits for resubmission before looking further. The first rejection arrived with no explanation — a follow-up email was required just to find out what needed fixing. Subsequent rounds surfaced a legal counsel review requirement and two separate reCAPTCHA flags. Approval came through in time. Getting there required treating every resubmission as the last available opportunity — proactively documenting compliance and anticipating what a reviewer might flag next rather than waiting to be told.
QuickBooks webhook reliability
QuickBooks does not reliably fire Invoice Update events when a payment is applied. Relying on Invoice events alone would mean payments that go through in QuickBooks silently fail to update the roster. The solution listens primarily for Payment Create and Update events — when one arrives, a secondary QBO API call fetches the payment record, traverses the linked transaction list, and extracts the invoice ID. That ID is matched against the stored qb_invoice_id in Supabase. The Invoice Update path is retained as a secondary route but is not the primary mechanism.
Edge runtime crypto rewrite
The original admin session validation used Node.js crypto module, which is unavailable in the Next.js Edge runtime. Middleware runs on the Edge, so all Node APIs break silently or throw. The fix was a full rewrite to Web Crypto API (crypto.subtle) — same algorithm, different API surface, with async/await throughout since crypto.subtle is promise-based unlike the synchronous Node version.
Mandatory platform migration with a live production system
Intuit mandated that all webhook consumers migrate from their legacy payload format to the CloudEvents envelope structure. The legacy format wraps events in an eventNotifications object; the CloudEvents format is a top-level JSON array where entity name and operation are encoded in a type field like qbo.payment.created.v1. Rather than a hard cutover, the handler was updated to detect the format at runtime and route to the appropriate parser — both paths normalize to the same downstream logic. The portal toggle in the Intuit Developer Portal becomes the deployment mechanism: it can be flipped on, tested, and rolled back without touching code.

