Pricing, payments & marketing
Rule-based pricing with deterministic quotes, gift cards, deposit and online payment via Stripe, POS integrations and rule-driven upselling. The kiosk's own till lives in Kiosk; bookkeeping is fed via RevenueEvents as usual.
dynamic_pricing_enabled in the venue profile gates the peak/off-peak rules — off = base price + discounts only
| Rule type | Parameters | Effect | Active |
|---|---|---|---|
| peak_multiplier | Fri–Sat 17–22 | × 1.4 | |
| peak_multiplier | weekdays 10–15 (off-peak) | × 0.8 | |
| tier_discount | ChainPassport BRONZE/SILVER/GOLD | −5 % / −10 % / −15 % | |
| group_discount | min_party_size 8 | −10 % | |
| duration_bracket | ≥ 120 min | −10% on excess time | |
| promo_code | SOMMAR26 · max 200 redemptions · until 31 Aug | −50 kr | |
| promo_code | VINTER25 (expired) | −25 kr |
Quote preview — POST /chain/pricing-rules/preview
Court 1 · Fri 3 Jul 18:00–20:00 (120 min) 480 kr
Peak multiplier Fri 17–22 (× 1.4) +192 kr
Duration discount ≥ 120 min (−10% on the 2nd hour) −34 kr
ChainPassport GOLD (−15 %) −96 kr
Promo code SOMMAR26 −50 kr
To pay (Quote.total) 492 kr
Deterministic: same input → same quote. The row breakdown is shown to the customer in the booking flow.
New rule
| Code (16 characters, CSPRNG) | Balance | Status | Expires | Redemptions |
|---|---|---|---|---|
| K7QM-2WPX-9RTL-4NVD | 350 / 500 kr | active | 2027-06-30 | 2 times |
| H3XC-8JZR-1TQW-6PLM | 0 / 1 000 kr | used up | 2026-12-31 | 4 times |
| B9WN-5KDF-3YHS-7GXA | 750 / 750 kr | active | 2027-03-15 | 0 times |
The code is unique per (tenant_id, code); redemption is atomic with an audit record per transaction — two simultaneous redemptions can never draw the same krona twice. Customers can check the balance anonymously (rate-limited endpoint that only shows balance + status) — see the customer portal.
Issue gift card
BookingPayment rows — deposit paired with pay_at_venue, settled at check-in
| Booking | Payment row | Amount | Status | Link |
|---|---|---|---|---|
| BK-4714 | deposit (Stripe PI pi_3Nx…) | 2 000 kr | succeeded | metadata → booking (round-trip) |
| BK-4714 | pay_at_venue (rest) | 7 500 kr | pending — settled at check-in | paired with the deposit |
| BK-4711 | online (Stripe PI pi_3Ny…) | 360 kr | succeeded | — |
| BK-4688 | refund (parent pi_3Nk…) | −180 kr | refunded | parent_payment_id → PARTIAL_REFUND |
Stripe-webhook /webhooks/stripe/booking-payments
evt_1PZk…payment_intent.succeededverified signature · processed
evt_1PZk… (duplicate)payment_intent.succeededidempotent — ignored (StripeWebhookEvent on event_id)
evt_1PYx…charge.refundedverified signature · processed
Refunds — against succeeded rows (F22.05.11)
The refund row is linked via parent_payment_id and the booking's payment status is recalculated. Every transition is logged in BookingPayment.audit_trail (actor, action, metadata) — F22.05.12.
POS integrations — transactions are pushed in, webhooks are signature-verified
| System | Mode | Latest ping (device health, 15 min) | Status |
|---|---|---|---|
| Square | transaction push + signed webhook | 4 min ago | ● online |
| Zettle (iZettle) | transaction push + signed webhook | 2 min ago | ● online |
| Stripe Terminal | PaymentIntent + Stripe-Signature (skew check) | 38 min ago | ⚠ ping missing — ops alert sent |
QR check-in token (F22.18.07) — signed JWT
The operator scanner verifies offline via cached JWKS (F22.18.08) — check-in works even when the hall's network is down. The scanner itself lives in Reception → Check-in.
Rule-driven smart upsell — conditions on color_type · party · weekday · season; shown in the customer's checkout
| Terms | Prompt | Outcome 30 days | |
|---|---|---|---|
| color_type=group · party ≥ 6 · Fri/Sat | Suggest after-work package (menu + extended time) | 18% conversion | |
| color_type=event · season=summer | Suggest summer terrace + barbecue menu | 11% conversion | |
| party ≥ 4 · weekdays | Suggest boule-set rental + try-out instructor | 7% conversion |
The customer can dismiss a prompt — the dismissal applies to the whole session (session dismiss, F22.18.06).
Marketing automations (F22.18.09–12)
Birthday offer on
Trigger: days_before_birthday = 7 · email + push
42 sent · 9 bookings
Win-back on
Trigger: inactivity_days = 90 · historical_min = 3 visits · email
18 sent · 3 bookings
Gift-card push scheduled
Trigger: annually wk 48 (Christmas shopping) · email
—
Post-detractor recovery on
Trigger: NPS ≤ 6 after a visit · email (personal)
4 sent · 2 replies
| Event | Channels | Language | Last changed | |
|---|---|---|---|---|
| Booking confirmation | email · SMS · push | sv en fr de es | 28 Jun | |
| Reminder 24 h before | email · push | sv en fr de es | 12 Jun | |
| Reminder 2 h before | SMS | sv en | 12 Jun | |
| Cancellation confirmation | sv en fr de es | 3 May | ||
| No-show notification | email · SMS | sv en | 3 May |
Edit template — Booking confirmation (SMS · Swedish)
The customer's per-channel opt-out (F22.14.06) is managed in the customer portal — see My account.