← Back

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.

Next.js 16React 19TypeScriptTailwind CSSSupabasePostgreSQLQuickBooks Online APIIntuit OAuth 2.0ZodReact Hook FormWeb Crypto APIVercel
Belles & Beaux program ↗public program page — not the admin system
Odessa Symphony Guild Belles & Beaux public program page
Belles & Beaux admin roster

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

QuickBooks OAuth 2.0

Full Intuit OAuth flow: authorization endpoint, callback handler, token exchange, and secure storage. Tokens encrypted at rest with AES-256-GCM using environment-managed keys. Automatic access token refresh on expiry, including refresh-token expiry handling.

Webhook-driven paid status

When payment clears in QuickBooks, a signed HMAC-SHA256 webhook fires and updates the student's record in Supabase. Payment events don't contain Invoice IDs directly — the handler traverses Payment.Line[].LinkedTxn[] to find the linked invoice, then matches against the stored qb_invoice_id. The handler also detects and supports Intuit's CloudEvents envelope format alongside the legacy eventNotifications structure, covering their mandatory 2026 migration.

Edge-compatible session security

Admin session validation originally used Node.js crypto — which breaks on the Next.js Edge runtime. Rewrote HMAC-SHA256 verification using Web Crypto API (crypto.subtle) for full Edge middleware compatibility. httpOnly, sameSite=strict, secure cookies with 8-hour bounded lifetime.

3-step enrollment flow

Student registration with React Hook Form + Zod step-aware validation. Dynamic guardian system (1–4 guardians) with useFieldArray, relationship selection, and a "same address as Guardian 1" checkbox that auto-copies fields. Zod superRefine enforces cross-field rules: at least one guardian email required for invoice delivery.

Live dues and reCAPTCHA

Dues and late fees loaded from a settings API at runtime with resilient hardcoded fallback. Grade-aware membership logic auto-assigns freshman pricing. Google reCAPTCHA v2 on the client with server-side token verification to prevent bot submissions.

Legal consent and e-signature

Media release authorization embedded directly into the signup flow — capturing guardian consent, social media opt-out, e-signature, and submission timestamp. Stored per-student alongside the enrollment record.

Graceful degradation

QuickBooks failures at any stage — invoice creation, customer lookup, payment link generation — do not block student registration persistence. The enrollment saves to Supabase first; QB operations are best-effort with logged errors and retry paths.

Admin tooling

Real-time roster with search, grade filter, paid/unpaid filter, KPI summary cards, and one-click payment status toggle. Print-optimized layout with hidden controls. Pricing panel for 6 configurable dues/fee keys with live updates reflected immediately on the public join form.

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

01

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.

02

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.

03

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.

04

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.

← Back to portfolioBelles & Beaux program ↗