Skip to content
Hails edited this page Jun 11, 2026 · 1 revision

Store

The supporter store at /store is how players support the site's hosting costs and pick up a few quality-of-life perks in return: a custom profile tag, raid queue priority, and (for super donators) pre-queueing. Payments run through PayPal orders with a server-to-server webhook as a safety net. This page covers what is sold, how purchases flow, the custom tag pipeline, and what an operator needs to configure.

What is sold

The seeded catalog (from schema.sql; admins can toggle items and add more):

Item Slug Price Billing Benefit
Supporter Pack supporter $2.99 Bimonthly (60 days) supporter: submit a staff-reviewed custom tag for your profile, recolor it, and show a Supporter badge
Priority Pass priority_pass $7.99 Monthly (30 days) queue_priority: jump to the front of the raid join queue

A third benefit type, super_donator, exists in the code for higher-tier items: it inherits queue priority and additionally unlocks pre-queueing (waiting on a raid boss before any lobby is open) plus a donator badge. See Raid Finder for what the raid perks do in practice.

Item types are one_time, monthly, or bimonthly. Monthly purchases expire 30 days after completion and bimonthly after 60; one_time purchases never expire (and cannot be bought again while still active). There is no auto-renewal: each term is a separate checkout. Users can cancel an active monthly or bimonthly purchase from the store page, which ends it immediately.

Two side effects of any active purchase:

  • a +5 read-time bonus to effective trust while it lasts (see Trust and Awards),
  • the perks above, evaluated live against the purchase's expiry on every check.

Purchase flow

  1. The user posts /store/checkout with an item slug. The server creates a PayPal order (intent: CAPTURE, USD) with return and cancel URLs pointing back at the site, records a pending purchase row keyed by the PayPal order id, and redirects the user to PayPal's approval page.
  2. Return path: PayPal sends the user back to /store/return?token=<orderID>. The server captures the order and marks the purchase completed, stamping the expiry from the item type.
  3. Cancel path: /store/cancel marks the pending purchase cancelled.
  4. Webhook path: PayPal also posts CHECKOUT.ORDER.APPROVED to /api/store/webhook. If the user approved payment but never made it back to the return URL, the webhook captures and completes the purchase instead. The endpoint verifies the transmission signature against the configured webhook id via PayPal's verification API, always answers 200 so PayPal does not retry, and ignores anything that fails verification or is not pending. It is the one CSRF-exempt route in the app (see Architecture).

Custom tags (Supporter Pack)

Supporters can request a custom colored tag, shown next to their name everywhere tags appear:

  • Submit a name (up to 32 characters) and color from the store or settings page. Submitting requires an active Supporter Pack.
  • The request enters staff review with states pending, approved, rejected, or revision:
    • Approved: the tag is created and assigned. Staff approval double-checks the pack is still active.
    • Rejected (with a reason): a 7-day cooldown starts before a fresh request can be submitted.
    • Revision requested (with notes): the user may resubmit immediately, no cooldown.
  • You can only have one pending or approved request at a time. If an approved tag is later rejected, it is removed from the profile.
  • Color changes to an approved tag are self-service but rate limited to 5 changes per 30 minutes.

The review queue lives in the admin panel; see the Admin Guide.

Operator setup

The store is off until both of these are true:

  1. The store_enabled site setting is on (toggled from the admin Settings tab, Admin+).
  2. PayPal credentials are configured. Without them the page can still be shown, but checkout is unavailable (PayPalReady is false and order creation fails safely back to the store with an error flash).

PayPal configuration

Create a REST app in the PayPal developer dashboard and set these environment variables (see Configuration for the full env reference):

Variable Meaning
PAYPAL_CLIENT_ID REST app client id
PAYPAL_CLIENT_SECRET REST app secret
PAYPAL_MODE live for production; anything else (or unset) uses the sandbox (api-m.sandbox.paypal.com vs api-m.paypal.com)
PAYPAL_WEBHOOK_ID The id PayPal assigns when you register the webhook

Register a webhook in the PayPal app pointing at https://your.site/api/store/webhook, subscribed to CHECKOUT.ORDER.APPROVED, and put the resulting webhook id in PAYPAL_WEBHOOK_ID. Without it, webhook signature verification fails closed and only the browser return path completes purchases.

OAuth tokens are fetched with the client credentials grant and cached in-process until shortly before expiry.

Managing the catalog

Items live in the store_items table (name, slug, description, price in cents, type, benefit, active, sort order). The admin Donations tab lists them and toggles active; adding items or changing prices is done in the database. Purchases keep a full status history (pending, completed, refunded, cancelled) joined to the user and item.

Clone this wiki locally