Admin portal #
qpayd can serve a minimal /admin page for store operators. The daemon does
not bundle the admin UI. Instead, /admin loads the @qpayd/admin browser
package from a configured URL and asks the browser to verify the downloaded
bytes with Subresource Integrity.
The config below is filled from the latest qpayd release metadata when this page
loads. Avoid @latest in production config because it can change without a
qpayd config change.
[server.admin]
enabled = true
store_id = "main"
asset_source = "__QPAYD_ADMIN_ASSET_SOURCE__"
asset_integrity = "__QPAYD_ADMIN_ASSET_INTEGRITY__"
@qpayd/admin is a browser-only UI that talks directly to the qpayd admin API.
Configure the qpayd base URL when mounting it. Add storeId to pin the panel to
one store, or omit it and let qpayd route the signed-in token to its stores.
<main id="qpayd-admin"></main>
<script type="module">
import { mountQPaydAdmin } from "@qpayd/admin";
mountQPaydAdmin("#qpayd-admin", {
baseUrl: "https://pay.example.com"
});
</script>
Set admin_allowed_origins for the store when hosting the admin panel on a
different browser origin than the API:
[[stores]]
id = "main"
admin_allowed_origins = ["https://admin.example.com"]
admin_token_env = "QPAYD_MAIN_ADMIN_TOKEN"
payout_token_env = "QPAYD_MAIN_PAYOUT_TOKEN"
If admin_token_env is configured, admin API requests must use that token. If
it is omitted, admin API requests use the store api_token_env.
If payout_token_env is configured, payout and refund actions use that token.
Set admin_token_can_payout = true on a store when one admin token should also
be able to run payout actions.
Remote admin assets require asset_integrity and must use https.
Set store_id = "main" to pin the hosted /admin page to one store. If
store_id is omitted, the page does not publish store ids in the HTML. After
login, qpayd returns the stores and scopes available to the submitted token.
Verify token routing after deploy:
curl -sS https://pay.example.com/v1/admin/session \
-H "Authorization: Bearer $QPAYD_MAIN_ADMIN_TOKEN"
Open /admin, sign in with the same token, and confirm the store and view match
the returned scopes. With store_id = "main", the page should only accept
tokens for that store. With store_id omitted, a token that reaches more than
one store should show a store selector. Repeat with a token that should not have
access and confirm qpayd rejects it.