- The public form submits directly; the extra "confirm generating a link"
dialog repeated guidance the form already shows, and creating a link never
charges.
- queue_protocol is no longer sent, and the order check is reduced to
"not a queue ticket and either paid or carrying a link" instead of
re-validating every field and the Stripe URL the server just verified.
- Redemption status polling no longer stops after five minutes with a
"continue" button; it keeps reading at a slower pace until the order
settles.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The lookup used to read local records only, so a visitor who paid after the
queue stopped watching saw "expired, no payment recorded". For a created
order it now runs the same read-only Stripe completion check the payment
window uses, records a confirmed payment on the order (and frees its window)
when the order lock is free, and says whether Stripe could be reached. It
still never creates orders or joins the queue; the lookup limit drops to 6
requests per minute per IP because each one may call Stripe.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Replacement links after an operator-verified decline are no longer capped
at three; each still needs the unpaid attestation and decline evidence.
- A payment node with a transport failure cools for 30 minutes, not 6 hours.
- Review orders keep being reconciled for 7 days instead of 24 hours.
- Recovery previews stay valid for an hour instead of 10 minutes.
- Code generation allows up to 2000 per batch and moves up to 1000 codes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Tab title shows queue position, then the payment countdown, so a
backgrounded tab still tells people when to come back and pay.
- The ready-notification button moves above the payment warning, starts as
enabled when permission was already granted, and is hidden where browsers
cannot notify; phones vibrate when the link is ready.
- Waiting copy no longer says backgrounding is safe; it states the 90-second
pause and 5-minute removal that the queue actually enforces.
- User-not-found, ineligible and expired-ticket errors say what happened,
that nothing was charged, and what to do next; errors scroll into view.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GET /api/manual-link/order?username= returns the newest public order that
this browser's cookie created for that X username: plan, link creation time
and a coarse state (paid, open, ended, not created). It reads local records
only, never joins the queue or creates orders, and never returns links, card
data or other browsers' orders. Paid state also uses the verification the
queue records for the last payment window. The public form and the payment
link card can run the lookup. Refs #6.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a read-only GET /api/manual-link/queue that reports how many people are
waiting and the estimated wait for a newcomer, sharing the estimate used for
queued tickets. The public form and confirmation dialog show it so people
know the wait before they commit to keeping the page open.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After a link was delivered the saved queue session stayed behind and
/queue/current returned finished tickets, so a refresh or later visit
replayed "付款链接已更新或失效". The page now saves the link's expiry and
forgets the session once it expires, is paid, or fails; the server only
recovers a finished ticket while its link is still live and answers 410
for ended links so the page returns to the clean form.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restoring the queue rejected anything other than 3 or 6 months, so a single
12-month ticket stopped the service from starting. Tickets are now checked
against 1..24 and the configured catalog, and invalid entries are skipped
with a log line. The page's saved queue session accepts the same range.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stored queue results never change, but terminal failures were saved as 502
and the page retried every 5xx, so users saw "reconnecting" indefinitely.
Pending and expired-link outcomes now answer 409, any other stored 5xx is
normalized to 422 (also for restored snapshots), lock failures are retried
by the worker, and the page reconnects only on network errors, 429, 502,
503 and 504.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the Stripe node pool and per-card status out of the recovery card
into two dedicated panels with skeleton, empty, paused, and
failure-and-retry states. Translate backend card problem and block
reasons into Chinese, explain the rotation cadence and pair cooldowns,
warn when no card can currently pay, and drive the pause warning from
the real PaymentPaused flag instead of the site-wide switch.
Refresh the preview mock with realistic rotation values, real reason
strings, and a PREVIEW_NETWORK=direct switch; rebuild the assets.
Add encrypted card pool management, persisted card and outbound rotation, and per-card decline cooldowns. Improve customer and redemption-code lookup, recovery controls, and filtering; rebuild frontend assets and refresh mock screenshots.
Give read-only eligibility checks four independent slots so payment processing no longer blocks them. Cover concurrency, slot cleanup, card rotation, and lookup behavior with tests.
POST /api/check runs the read-only X gifting-eligibility query for
a username under the shared work semaphore with its own 8/min rate
bucket, so visitors can verify their account before redeeming. The
redeem page gains an advisory check button with loading and result
states.
Add stackable complement operators (not/!/非), per-condition
inequality (status!=active, clickable = / ≠ toggle on canvas
tokens), and months/username conditions with - for empty values.
The palette gains duration and account groups, and full-width
operator punctuation is normalized.
The expression renders as deletable rounded-rectangle tokens on a
canvas, composed from a guarded palette of status/folder/logic
buttons, with a raw-text mode for power users. Guards prevent
invalid sequences, mid-deletions explain what broke and how to
repair it, full-width punctuation is normalized, and all actions
are announced to screen readers.
Compose folder and status conditions with and/or/parentheses in the
admin UI (typed or assembled from helper chips). In filter mode all
pages are fetched and matched client-side with local pagination, so
no backend changes are needed. Invalid expressions show precise
Chinese errors without fetching.
Replace the cramped 90-bar grouped activity chart with three
single-series small-multiple strips (created, redeemed, succeeded),
give plan and review-stage bars proper height with legends and
percentage labels, and lay the summary cards out 3x2 on desktop.
Add GET /api/admin/stats aggregating code counts, redemption and
success rates, per-plan totals, a 30-day activity series, and review
stage distribution. Render a collapsible stats panel with summary
cards and an accessible SVG bar chart on the admin page, show total
count and pages in pagination, and let users resume status polling
after the 5-minute timeout without re-redeeming.