System Documentation

One-Off Purchase System

Complete reference for the one-off purchase flow (Track 3) — day passes and other single-transaction products. Uses Stripe PaymentIntent for immediate charges and creates OfficerRnD day passes for kiosk check-in.

Stack Webflow · Cloudflare · Stripe · OfficerRnD · MailerSend
Payment Type One-off PaymentIntent (not subscription)
Worker labour-temple-checkout (shared with Track 1)

How it all connects

Track 3 shares the same Cloudflare Worker as Track 1 (membership subscriptions) but uses Stripe PaymentIntents for one-off charges instead of SetupIntents for subscriptions. The same Webflow CMS template page serves both flows, toggled by a "Purchase Type" CMS field with conditional visibility.

Webflow Purchase page + form fields
Cloudflare Worker Purchase orchestration
Stripe PaymentIntent (one-off)
O OfficerRnD Member + day pass creation
MailerSend Purchase confirmation
Key difference from Track 1: Track 1 uses SetupIntent → subscription (recurring). Track 3 uses PaymentIntent (one-off charge). Both share the same Worker, the same CMS template page, and the same OfficerRnD member creation flow.

From page visit to day pass holder

Eleven steps take a visitor from landing on a purchase page to holding an active day pass.

1
User lands on a product page

Visitor browses to a product page on labourtemple.com (e.g. /memberships/day-pass). The CMS "Purchase Type" field is set to "One-Time", which shows the purchase form instead of the subscription checkout form via conditional visibility.

Webflow CMS
2
Order summary renders

purchase.js detects the product slug from the URL path (e.g. "day-pass"), fetches product data from the Worker's GET /products endpoint, and renders a flat-price order summary. No pro-rating or initiation fees.

Purchase JS
3
User fills out the purchase form

Native Webflow fields collect: first name, last name, email, phone, company name, referral source, and optional notes. The Stripe Payment Element (mode: payment) collects card or bank account details.

Webflow + Stripe
4
User clicks "Buy Now"

The site script calls window._validate() to check all required fields. If validation fails, fields are highlighted and submission is stopped.

Site Script
5
Payment details are tokenized

The script calls elements.submit() to securely tokenize the payment method with Stripe.

Stripe.js
6
Worker creates member + PaymentIntent

Form data and product slug are sent to POST /create-purchase. The Worker creates or finds an OfficerRnD member, creates or finds a Stripe Customer, applies promo if provided, and creates a PaymentIntent. Returns clientSecret, paymentIntent ID, and memberId.

Cloudflare Worker
7
Payment is charged

The site script calls stripe.confirmPayment() with the client secret. Stripe charges the card or bank account immediately.

Stripe
8
Purchase is completed

The site script calls window._complete(memberId, paymentIntentId, slug, customerName), which sends data to POST /complete-purchase.

Purchase JS
9
Worker creates OfficerRnD day pass

The Worker verifies the PaymentIntent succeeded, then creates a day pass via POST /passes with explicit parameters: member, count: 1, type: "hotdesk", validFrom/validTo (today), intervalLength: "once", grantActiveStatus: true. The pass appears in the member's Day Passes tab for kiosk check-in.

OfficerRnD
10
Confirmation emails sent

The Worker sends a branded purchase confirmation email (with receipt URL, payment method, date) to the customer and a plain-text notification to the team via MailerSend.

MailerSend
11
User redirected

On success, the user is redirected to /purchase-confirmation.

Webflow

What each service does

Five services work together to power the purchase system — the same Worker used for Track 1 subscriptions.

Webflow
Frontend — product pages & form

Hosts product pages (CMS collection) and embeds the purchase form. The CMS "Purchase Type" field toggles between checkout (subscription) and purchase (one-off) forms via conditional visibility.

  • CollectionMemberships (same as Track 1)
  • TemplateMemberships page (68d71235d108eda6fe6cf617)
  • Embedpurchase-embed.html (~4.2K chars)
  • External JSpurchase.js (served from Worker)
  • Site Scriptstripe_purchase_v1 (1,902 chars)
  • TogglePurchase Type = "One-Time" shows purchase form
Cloudflare Worker
Backend API — purchase orchestration

Shared with Track 1. Orchestrates the purchase: creates OfficerRnD members, Stripe customers, PaymentIntents, day passes, and sends confirmation emails. Also serves the external purchase JS.

  • Namelabour-temple-checkout (shared with Track 1)
  • URLlabour-temple-checkout.labourtemple.workers.dev
  • EndpointsGET /purchase.js, GET /products, POST /create-purchase, POST /complete-purchase
  • SharedPOST /validate-promo (same as Track 1)
  • Deploycd cloudflare-worker && npx wrangler deploy
Stripe
Payment processing — one-off charges

Processes card and ACH payments via PaymentIntents. No subscriptions or recurring billing. Charge receipt URL is included in the confirmation email.

  • ModeLive
  • MethodsCard + US Bank Account (ACH)
  • FlowPaymentIntent (immediate charge)
  • PromosManual codes only (no auto-promo)
  • ReceiptStripe-hosted receipt URL (no auth)
O
OfficerRnD
Member database & day pass management

Stores member records and day pass allocations. Day passes appear in the member's profile for kiosk check-in at the front desk.

  • APIPOST /passes (undocumented but working)
  • Pass Typehotdesk (required for correct display)
  • ValidityPurchase date (validFrom = validTo = today)
  • StatusgrantActiveStatus: true (kiosk-ready)
MailerSend
Purchase confirmation emails

Sends branded purchase confirmation emails with receipt details to the customer and plain-text notifications to the team.

  • Templatepr9084zxnvjlw63d (purchase confirmation)
  • Variablesfirst_name, product_name, amount_paid, receipt_url, payment_date, payment_method
  • Teamadmin@, membership@, chris@hundredacre.design
  • Non-blockingEmail failures don't break purchase

Current products

Products are defined in PRODUCT_CONFIG inside the Worker. Unlike PLAN_CONFIG (subscriptions), products have no initiation fees, no pro-rating, and no auto-promo system.

Product Name Slug Price OfficerRnD Plan ID Terms
Day Pass day-pass $50 66b399ef3de7dbab92b5c985 /legal/membership-terms
Adding a new product: (1) Create a one-time Stripe Product + Price, (2) add entry to PRODUCT_CONFIG in worker.js and redeploy, (3) add a CMS item in Webflow with "Purchase Type" set to "One-Time", (4) publish the Webflow site.

How day passes are created

Day passes are created via OfficerRnD's POST /passes endpoint. This is an undocumented API endpoint (not in the API reference, but documented in help center articles and confirmed working).

API call parameters

FieldValueNotes
memberOfficerRnD member IDFind-or-create by email
count1One pass per purchase
type"hotdesk"Must be "hotdesk" (not "membership") for correct display
validFromToday midnight UTCe.g. 2026-04-03T00:00:00.000Z
validToToday midnight UTCSame as validFrom for single-day pass
intervalLength"once"Non-recurring
grantActiveStatustrueRequired for kiosk check-in
Critical: All fields must be passed explicitly. The API does NOT inherit settings from the OfficerRnD plan configuration. The plan's passesValidityPeriod.intervalCount must be set to a non-zero value (currently 12 months) or passes will have zero validity.
Non-blocking: Pass creation failure does not block the purchase. Payment is already captured. The pass is logged as a warning in the Worker console.

Common tasks

  1. Create one-time Stripe Product + Price
  2. Add to PRODUCT_CONFIG in worker.js
  3. Deploy worker: npx wrangler deploy
  4. Add Webflow CMS item with Purchase Type = "One-Time"
  5. Publish site

Three locations contain purchase logic:

  • Embed HTML: CSS/styling (rarely edited)
  • External JS (purchase.txt): Logic, deploy worker via npx wrangler deploy
  • Site script (stripe_purchase_v1): Stripe initialization (2K char limit)
  • Customer template: MailerSend dashboard (template pr9084zxnvjlw63d)
  • Team recipients: TEAM_EMAILS array in worker.js
  • Variables: first_name, product_name, amount_paid, terms_url, promo_name, receipt_id, receipt_url, payment_date, payment_method, support_email
  • Embed: Copy to Webflow, publish
  • External JS: Edit purchase.txt, wrangler deploy (5 min cache)
  • Worker: wrangler deploy
  • Site script: Register via Scripts API, apply to page, publish

Architecture decisions & patterns

Track 1 (subscriptions): Uses SetupIntent to confirm the payment method first, then creates a subscription. The charge happens via the subscription's first invoice.

Track 3 (purchases): Uses PaymentIntent to charge immediately. No subscription. The Stripe Elements mode is set to payment with a fixed amount.

The Memberships CMS template page contains both the checkout form (Track 1) and the purchase form (Track 3). Webflow conditional visibility on the "Purchase Type" Option field controls which is shown:

  • Purchase Type = "Subscription": Shows checkout form fields (checkout-*), checkout embed, join-btn
  • Purchase Type = "One-Time": Shows purchase form fields (purchase-*), purchase embed, buy-btn

Both site scripts self-guard: stripe-payment.js checks for #checkout, stripe-purchase.js checks for #purchase. Only one script initializes Stripe.

Unlike Track 1 (which uses a CMS data-plan attribute), Track 3 detects the product slug from the URL path:

var slug = location.pathname.split('/').filter(Boolean).pop() || '';

For /memberships/day-pass, the slug is day-pass. This is matched against PRODUCT_CONFIG keys in the Worker.

After confirming the PaymentIntent succeeded, the Worker extracts receipt details from the Stripe Charge object:

  • receipt_url: Stripe-hosted receipt page (no auth needed)
  • receipt_number: Used as receipt ID (falls back to charge ID)
  • payment_method_details.card.brand/last4: e.g. "Visa ending in 4242"
  • payment_method_details.us_bank_account: e.g. "Chase ending in 6789"

Extraction failure is non-critical and logged as a warning.

  • OfficerRnD POST /passes is undocumented: Works but not in API reference. All fields must be explicit.
  • Pass type must be "hotdesk": Even if the plan says "membership", the API requires "hotdesk" for correct display.
  • Plan validity config required: passesValidityPeriod.intervalCount must be non-zero (12 months).
  • No auto-promo for purchases: Only manual promo codes supported.
  • purchase.js cached 5 minutes: Changes require Worker deploy + cache expiry.
  • ACH bank names: Truncated display (Stripe Financial Connections limitation).

Common issues & fixes

Payment Element not showing

Check #purchase element exists (conditional visibility), window._S defined in embed, site script applied and published.

"Invalid product" from Worker

Product slug from URL doesn't match PRODUCT_CONFIG key. Check the URL path.

Day pass not appearing in OfficerRnD

Check Worker logs for pass creation warnings. Verify plan's passesValidityPeriod.intervalCount is not 0. Ensure type is "hotdesk".

"Guest" name in confirmation email

Customer name not passed from frontend. Verify _complete() sends 4th argument (customerName).

Wrong form showing on page

Check the CMS "Purchase Type" field value. "Subscription" shows checkout form, "One-Time" shows purchase form.

Debugging tools

Browser Console for JS errors · Network tab for POST requests · Cloudflare Logs for Worker errors · Stripe Dashboard > Developers > Logs · MailerSend Dashboard > Activity