Protect the couple
Do not treat an unconfirmed request like a completed transaction. Make status and next steps visible.
How I designed and built an end-to-end payment lifecycle for a two-sided marketplace—from booking authorization and vendor confirmation through capture and payout.
Co-Founder & Chief Product Officer
Product design + AI-assisted development
Stripe Elements, PaymentIntents,
Connect, subscriptions & Transfers
Plan Zinnia
Wedding marketplace

Couples may be paying thousands of dollars to a small business they have never worked with. Vendors need confidence that a booking is real—and that they will actually get paid.
A traditional ecommerce model—click buy, charge immediately—didn’t fit the relationship. A vendor still needed to confirm availability and accept the work, while the couple needed confidence that their payment would not disappear into an uncertain booking.
The design problem became bigger than checkout: how could the product make money movement, commitment, timing, and responsibility understandable to both sides?
Do not treat an unconfirmed request like a completed transaction. Make status and next steps visible.
Give the business a clear opportunity to accept or decline before the payment lifecycle advances.
The interface, booking record, Stripe state, notifications, and payout state all need to tell the same story.
Instead of immediately capturing payment, Plan Zinnia creates a pending booking and authorizes the payment while the vendor reviews the request. The vendor then has a defined confirmation window.
If the vendor confirms, the payment is captured and the booking moves forward. If they decline—or the request expires—the authorization is canceled rather than becoming a completed charge.
Use payment authorization as the bridge between a couple saying “I want to book this” and a vendor saying “I can take this job.”

The challenge was not exposing Stripe terminology. It was translating financial state into language and actions that make sense during an emotional, high-stakes purchase.
The couple needs to know whether a request is pending, confirmed, declined, expired, or canceled. The vendor needs to know what action is required, how long they have, and what happens to the payment when they respond.
Show the service, event details, fee breakdown, and payment information before the couple commits.
Set expectations: the request exists, payment is authorized, and the vendor still needs to respond.
Make confirm and decline consequential, understandable actions—not generic status changes.
Capture payment, establish the booking relationship, and open the next phase of communication.
Keep booking, messaging, operational status, and financial status connected through completion.
Release vendor funds after the event according to the marketplace payout rules.



Underneath the screens, the booking lifecycle coordinates Stripe PaymentIntents with manual capture, Stripe Connect accounts, subscriptions, verified webhooks, scheduled jobs, transactional email, and the Plan Zinnia booking model.
Booking records retain cent-denominated fee breakdowns and payment state so the product can explain what happened without relying on Stripe as the only source of context. Sensitive payment operations happen server-side and identify the current vendor from authenticated context rather than trusting client-provided identity.
A “Confirm booking” button is also a financial operation. Designing the interaction meant understanding what should happen to the PaymentIntent, booking status, messages, email, and eventual payout when that button is pressed.
Manual capture separates authorization from vendor acceptance.
Vendor accounts create the marketplace relationship required for downstream payouts.
Subscription events synchronize payment-provider state back into the product.
Eligible vendor payouts are released automatically after the event and required waiting period.

The happy path is only part of a payments product. The harder design work is deciding what happens when people do nothing, change their minds, or a system fails.
Pending bookings carry an expiration time. A scheduled job expires stale requests so a couple is not left indefinitely in payment limbo.
The payment authorization is canceled and the booking state communicates that the request did not become a confirmed booking.
Payment operations verify Stripe state before downstream actions such as payout, reducing the risk of treating an incomplete payment as settled.
The payout job checks booking eligibility and payment success before creating the transfer and marking funds released.

Building Plan Zinnia’s payment system pushed me beyond designing a checkout screen. I had to think about money as a lifecycle shared by two customers, a payment provider, and the marketplace itself.
The most important design work was making that lifecycle legible: when a couple has committed, when a vendor has committed, when money actually moves, what happens when something goes wrong, and what each person should expect next.
AI-assisted development accelerated implementation, but I remained responsible for the product model, UX decisions, integration behavior, testing, debugging, and evolution of the production system.
For high-stakes systems, the interface cannot be designed separately from the state machine underneath it. Trust comes from making both behave as one coherent experience.