# Release verification

## Authentication

- Login returns to the exact registered callback and exchanges the code once.
- Refresh restores the session after closing and reopening the browser and supports the expected 30-day session window.
- Logout removes local state; a token for another site is rejected.

## AI tasks and points

- Models and point prices come from Star APIs, not constants.
- The displayed catalog equals the intersection of the current site-scoped runtime response and the site's own display-ID configuration; stale, disabled, absent, or non-callable modes are never reintroduced from local data.
- The UI renders one product entry per `products[]` item, then submits the selected mode's exact `task_model` and `task_type`; it does not construct child aliases from suffixes.
- Product logos render from Star Task's runtime field (`products[].icon`, or `models[].logo_url` only for the legacy flat view); no model/vendor logo map, bundled brand asset, or constructed `/applogo` URL remains in frontend code.
- A product with mixed mode availability keeps unavailable modes disabled and explains the runtime `unavailable_reason` without hiding callable sibling modes.
- One click creates one UUID v4 idempotency key; a network retry reuses it.
- A multi-image set creates one task collection instead of a browser-side loop of independent tasks.
- Collection refresh/login recovery restores per-image status and all regeneration versions.
- Collection stop/resume never recreates a successful image; an insufficient-points resume pauses without losing completed results.
- Pending, succeeded, failed, and cancelled states render correctly.
- A successful image/video/audio opens from a `task.starhk.uk` URL after the upstream URL expires.
- Audio/music controls come only from runtime `form_schema`; unavailable or unpriced catalog models cannot be submitted.
- Text and image capabilities, modes, controls, and material limits come only from runtime catalog metadata; no model-name fallback is present.
- Audio detail views expose playback/download plus Star/upstream task IDs, completion time, actual points, and failure reason.
- Successful and failed jobs produce the expected debit/refund, with no frontend point mutation.

## Reusable AI tools

- Tool names, categories, form fields, constraints, versions, and prices come from `/v1/tools`.
- Text generation works without an image; image-understanding rejects missing or excess images before charging.
- The frontend renders normalized `text`, optional structured `data`, and ordinary media outputs.
- Changing a tool in Star Admin affects new tasks without a frontend release; an idempotent retry does not create or charge twice.

## Payments

- Checkout uses only the current six public payment product IDs, or a site-selected subset of them.
- The product catalog is filtered through the site's explicit allowlist; hidden products cannot be submitted from the purchase UI.
- Payment methods come from `listPaymentMethods()` and the visible set is the intersection of runtime capabilities, the site's scenario-specific selection, and the product's supported mode.
- Country detection and page locale influence defaults or labels only; they do not locally invent capabilities or act as authorization rules.
- One-time Airwallex checkout submits either one selected runtime `payment_method`, or an ordered `payment_methods` array for an aggregated Hosted Payment Page picker, plus an explicit ISO country hint. The two fields are never sent together; subscriptions expose only methods whose runtime `modes` include `subscription`.
- An aggregated mainland-China picker uses `payment_methods: ['alipaycn', 'wechatpay']` when Alipay should appear first/default and WeChat Pay below it; every entry is present in the latest runtime capability response.
- The production CSP `script-src` allows both `https://static.airwallex.com` and the runtime bundle host `https://checkout.airwallex.com`; the browser console has no blocked Airwallex scripts, frames, or connections.
- FPS is not displayed or submitted. Every registered enabled site can use configured Airwallex capabilities without a frontend-site allowlist.
- Logged-in checkout redirects once, and logged-out checkout resumes once after OAuth before the normal return-path redirect.
- The purchase control is disabled during Checkout Session creation; an ambiguous POST failure is not retried automatically.
- Success, cancellation, and provider-pending returns are distinct. Success refreshes overview and site-scoped orders with a bounded wait for Webhook fulfillment.
- A delayed or failed Webhook never causes the frontend to grant points or change a plan locally.
- Orders and receipts belong to the authenticated user and current site; disabling payments does not break the account page.
- Local-currency Checkout records both the USD package amount and the customer's actual amount/currency without mixing the two.
- Cancel renewal calls `POST /api/v1/orders/:id/subscription/cancel`, makes no refund or local entitlement change, and displays the returned `current_period_end` as Unix seconds in the user's timezone.
- A pending cancellation renders “已取消续订，将于 YYYY-MM-DD 到期” and offers resume; `POST /api/v1/orders/:id/subscription/resume` restores automatic renewal before the subscription ends.
- One-time, unpaid, refunded, and ended orders show no renewal controls. Request buttons are locked while pending, and failed requests retain the previous state.
- Cancel renewal and immediate refund are described and implemented as separate operations; the frontend never calls an admin refund route.
- Browser bundles contain no payment-provider, site, admin, or fulfillment secret.

## Analytics

- Exactly one request is accepted on initial load and one on each real SPA navigation.
- Refresh does not create a new visitor ID; private/incognito storage may do so.
- Auth callback and duplicate URL events are not counted.
- Today/month PV and UV appear for the correct site in Star Admin.

## Security and compatibility

- Browser bundles contain no admin, site, or upstream provider secret.
- CORS works only from registered origins.
- Existing routes, saved creations, pricing display, and mobile layout still work.
- Only models returned for the assigned `site_id` can be shown or called. A UI-only display subset does not require a server allowlist; when an administrator does change the optional allowlist, the new `model_policy_version` replaces stale cached entries.
- Inherited and custom site prices match `/v1/pricing/quote` and the task creation response; no request submits a client-controlled price.
- Failure of analytics or an upstream provider does not break login or the rest of the page.


## Optional delegated pricing

- A dedicated token is stored only in the site's trusted backend; its management endpoint authenticates site administrators.
- Preview does not mutate prices; publication uses the etag reviewed by the administrator.
- 1K/2K/4K and multi-image quotes match the configured values; other sites remain unchanged.
- Stale writes return 409; read-only, expired and revoked credentials cannot publish.
- Inheritance and same-site rollback work; the frontend refreshes cached catalog/rules after publication.
