06 · Food Tech · Marketplace
Rescuing a broken Stripe layer, then building the marketplace around it.
Seven months, one food vertical turned into six. I was hired to fix the checkout on a Bubble.io marketplace where the previous developer had said it was done. It was not. The rebuild covered the payments, the escrow release flow, the transactional email layer, and the onboarding that grew the platform from one food vertical to six.
Hired to fix one thing, stayed for seven months
FoodIWant is a two-sided marketplace for the food and hospitality industry. On one side, food-service professionals (line cooks, servers, dining-experience pros, caterers, event staff) post profiles and browse gigs. On the other side, restaurants and food businesses post jobs, hire talent, run contracts, and pay through the platform. There is also a consumer-facing layer for meal specials and restaurant promotions. Built on Bubble.io.
The founder hired me from Upwork in June 2023 for a small, scoped problem: the Stripe checkout did not work. His previous developer had handed the project over as “done.” It was not done. Payments would not collect; the transactional email layer was broken too. The brief was narrow on purpose: fix the payment collection issue so customers could actually pay, and we would talk about the rest after.
I fixed it. The conversation about the rest started the same week and ran until January 2024.
The two sides of the marketplace, captured on one signup screen. The vertical of “Hire” expanded over the engagement from restaurants to caterers, event organizers, and the general “other” food businesses.
What got scoped to me, in three phases
The work split into three phases across the engagement. The first was the original brief. The second and third were the work that grew out of doing the first one well.
- Phase 1 (weeks 1 to 3): The Stripe rescue. Diagnose why the Bubble.io to Stripe handoff was failing. Get checkout collecting payment correctly. Get the webhook layer wired so the Bubble database stayed in sync with Stripe events. Fix the transactional email layer (Sendinblue at the time) that was silently failing the same way.
- Phase 2 (months 2 to 4): The escrow build. The marketplace needed a payment-holding mechanism so customers paid up front, funds sat with the platform, and the vendor was paid only after the customer confirmed the work was complete. Conceptually the basic shape of how Upwork holds money before releasing it to a freelancer. Built on Stripe Connect with separate authorize and capture, hold periods on the platform account, and release flows triggered by client confirmation.
- Phase 3 (months 4 to 7): Multi-vertical expansion. The product started restaurant-only. Over the engagement it expanded to multiple food-industry verticals (front of house, back of house, events and private hire, virtual assistants, sales and marketing, writing and content creation), each with its own onboarding fields, rate model, and dashboard variant. The architecture had to absorb new verticals without a rewrite per addition.
The talent feed inside the marketplace, post-expansion. Each category had its own onboarding flow and dashboard variant; the architecture absorbed new verticals without per-vertical rewrites.
The Stripe rescue: webhook + checkout diagnostic.
The fastest way to fix a broken Bubble.io to Stripe integration is to stop assuming the integration is the problem and start checking the seams. The seams are: the API Connector call configuration on the outbound side, the Stripe webhook receiver workflow in Bubble’s backend workflows, the parameter shape Stripe sends back, and the Bubble database fields the workflow writes to.
The version I inherited had a mix of issues across all four seams. The API Connector calls had hardcoded test-key endpoints in places that should have been live. The webhook receiver was registered for the wrong event types. The workflow that processed checkout.session.completed was looking for parameter paths that did not match Stripe’s actual payload structure. The Bubble user record was not getting the Stripe customer ID written back, which meant every subsequent action was orphaned.
The rescue was a careful walk through every seam, fixing each one, and verifying with the Stripe Dashboard’s webhook delivery log that events were landing and the right Bubble workflows were firing. Boring detective work. Two weeks of it took the checkout from “broken in production” to “collecting payment cleanly on the first attempt.”
Email: rescue Sendinblue first, then migrate to Postmark.
The transactional email layer (signup confirmations, password resets, order receipts, contract notifications) was failing the same way the Stripe layer was failing. The original integration was Sendinblue: the API Connector calls had been configured against a since-rotated key, and the workflow triggers were pointed at the wrong events. The user-facing symptom was identical to the Stripe one: the happy path looked fine to the founder, but anyone who actually signed up never got the confirmation email and could not complete onboarding. The first fix was the same pattern as the Stripe rescue. Audit every send point. Re-key against a current credential. Re-test every trigger end to end.
Once Sendinblue was reliable inside the app, the deliverability outside the app was the next issue. Sendinblue’s bounce rates on transactional sends were higher than the marketplace needed for password-reset and contract-notification emails (the two paths where a missed email blocks a user from completing an action). We evaluated the alternatives and migrated the platform to Postmark, which is built for transactional and historically posts the lowest bounce rates among the major providers. The migration was straightforward (Postmark’s API surface is small and the Bubble.io API Connector calls were one-for-one swaps), but the deliverability win was real and immediate.
Payment escrow, in the basic shape of how Upwork holds money.
Once the rescue work was done and trust was earned, the founder wanted the harder feature: a payment-holding flow so customers paid up front, funds sat with the platform, and the vendor was paid only after the customer confirmed the work was complete. The marketplace had work-hour contracts and gig-based contracts; both needed the same release mechanism.
The architecture used Stripe Connect’s separate-charges-and-transfers pattern. The customer paid the platform account at checkout (charge created, funds settled into the platform’s Stripe balance). The vendor was set up as a connected account during onboarding through Stripe’s hosted onboarding flow. When the customer marked the work complete inside the Bubble app, a backend workflow fired a Stripe Transfer from the platform balance to the connected account, less the marketplace fee. Disputes paused the release; refunds reversed the charge and the held balance returned to the customer.
This is the same conceptual shape as Upwork’s basic escrow model. The implementation was a few weeks of work: the Stripe Connect onboarding flow, the backend workflows for charge/release/refund, the customer-side confirmation UI, the vendor-side payout visibility, and the dispute hold logic. Boring infrastructure that turned the marketplace from “we move money” to “we hold money trustworthily.”
The vendor-side reports view. Work in progress, paid, pending, referral commission, hours worked. All driven by the Stripe Connect transfers and the escrow release logic underneath.
Multi-vertical expansion, scaled to the founder's industry-specific vision.
The product launched restaurant-focused. Over the back half of the engagement the founder wanted to add five more verticals: back-of-house roles, events and private hire, virtual assistants for food businesses, sales and marketing, and writing and content creation. Each vertical had its own onboarding fields (a line cook’s profile is not a content writer’s profile), its own rate model (hourly versus per-piece versus seasonal), and its own dashboard variant. The founder had specific, industry-informed ideas about what each onboarding should ask. My job was to implement those ideas in a shape that would absorb the next five without a rewrite each time.
The naive approach is to duplicate the onboarding flow, the profile data type, and the dashboard for each vertical. That is a per-vertical rewrite, and the product would have been five times the maintenance surface in three months. We did not go that way.
The architecture used a Bubble Option Set for the vertical types, with each vertical option carrying its own configuration (allowed rate models, required onboarding fields, dashboard variant key). The onboarding flow read the selected vertical from the Option Set at runtime and rendered the right field set. The dashboard used Conditional Visibility on the vertical-specific sections. Adding a new vertical was a row edit in the Option Set plus a few new field definitions, not a copy-pasted flow.
The same pattern showed up across the platform: experience-level tiering (Semi-Pro to Super User), business-type registration (Restaurant, Caterer, Event Organizer, Other), and job-type filtering (On-Demand, Ongoing, Seasonal, Event) all ran off Option Sets read at runtime rather than per-variant hardcoded UI. The freelancer-facing job filters that let a back-of-house worker filter out front-of-house roles ran off the same Option Set machinery, so the search UX matched the founder’s intent without per-vertical query branches.
Step one of the job-post wizard. Category selection comes first because everything downstream (rate band, visibility audience, expertise filter) keys off the category the poster picks.
The Visibility step of the same wizard. Distribution controls a restaurant owner expects from a marketplace, layered over the escrow and Option-Set foundation.
The Budget step. Experience tiers and rate models both ran off the same Option Set machinery, so adding a vertical was a config row rather than a new wizard.
Step seven, the Review. Every prior step’s selections collapse into a single confirm-and-submit before the post hits the talent feed.
The consumer-facing Meal Specials surface. Restaurants list deals, consumers browse by category and location. The third side of the marketplace, layered over the talent and contracts core.
The seven-month arc
The engagement ran from 3 June 2023 to 19 January 2024. Three weeks to rescue the payment layer. Three months to build the escrow flow and the email infrastructure. Three months to expand the marketplace from one vertical to six, with the onboarding and dashboard architecture that absorbed new verticals as a config change rather than a code change.
The cadence was small deploys, frequent founder demos, and tight feedback loops. The founder cared about user experience and pushed back on shortcuts that would have made the platform feel half-finished; that pressure was good engineering hygiene. The platform shipped, customers transacted through it, payments held and released correctly.
What changed
The platform is no longer live; the marketing site at foodiwant.com is offline and the application has been retired. The work shipped, the payment escrow held, the multi-vertical architecture absorbed new categories without breakage. The decision to close the product came later and was the founder’s call.
The pattern of the engagement is the part I would do again. Take the narrow brief seriously. Do the small thing well. Let the relationship grow on the back of the first deliverable. Seven months later, the work you have shipped is unrecognizable from the brief that started it.