Skip to content

feat(courses): pay for the club in TON on the site, any language - #1638

Merged
gHashTag merged 1 commit into
mainfrom
feat/courses-pay-ton
Oct 13, 2026
Merged

gHashTag merged 1 commit into
mainfrom
feat/courses-pay-ton

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

What

Pay for the club in TON on t27.ai, any language, no card and no Telegram. Built on the render API from gHashTag/999-multibots-telegraf#4005 (merged).

{
  "version": 1,
  "head_sha": "6e6c6df22bcccbdac80f30a900a2755f1336279e",
  "summary": "Readers in any language can now pay for the club in TON on t27.ai, without a card or Telegram. The gate shows one email field with a Pay N TON button, then the amount, address and comment to copy and a wallet button, and opens the course by itself once the render sees the payment. Built against the render API merged in gHashTag/999-multibots-telegraf#4005.",
  "changes": [
    "src/lib/courseLadder.ts: checkoutOffer reads ton.on and the TON amount; startTonCheckout posts pay ton; readOrder understands TON transfers and expired orders; only ton://transfer links are accepted",
    "src/pages/Course.tsx: PayForm with one email and rouble and/or TON buttons; TonWait shows amount, address, comment with copy buttons and a wallet button; the order poll waits up to 75 minutes for TON and reports expiry",
    "specs/ladder/course-ladder.t27 + i18n/course-ladder.ru.json: twelve TON words; PAID_LATER now only says card payment in dollars is not built",
    "src/pages/Course.css: layout of the TON rows"
  ],
  "tests": [
    {
      "command": "npm run check:course-ladder && npm run test:course-ladder && npm run check:course-pages && npm run check:aria",
      "result": "61 words in 2 languages up to date; 5 tests pass; pages and ARIA references clean",
      "status": "passed",
      "evidence": "local run in apps/website on head 6e6c6df22 after regenerating the words"
    },
    {
      "command": "npx vite build",
      "result": "built in 18.67s with no errors",
      "status": "passed",
      "evidence": "local vite build in apps/website on head 6e6c6df22"
    },
    {
      "command": "browser on vite dev with the render answers mocked: three courses open, fourth course, pay in TON",
      "result": "form, transfer details, ton:// link, order in the address, then key saved and Welcome to the club after the mocked payment",
      "status": "passed",
      "evidence": "BrowserOS neo check against the dev server with window.fetch mocked for the render"
    }
  ],
  "limitations": [
    "No real TON payment was made; TON stays off on the site until TON_WALLET_ADDRESS and SESSION_SIGNING_KEY are set on the render service.",
    "Whether Tonkeeper and Telegram Wallet open the ton://transfer link on every platform was not tested."
  ],
  "tags": [
    "courses",
    "payments",
    "ton",
    "ux"
  ],
  "blog": {
    "title": "Paying for the club in TON, right on the course page",
    "summary": "Any reader can now pay for the club in TON from any wallet on t27.ai, and the course opens itself when the payment lands.",
    "outline": [
      "Until now a reader outside Russia could only pay for the club in Telegram Stars inside the bot, which cost us every reader without Telegram.",
      "The course gate now shows one email field with a Pay in TON button for every language, next to roubles for readers who use Russian.",
      "After the press the page shows the exact amount, the address and a short comment, each one press to copy, and a button that opens a wallet.",
      "The order lives in the page address, so a reload keeps waiting, and the course opens by itself as soon as the render sees the transfer on chain.",
      "Only TON is accepted and the project token is never part of the checkout, because the company facts say that token is never sold."
    ]
  }
}

🤖 Generated with Claude Code

The render now answers the checkout with ton.on and the amount at the current TON
rate (gHashTag/999-multibots-telegraf#4005). The gate shows one email field with
"Pay N TON" for every reader (next to roubles for Russian readers); after the press
it shows amount, address and comment to copy and a ton:// wallet button, keeps the
order in the address and opens the course itself once the payment arrives. An
expired order says so. Only TON, never $TRI (FACTS.md s.6).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gHashTag gHashTag added the owner-approved-foreign Owner approved hand-written non-t27 code in this PR label Oct 13, 2026
@github-actions github-actions Bot added the status:in-progress 🔵 Agent working label Oct 13, 2026
@tri-claw

tri-claw Bot commented Oct 13, 2026

Copy link
Copy Markdown

Summary by t27-bees

Checked by the t27 compiler

t27c parse, parse-complete and typecheck on each changed .t27 file at 6e6c6df. Nothing was generated, built or run. A pass means the file is well formed, not that it is right.

  • apps/website/specs/ladder/course-ladder.t27: parses and typechecks.

Read by the model

Worth a look

  • src/lib/courseLadder.ts:115–123 — checkoutOffer returns { on: false, ton: { on: true, ... } } when only TON is enabled. Callers check offer.ton?.on directly, but offer.on being false while a payment method is live is inconsistent and could trip future logic that relies on offer.on as "any payment available".

  • src/lib/courseLadder.ts:145–146 — startTonCheckout calls readPromo() and readSource() which aren't in the diff's import list. If they're not exported from this module, the build would fail (though the test suite passes, so they likely exist elsewhere in the file).

  • src/pages/Course.tsx:445–448 — The polling loop extends limit to 75 minutes on every iteration where r.ton is present. Since started is fixed at loop entry, this doesn't extend the deadline further, but resetting limit repeatedly is confusing. If the render ever returns ton without status: 'expired' past the order's actual expiry, the loop would continue until the 75-minute cap.

  • src/pages/Course.tsx:580–581 — navigator.clipboard.writeText can reject (e.g., on HTTP or without user gesture). The .then() handler only runs on success; the catch is empty, so a silent failure leaves the user thinking it copied. The click handler provides a gesture, but HTTPS is required — the dev server may be HTTP.

  • src/pages/Course.tsx:679–681 — TonWait is rendered when paying === 'waiting' || paying === 'slow' and ladder.ton exists. If the order expires between polls, paying becomes 'expired' and the expired message shows, but there's a brief window where ton is still set and paying hasn't flipped yet — the user could see stale payment details.


Read: 6 files, +242 -36, the whole diff. Head 6e6c6df. Model: openai-compatible/nvidia/nemotron-3-ultra-550b-a55b. Commands: @t27-bees review | summary | help | pause | resume. Free for every repository.

@gHashTag
gHashTag merged commit ee51235 into main Oct 13, 2026
19 of 20 checks passed
@github-actions github-actions Bot added status:completed Done and removed status:in-progress 🔵 Agent working labels Oct 13, 2026
github-actions Bot added a commit that referenced this pull request Oct 13, 2026
Merge pull request #1638 from gHashTag/feat/courses-pay-ton

feat(courses): pay for the club in TON on the site, any language
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

owner-approved-foreign Owner approved hand-written non-t27 code in this PR status:completed Done

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant