Skip to content

feat(site): buy + gated downloads for Terraphim Proxy - #48

Merged
AlexMikhalev merged 3 commits into
mainfrom
feat/issue-508-buy-download
Oct 24, 2026
Merged

AlexMikhalev merged 3 commits into
mainfrom
feat/issue-508-buy-download

Conversation

@AlexMikhalev

Copy link
Copy Markdown
Contributor

Summary

Implements the terraphim.ai side of the Terraphim Proxy purchase flow, coded against the marketplace API contract being built on root/terraphim-skills-server issues #16/#17 (no live API calls in this PR).

  • Buy button — 'Buy — $15' (one-time, no subscription) in the Your keys, your costs section of /terraphim-llm-proxy/. On click it reads terraphim_token from localStorage; absent → sign-in link to api.terraphim-skills.md/auth/oauth/github; present → GET /api/billing/checkout?product=terraphim-proxy with the Bearer token and redirects to the returned Stripe URL. 401 → sign-in prompt; other failures → 'checkout unavailable' message. Includes a One-time purchase · macOS caption linking the refund policy, plus a ghost Downloads button and a Downloads entry in the Links list.
  • Download page — new /terraphim-llm-proxy/download/ (weight after refunds, page.html). Shows a 'purchase confirmed' banner on ?checkout=success, renders the public release catalogue (GET /api/products/terraphim-proxy/releases, tolerating both array and {releases:[...]} shapes) as a table of version / asset / released / size / truncated SHA-256 (full hash in title) / action. Download buttons fetch /releases/{version}/{asset} with the Bearer token; 402 → 'purchase required' callout linking back to the buy section; success → blob saved via a hidden anchor and the response X-SHA256 displayed for shasum -a 256 verification. Signed-out visitors see a sign-in prompt in place of the download actions; an unreachable API leaves a 'releases unavailable' note rather than a blank page.
  • Privacy policy — the Purchases section now covers direct downloads: payment processed by Stripe via the Terraphim Skills marketplace (api.terraphim-skills.md), which stores the account email, the purchase, and which releases were downloaded, used solely for entitlement and refund adjudication. The 'no data whatsoever' claim is scoped explicitly to the application itself. Effective date bumped to 24 October 2026.
  • Scripts live in static/js/proxy-buy.js and static/js/proxy-download.js (Zola's markdown breaks inline <script> blocks at blank lines); page-scoped styles ride inline in the markdown, reusing .btn, .prose table and the theme's OKLCH variables.

Note: the branch also picks up the previously-unpushed 9530641 docs(proxy): refund policy for direct-download purchases commit that was already on local main — the buy button links to it, so it travels together.

zola build clean (55 pages, 0 errors).

Refs https://git.terraphim.cloud/terraphim/terraphim-llm-proxy/issues/508

- _index.md: Buy — $15 button in 'Your keys, your costs' wired to the
  Terraphim Skills checkout endpoint, sign-in fallback, ghost button to
  the downloads page, caption linking the refund policy
- download.md: releases table from the public catalogue API, Bearer-gated
  artefact downloads with 402 purchase_required handling, X-SHA256
  verification display, ?checkout=success banner, graceful API failure
- proxy-buy.js / proxy-download.js: static scripts for both flows
- privacy.md: direct-download purchases paragraph (Stripe via
  api.terraphim-skills.md; marketplace stores email, purchase and
  download records for entitlement/refund adjudication); 'no data
  whatsoever' claim scoped explicitly to the application

Refs terraphim/terraphim-llm-proxy#508
… policy, JS tests

F1: sign-in links now carry ?return_to=<absolute current page URL> so the
OAuth callback lands back on this origin with the JWT in the fragment;
both page scripts capture #token=<jwt> (and ?token= for forward-compat)
into localStorage and history.replaceState it out of the address bar.

F3: the ?checkout=success banner only reads 'purchase confirmed' when a
token is present; signed-out visitors see neutral 'checkout completed —
sign in' wording — the parameter is trivially forgeable.

F4/F5: refund policy — short version now matches the table (14 days +
no download), both CONFIRM placeholders resolved ('effective upon launch
of direct-download sales', '3 business days'), and 'downloaded' is
defined as when our service first serves a release download request —
failed/interrupted delivery does not count, ambiguous cases resolved in
the customer's favour, cross-referenced from the table.

F6: pure helpers extracted to static/js/proxy-helpers.js (UMD-style —
window.ProxyHelpers in the browser, module.exports under node) with
node:test coverage in tests/js/proxy-helpers.test.js: escaping of
script/attribute payloads, wrapped vs unwrapped release shapes, URL
encoding, humanSize/fmtDate edge cases, token-in-location parsing and
hash stripping. 15 tests, 0 deps, no mocks.

Refs terraphim/terraphim-llm-proxy#508
@AlexMikhalev

Copy link
Copy Markdown
Contributor Author

Structural review (pi-rust / gpt-5.6-sol via structural-pr-review)

Confidence: 4/5 — approved after fixes. Round 1 returned 6 findings; all verified against the diff and resolved in aaeebab.

Round 1 findings → resolution

# Severity Finding Resolution
1 Critical Sign-in could never establish terraphim_token on this origin — localStorage is origin-scoped and the OAuth callback lands on terraphim-skills.md ?return_to=/?next= handoff (server-side, skills-server#16): sign-in links now carry return_to of the current page; captureToken() stores the #token= fragment JWT into localStorage and scrubs it via history.replaceState
2 Important Token left in URL/history stripTokenFromHash + history.replaceState
3 Important Forged ?checkout=success showed "Purchase confirmed" to anyone Banner requires a token at load; signed-out visitors get neutral "sign in to confirm" wording
4 Important Refund policy: "any time" vs 14-day contradiction + [CONFIRM] placeholders Short version matches the table; placeholders resolved
5 Important "Downloaded" isn't measurable — server can only observe the served request Policy reworded to "when our service first serves a release download request"; interrupted delivery explicitly excluded, ambiguous cases favour the customer
6 Important No automated coverage Pure helpers extracted to proxy-helpers.js (UMD, no DOM); 15 node:test cases covering XSS payloads, wrapped/unwrapped shapes, URL encoding — 15/15 pass, verified locally

Verified locally

  • zola build clean (55 pages)
  • node --test tests/js/proxy-helpers.test.js → 15/15
  • esc() applied to all API-derived fields entering innerHTML; encodeURIComponent on path segments; object URLs revoked
  • Check release-link drift CI failure is pre-existing drift in README.md on main — reproduced locally on main, unrelated to this PR

Important files changed

  • static/js/proxy-helpers.js, proxy-buy.js, proxy-download.js — token capture, checkout, gated downloads
  • content/terraphim-llm-proxy/{download,refunds,privacy,_index}.md — pages + policy copy
  • tests/js/proxy-helpers.test.js — 15 cases

Last reviewed commit: aaeebab (head of feat/issue-508-buy-download)

@AlexMikhalev
AlexMikhalev merged commit c871ad8 into main Oct 24, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant