Files
parking_solution/wiki/concepts/validation-sponsorship.md
T
julian 5697137c52 feat(subscription): rename permit→subscription + monthly pricing
The "permit/lejet" feature is really a subscription. Full rename of the
mutable master data, plus a recurring monthly price.

- DB (migration 0004, data-preserving ALTER RENAME): permits→subscriptions,
  permit_credentials/_plates→subscription_*, sessions.permit_id→subscription_id.
- Pricing: per-subscription priceMinor + period(monthly) + currency, with a
  site default (site_config.subscription_monthly_price_minor) pre-filling the form.
- Server: subscription-flow.ts (SubscriptionFlow), routes/subscriptions.ts
  (/api/subscriptions). Web: SubscriptionManager, route, i18n (sq Abonimet/en).
- The signed ledger `permitId` payload is intentionally kept — immutable
  hash-chained history; renaming it would break verification of past events.

Deferred (wiki notes): fee collection into the ledger/shift (a shift-attributed
payment), LPR/ANPR plate source, time-of-day access windows (overnight subscriber).

Also carries the device-footer UI surface (api DeviceStatus, router mount,
i18n devices) due to shared-file overlap with the preceding footer commit.

Verified end-to-end on a fresh DB and migration on a live-DB copy (sessions
preserved). Live DB migrated. Full monorepo builds clean.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
2026-06-18 13:15:04 +02:00

95 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
type: concept
tags: [parking, domain, business, pricing, validation, design]
sources: [parksql2017-legacy-schema]
updated: 2026-06-17
status: open
---
# Validation & Sponsorship — merchant comps, coupons, postpaid B2B
Builds on [[validation-discounts]] (the signed-event discount mechanism) to add the layer it leaves
open: **a sponsor account and postpaid B2B billing.** The driving case — **a nearby business with a
postpaid agreement whose customers enter and exit freely, billed to the business monthly.**
> This page owns the **sponsor/account/settlement** model and the **permit-vs-validation
> distinction**. The *how a discount is applied* mechanics (signed event, `due = max(0, fee −
> discounts)`, attribution, anti-abuse) live in [[validation-discounts]] — not duplicated here.
> Status: **design, not built.**
## Why this is NOT a permit (the key distinction)
| | [[subscription]] | Validation / sponsorship |
| --- | --- | --- |
| Subject | Known in advance; carries a credential (card/QR/plate) | Anonymous walk-in; identified only by the **ticket they were issued** |
| When applied | At entry (credential opens the lane) | **After entry**, against an existing session — at a pay station, by a code, or by a sponsor rule |
| Who pays | The subscriber, out-of-band | A **third party** (merchant/sponsor), or nobody (comp) |
| Model fit | `permits` + credentials | New: a **validation event** on a session + a **sponsor account** |
A permit bypasses tariff computation; a validation **adjusts the computed fee** (or zeroes it). They
compose — but they are different primitives.
## Two economic models (both real)
- **Prepaid** — merchant buys a pool of value up front (legacy `BA_Cupons`: printed single-use codes
worth `DiscMinutes`; City Center research: merchant pre-buys time tickets 15 min→all-day).
Reconciliation = count used codes against the pool.
- **Postpaid** (the asked-for case) — merchant signs an agreement; their customers park free or
discounted; the system **accrues each validation against a sponsor balance** and **invoices monthly**
(City Center: "billed for the number of tickets validated each month," verified 3-0). No money moves
at the lane.
The legacy system did **only prepaid coupons** — **the postpaid sponsor account is net-new** for this
project.
## Modifier types (extends [[validation-discounts]])
The discount-type enum lives in [[validation-discounts]]; legacy `DiscType` (smallint) and research
(Amano McGann / HUB J4M, abstained-not-refuted) confirm the set: `comp` / `percent` / `fixed` /
`time-credit` (legacy `DiscMinutes`) / `rate-switch`. **Sponsorship adds one field** to a validation:
a `sponsorId`. Full-comp + a sponsor = the "free entry/exit, bill the business" case.
## The sponsor-liability consequence (anti-fraud)
The validation is a signed event ([[validation-discounts]], [[append-only-event-chain]]); what
**sponsorship** adds is that **free-to-the-parker is not free-to-the-ledger** — it is a *receivable
from the sponsor*. Under the [[threat-model|operator-as-adversary]] model:
- A postpaid sponsor's "enter/exit freely" still **mints signed entry + exit events** (and snapshots)
— the audit trail is identical to a paying car; only the **settlement target** differs.
- The **sponsor's period liability = the sum of `sponsorId`-tagged validation events** over the
period — derivable from the chain, reconcilable like a [[shift|shift Z-report]] and visible to
[[reconciliation]] (a sponsor comping far more than plausible footfall is an anomaly).
## Proposed data shape (illustrative — design only)
```
sponsors id, name, contact, mode {prepaid|postpaid},
balance_minor (prepaid pool / postpaid accrual), billing_period, active
validations id, session_id, sponsor_id?, type, amount_minor|minutes,
code?, operator_id, created_at // append-only; one row per application
(coupons) code, value_minutes|minor, single_use, used_at? // prepaid pool, optional
```
- A **postpaid** sponsor: each full-comp validation appends a row and accrues `amount` to the
sponsor; monthly invoice = sum over the period; exit is free at the lane.
- **Free entry/exit "freely"**: either the sponsor issues credentials (then it's closer to a
[[subscription]] — pick that path), or customers take a normal ticket and a sponsor rule / merchant code
comps it at exit. The agreement wording decides which; **both are expressible.**
## Reconciliation & settlement
- **Prepaid**: pool decrements; alert at low balance; no invoice.
- **Postpaid**: accrue; **monthly statement** per sponsor (legacy/City Center cadence ~the 10th).
Statement lines trace to signed validation events → disputes resolvable against the chain.
## Open
- **"Enter/exit freely" mechanism**: sponsor-issued credentials ([[subscription]]-like) vs. ticket +
comp-at-exit. Likely offer both; confirm the operator's actual deal shape.
- Prepaid coupon format: printed codes (legacy) vs. QR vs. merchant web-validation portal.
- Who may apply a validation, and the **per-operator cap** (a comp is a fraud vector — bound it and
always sign it).
- Invoicing: in-app statement only, or export for external billing? FX if sponsor bills in another
currency (defer to [[tariff]] FX).
- Partial-stay sponsorship (merchant covers first 2h, parker pays the rest) — `time-credit` or
`rate-switch` covers it; confirm.