Building a Pretix + Stripe Connect Plugin for Live-Music Venues
Facts verified against Upwork contract history + commit log. No metric stated below was invented; if it isn't measured, it isn't here.
The Brief
A regional live-music operator (Hilltown Media Group) needed to ship venue-branded online ticketing on top of Pretix — a self-hosted, open-source event ticketing platform — with a multi-venue Stripe Connect topology where each venue stays Merchant of Record (MoR) for tax purposes. They came in with a clear architectural opinion: Direct Charges (or Destination with on_behalf_of), a fixed $1.00 platform fee decoupled from the venue's sales tax, and a Pretix-plugin-shaped extension surface so nothing forks the upstream platform.
What Was Delivered or Funded (Phase 1 Evidence Gates)
The Upwork contract has three Phase 1 milestones with separate evidence gates. I am keeping the accounting language strict here:
- M1 — Released and withdrawn: Pretix plugin scaffold (
yourstage_core) with the venue-as-MoR Stripe Connect integration, Direct Charges on the connected account, and the $1 platform fee implemented asapplication_fee_amountso it never lands in the venue's taxable-revenue ledger. - M2 — Approved and pending balance conversion: multi-venue onboarding glue, Stripe account connection flow, local tax-rate settings, and buyer-flow proof.
- M3 — Escrow funded: checkout hardening, Stripe evidence, webhook/refund reconciliation, and production-readiness proof.
The verified cash gate as of June 5, 2026 is M1 withdrawn at $1,350 net, M2 pending at $900 net, and M3 escrow funded at $1,500 gross. Phase 2 ($5,000) covers pre-tab drink tokens, featured-merch upsells at checkout, an automated waitlist with SetupIntent -> off-session PaymentIntent conversion, on-door upsell modals on the Pretix scan API, and Stripe Terminal JS/RN integration for the box office.
Three Architectural Decisions Worth Lifting
These are the choices that took the most thinking and that I'd reuse on the next marketplace platform — listed not because they're novel but because each one quietly avoids a class of bug.
1. application_fee_amount instead of cart-line platform fee
Using Stripe Connect's application_fee_amount keeps the fee on the platform's Stripe account, off the venue's books, and out of every tax jurisdiction's audit surface. Two-line change. The reason to know about it isn't the code, it's the months of back-office pain it removes.
2. Native Pretix add-on items, not custom cart math
Modeling the upsells as native Pretix Item add-ons means the existing Phase 1 Stripe Direct Charge math calculates the new totals correctly with zero changes. Same applies for refunds — Pretix's order_canceled / order_refunded signals already know how to attribute partial refunds back to add-on items.
The general principle: when extending a platform with a plugin model, lean on the platform's native primitives even when they feel verbose. The downstream-invariant cost of going around them is paid forever.
3. SetupIntent for the waitlist, not stored card-on-file
PaymentMethod against the user record.
The right way is SetupIntent → vault setup_intent_id only (not the PaymentMethod, not last4) → convert to off-session PaymentIntent when a seat frees. PCI scope stays SAQ-A. And the off-session path forces you to handle authentication_required declines (5-15% expected on EU/UK SCA + US 3DS2) with a skip-and-notify fallback — which means the waitlist actually keeps moving instead of stalling on one issuer step-up.
What I'd tell the next freelance dev shipping a Pretix-plus-Stripe-Connect project
signals.py source before you write your own. Half the work is already there.
StripeWebhookEvent table (event-id PK for idempotency) on day one. It's the dashboard you'll want at 11pm when a venue's first show is selling.
What's Next
Phase 2 ($5K, milestone-scoped — upsells, automated waitlist, box office POS, and webhook reconciliation), then Phase 3 (gamification + SMS marketing) and Phase 4 (self-serve onboarding + PWA scanner).