openvibers/openvibe.billing Commits

/policy: a canonical link (config.baseUrl + /policy) and the network's app icon inline (openvibe-shared/app-icon), so the browser no longer asks the API for /favicon.ico and logs its 404 on every visit; both found by the browser check (Host scripts/browser-check.js, WS-Q task 3)

OP OpenVibers committed 26 Sept 2026 · f7ff3cd2b8 · 2 files, +12 −2


Changes

ddiff --git a/server/policy.js b/server/policy.js
iindex cb2a544..3a490f4 100644
---- a/server/policy.js
++++ b/server/policy.js
@@@ -58,7 +58,12 @@ function policyData(rates, { authority = 'live' } = {}) {
};
}
--function renderPolicy(d) {
++// The network's app icon inline: without a <link rel=icon> the browser asks for /favicon.ico, which Billing's
++// API answers 404 (a console error on every visit, found by the browser check).
++const ICON_URI = 'data:image/svg+xml,' + encodeURIComponent(require('openvibe-shared/app-icon').favicon({ site: 'network' })).replace(/'/g, '%27');
++
++/** The /policy page; `baseUrl` (config.baseUrl) makes its canonical URL. */
++function renderPolicy(d, { baseUrl = 'https://billing.openvibe.network' } = {}) {
const s = d.subscription, c = d.cashout, p = d.purchase;
const tierRows = p.tiers.map((t) => `<tr><td>${num(t.from)}${t.to == null ? ' or more' : `–${num(t.to)}`}</td><td>${usd(t.price_per_100_cents)}</td><td>${usd(t.creator_value_per_100_cents)}</td><td>${pct(t.openvibe_keeps_pct)}</td></tr>`).join('');
const ledger = d.authority === 'billing'
@@@ -67,6 +72,8 @@ function renderPolicy(d) {
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Billing policy · OpenVibe</title>
<meta name="description" content="What you pay, what creators receive, what OpenVibe keeps, and how cashouts, holds and refunds work.">
++<link rel="canonical" href="${esc(String(baseUrl).replace(/\/$/, ''))}/policy">
++<link rel="icon" type="image/svg+xml" href="${ICON_URI}">
<link rel="alternate" type="application/json" href="/policy.json">
<style>
:root{color-scheme:light dark;--bg:#f6f7fb;--fg:#141824;--muted:#555d70;--card:#fff;--line:#dde1ea;--accent:#3d63dd}
@@@ -128,7 +135,7 @@ function policyRouter({ config }) {
const data = () => policyData(config.rates, { authority: config.authority });
r.get('/policy', (req, res) => {
res.setHeader('Cache-Control', 'public, max-age=300');
-- res.type('html').send(renderPolicy(data()));
++ res.type('html').send(renderPolicy(data(), { baseUrl: config.baseUrl }));
});
r.get('/policy.json', (req, res) => {
res.setHeader('Cache-Control', 'public, max-age=300');
ddiff --git a/test/policy.test.js b/test/policy.test.js
iindex 78a8f11..4a6d14c 100644
---- a/test/policy.test.js
++++ b/test/policy.test.js
@@@ -30,6 +30,9 @@ const { policyData } = require('../server/policy');
assert.ok(html.includes(s), `the page says ${s}`);
}
assert.ok(html.includes('href="https://openvibe.network/terms"'), 'the terms link is the Network terms page');
++ // One canonical URL on the configured origin, and an icon in the page (no /favicon.ico request to 404).
++ assert.deepStrictEqual(html.match(/<link rel="canonical" href="[^"]*">/g), [`<link rel="canonical" href="${t.config.baseUrl}/policy">`]);
++ assert.match(html, /<link rel="icon" type="image\/svg\+xml" href="data:image\/svg\+xml,%3Csvg/);
r = await fetch(`${t.base}/policy.json`);
assert.deepStrictEqual(await r.json(), JSON.parse(JSON.stringify(d)));
r = await fetch(`${t.base}/robots.txt`);