feat(promo): enforce active + max_uses, validate endpoint, codes hidden from public
Promo handling was inherited from upstream unchanged and had four gaps the webapp was about to put in front of buyers: - `active` was decorative: purchase never read it, so a deactivated code kept discounting. Now rejected with "Promo code is not active." - No redemption cap (#32). `PromoCode.max_uses` (None/0 = unlimited) with `used_count` DERIVED from paid tickets carrying the code in `extra.applied_promo_code` — each ticket of a multi-ticket purchase consumes one use (upstream v2 counts one per basket; documented). Paid-only counting so an abandoned Stripe session can't lock out the last uses for the 24 h unpaid-row lifetime; bounded overshoot under concurrency accepted. - Every code was readable by anyone: `PublicEvent.extra` was the full `EventExtra` and `/events/public` returned the untrimmed `Event` (wallet id included). `EventExtraBase` / `PublicEventExtra` project them out; `/public` now goes through `PublicEvent`. Organizer and admin listings keep the full model, now hydrated with `used_count`. - No preview: `POST /events/api/v1/promo/validate/{event_id}` (same URL as upstream v2; `quantity` replaces v2's `items` since this fork has no ticket types) returns v2-shaped `BasketTotals` + `currency`. Advisory: bad codes are simply absent from `discounts_applied`; purchase still hard-fails them with distinct details. All pricing (validate, invoice, Stripe amount) goes through one pure `basket_totals` with a single rounding rule (whole sats / 2 dp fiat), so the preview equals the charge. Stripe metadata carries `promo_code`; the organizer stats rows carry `applied_promo_code`. `api_event_update` keeps stored codes when the request omits `extra.promo_codes` (explicit `[]` still clears): now that public records don't carry them, a client round-tripping one would otherwise wipe the organizer's codes on every edit. Closes #32 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ByAwHU4pRnyE58YocQvAas
This commit is contained in:
parent
4c3b1bca31
commit
8602bd71e3
7 changed files with 618 additions and 19 deletions
|
|
@ -6,13 +6,19 @@ from loguru import logger
|
|||
from .crud import db
|
||||
from .tasks import wait_for_paid_invoices
|
||||
from .views import events_generic_router
|
||||
from .views_api import events_api_router, qr_api_router, tickets_api_router
|
||||
from .views_api import (
|
||||
events_api_router,
|
||||
promo_api_router,
|
||||
qr_api_router,
|
||||
tickets_api_router,
|
||||
)
|
||||
|
||||
events_ext: APIRouter = APIRouter(prefix="/events", tags=["Events"])
|
||||
events_ext.include_router(events_generic_router)
|
||||
events_ext.include_router(events_api_router)
|
||||
events_ext.include_router(tickets_api_router)
|
||||
events_ext.include_router(qr_api_router)
|
||||
events_ext.include_router(promo_api_router)
|
||||
|
||||
events_static_files = [
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue