- 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>
redeemMessage replaces a chain of overriding assignments in the redemption
worker, and openSiteDB moves schema setup and migrations out of Run. An order
whose checkout creation did not finish now invites the customer to recheck
and continue, matching the retry on the next explicit request.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
checkout.NormalizeUsername and checkout.ValidOwner replace the regular
expressions compiled inline in eight handlers and checkout entry points, and
the two public link entry points share one setup helper. A public link
request without the browser cookie now gets one instead of a 400 asking the
visitor to refresh. The public path no longer threads an unused
verified-unpaid flag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- createLink returns a linkOutcome instead of writing HTTP headers that the
queue worker parsed back (Retry-After, X-Checkout-Wait-Seconds,
X-Checkout-Requeue); the fake ResponseWriter is gone and the failure
reason table is merged into the single error switch.
- tryLock/waitLock replace five copies of the worker slot plus checkout.lock
dance (manual links, window holder, redemption, recovery, reconcile).
- A finished ticket replays its stored link until the payment window ends
instead of re-verifying against Stripe after 15 seconds; expired links get
410 and the order lookup reports payment.
- Queue tickets serialize directly (key v2), are saved on every structural
change instead of a dirty/10-second checkpoint, and a damaged snapshot or
ticket is dropped instead of refusing to start.
- The wait estimate is the creation window plus one payment window and build
time per person ahead; the moving average is gone.
- Redemption waits up to 30 seconds for another order instead of failing
with 503, the HTTP write timeout covers the 110-second link creation, and a
full rate-limit table resets instead of rejecting everyone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The active payment window no longer accepts the old 15-minute format or
scans every order to bootstrap itself; a missing reservation means no window.
- Public link requests no longer need queue_protocol, unknown JSON fields are
ignored, and an empty cancel beacon is no longer treated as leaving.
- Checkout creation never charges, so a saved-card order whose creation failed
is retried on the next explicit request instead of being blocked forever;
creation still uses the safe retry helper within one request.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep only tests that guard against double charges, payment spacing, decline
handling, public link isolation, the payment window, queue ordering and
ownership, and the redemption resume path (about 20% of the former size).
checkFixture moves into the queue tests now that check_test.go is gone.
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>
Waiting for the active payment window is the normal state at the head of
the public queue and produced thousands of "manual link failed" lines.
Real failures are still logged.
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.
Route X account and eligibility checks directly while regional pricing
and checkout creation share the configured proxy exit, keeping quoted
currency and amounts consistent for the customer.
A decline now cools the card×node pair and the exit's shared IP for 30
minutes first; only a card declined through two distinct nodes enters a
whole-card cooldown, and do_not_try_again blocks just that card. Card
and rotation status stay readable with no cards configured and no longer
report expired cooldowns as blocks. Links-only recovery works without a
payment card, and the recovery status endpoint reports the
payment-protection pause.
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.
Move the hardcoded merchant account, product IDs, amounts and
currency into an encrypted catalog vault record read per checkout
flow. The setup wizard offers the X Premium defaults (X's public
gifting identifiers) or a fully custom catalog, put --name catalog
updates it, and status validates it. Tests use fake catalog
fixtures instead of real identifiers.
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.
A pasted sing-box config carrying services (api/ssm-api), endpoints
(wireguard/tailscale), or experimental controllers (clash/v2ray/debug)
could bind non-loopback listeners or alter host networking once the
full include registries are enabled. Strip those sections so the
proxy record can only forward traffic; outbounds, route and dns still
pass through. Align the README outbound list (dns outbound was
removed in sing-box 1.12) and document the stripped sections.
Register the full sing-box include registries instead of the
anytls-only outbound registry so the vault proxy record may use any
core outbound type (direct, socks, http, shadowsocks, vmess, vless,
trojan, anytls, shadowtls, snell, ssh, tor, block, dns, selector or
urltest) plus route and dns sections. Reject configs without
outbounds early. The forced loopback mixed inbound and disabled log
are unchanged.
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.