Inspiration

Mission shopping — preparing for a new baby, moving into a dorm, gearing up for a camping trip — is one of the most expensive, highest-stakes purchases people make, and it's miserable. Every item is something you've never bought before, so you research each one for hours. You forget essentials. You buy in the wrong priority order. And you almost always blow the budget, because no existing tool optimizes the whole list — every shopping app cares about one item at a time, and generic checklists on the internet don't know your budget and can't buy anything for you.

When we brainstormed ideas for the PayPal AI Hackathon, we kept circling back to one question: what if the budget was the first-class citizen, not an afterthought? Not "find me a cheap stroller" but "here is $500 and a baby arriving in six weeks — give me the complete, correct, safe list, allocate my money where it matters, and let me pay for all of it in one PayPal checkout." That inversion — from per-item deals to whole-list budget optimization — became MissionBudget.

What it does

MissionBudget turns a mission + a budget into a ready-to-buy plan through a three-step agentic pipeline — the AI plans the list, shops the catalog, and allocates the money, then PayPal takes it from there:

  1. A complete AI-generated item list — every item carries a tier (Essential / Important / Optional) and a plain-language reason for being there, bilingual (English/Vietnamese) in a single LLM call.
  2. Real products — items are matched to live offers via the Channel3 product API (100M+ products), with a curated-catalog fallback so the demo never dies.
  3. Whole-list budget optimization — a deterministic "tiered water-filling" allocator decides which variant of which item fits your budget: safety-critical essentials get upgraded first, nice-to-haves get dropped last, and nothing essential is ever silently sacrificed.

The signature moment is the budget slider: drag the budget and the entire list re-optimizes in under two seconds — items upgrade, downgrade, drop and re-enter with animated diff chips and a human-readable explanation of every change. Then the whole list becomes one PayPal order — one-click checkout with the Orders API, Pay in 4 messaging when money is tight, and webhook-confirmed fulfillment.

Around that core: guest-first flow with optional email/Google sign-in, a missions module (filter, edit, reschedule, full History tab), and a Market where any Channel3 product can be bought standalone or folded back into a mission.

How we built it

Spec-first, contract-first. Before any feature code, we froze a 15-section product spec, an architecture document with 8 ADRs, a 12-endpoint API contract (E1–E12), a design system, and screen specs for all 7 screens. A team of specialized AI agents (architect, backend, AI core, designer, frontend, QA, scribe) coordinated through a shared worklog against those frozen contracts — eight days and 125+ commits in: ~59,000 lines of application TypeScript plus ~6,200 lines of tests, 242 tests green.

Stack. Next.js 16 (App Router) + React 19 + TypeScript + Tailwind CSS 4 + shadcn/ui, Prisma over PostgreSQL on Render, deployed as a standalone build. PayPal Orders API v2 with OAuth2 client credentials and an in-memory token cache, JS SDK buttons, signature-verified webhooks (dedup on a unique event id), Pay Later messaging. GLM (Z.ai) as the LLM behind a provider interface. Channel3 for product search; KERNEL cloud-browser for async stock verification with graceful fallback.

The one architectural rule that shaped everything: LLM writes content, code does the money math. The LLM generates the item list, reasons, and summaries — creative, fuzzy, language-shaped work. The allocator that decides where every cent goes is a pure function with zero I/O and no LLM in the loop. That's what makes the slider instant, deterministic, unit-testable, and auditable — you can't have a drag interaction call a language model and still feel live.

The optimizer: tiered water-filling. Think of the budget as water poured into tiered basins. Given budget (B) (integer cents) and items (i) with tier (t_i \in {1,2,3}), safety flag (\sigma_i), quantity (q_i) and offers (p_{ij}):

Phase A — cover essentials. Every tier-1 item gets its cheapest variant:

$$ A = \sum_{i \in T_1} q_i \min_j p_{ij} $$

If (A > B), we never silently drop a safety-critical item — we surface a Mission Gap: shortfall (A - B), plus a Pay-in-4 option at (\lceil A/4 \rceil) per installment.

Phase B — cover lower tiers in value order. Tier-2 then tier-3 items, sorted by ascending minimum price. Each item earns a drop threshold — the minimum budget at which phases A+B would pick it up again: everything ahead of it in the coverage order, plus its own minimum:

$$ \theta_k = A + \sum_{\ell \preceq k} \min p_\ell $$

Drop thresholds are what make the slider feel intelligent: raise the budget past (\theta_k) and item (k) automatically re-enters the list, with its threshold shown on the card. The math predicts the future of the list.

Phases C & D — upgrade pass. Remaining budget is spent upgrading items to better variants, greedily by value per dollar:

$$ u_i = \frac{3\sigma_i + w_{t_i} + g_i}{\Delta p_i} $$

where (w_1 = 0.25), (w_2 = 0.15), (w_3 = 0.05) (tier weights), (g_i = 0.05) for a Budget→Standard step and (0.025) for Standard→Premium (diminishing returns). The (3\sigma_i) term is the whole point: for a baby mission, the car seat upgrade scores (\frac{3.30}{65} \approx 0.051) points per dollar and beats the towels at (\frac{0.20}{5} = 0.040) — safety wins even at (13\times) the price delta. On the golden $650 example: after covering the list at the cheapest tier, the allocator spends exactly $132 in value order — smoke detector +$20, mattress +$30, car seat +$65, towels +$5, swaddles +$6, burp cloths +$6 — landing at $648 spent, $2 left. Every spec example like this one is locked in as a golden unit test.

Challenges we faced

1. An optimizer that must be instant, explainable, and never wrong. The slider re-optimization had a hard <2s budget including network and animation — impossible with an LLM in the loop, and irresponsible with a non-deterministic one. Pure-function water-filling computes a full re-allocation in milliseconds; the golden-example tests pin its behavior to the cent.

2. Floating-point money vs. PayPal's item_total. PayPal rejects an order whose line items don't sum exactly to the total. Binary floats made that a lottery. Our ADR-008: integer cents everywhere — database, API, allocator — converted to strings only at the PayPal and UI boundaries. Zero rounding bugs since.

3. Getting LLM output you can trust. The list generator must return strict JSON, bilingual, schema-exact — and LLMs love markdown fences and creative field names. We wrapped every call in a completeJSON pipeline: strip fences → parse → zod-validate → on failure, one repair retry that feeds the zod error back to the model → hard LLMFormatError beyond that, with timeouts and a fallback catalog so the product still works when AI doesn't.

4. Racing an async pipeline against its own timeout. Mission generation runs three async steps with a global 90s timeout — so "timeout" and "finished" can race. Terminal states are written with conditional updates (WHERE status = 'GENERATING'), guaranteeing exactly one winner between FAILED and OPTIMIZED, plus double-run guards that survive hot reloads.

5. Producing a sub-3-minute demo video — without a film crew. Recording the whole app flow in one browser session OOM'd the recorder, so we switched to segmented recording stitched with ffmpeg. The narration went through three lessons: a voice that read our English script beautifully turned out to measure at 266 Hz — a child-like pitch that undercut the product's trust story. We measured seven candidate voices with librosa (pyin F0 tracking), found exactly one true baritone (130 Hz), regenerated all 16 narration segments with per-segment adaptive speed, and time-fitted each one with atempo to preserve the approved pacing — then replaced only the audio track (-c:v copy), keeping the approved video bit-exact and re-synthesizing the deterministic, seeded BGM/SFX with measured correlation of 1.000 against the old mix.

What we learned

  • Put AI where it's strong, and keep it away from where it's dangerous. Content generation: LLM. Money arithmetic: deterministic code. This split made the product faster, testable, and explainable — and honestly, it's the pitch.
  • Golden examples beat requirements. Freezing three worked examples ($500/$650/$300 baby missions) in the spec and reproducing them in unit tests caught every regression the slider ever met.
  • Measure, don't guess. Voice "too childish" became a number (266 Hz → 130 Hz). "Choppy narration" became an ASR word-match score. "Deployed correctly" became a byte-level comparison of the live video against the local file. Numbers ended every argument.
  • Contracts are what let a large team (of humans and agents) move in parallel without stepping on each other — the API contract and design tokens were the two documents that made 125+ merge-safe commits possible.
  • Graceful degradation is a demo-survival skill. Channel3 rate-limits, cold image optimizers, flaky stock checks — every third-party dependency has a fallback path, because a hackathon demo gets exactly one take.

What's next

Take payments beyond the sandbox; add more mission templates (streaming setup, first apartment, travel); watch prices between mission creation and checkout and re-optimize when offers move; and collaborative missions — friends and family contributing to one shared budget, paid in one PayPal order.

Built With

Share this project:

Updates

Submission history