No description
Find a file
2026-07-29 10:56:50 +03:30
assets first commit 2026-07-25 17:08:19 +03:30
css first commit 2026-07-25 17:08:19 +03:30
js feat: redirect routes added 2026-07-29 10:56:50 +03:30
favicon.svg first commit 2026-07-25 17:08:19 +03:30
index.html first commit 2026-07-25 17:08:19 +03:30
README.md first commit 2026-07-25 17:08:19 +03:30

Winsoo Invite Page

A single-page invite/referral landing page for Winsoo. A visitor lands on the page via an invite link, enters their mobile number, and the page submits that number against the invite code embedded in the URL. Built with plain HTML, CSS, and JavaScript — no build step, no framework, no dependencies.

Project structure

InvitePage/
├── index.html                  Markup for the page
├── favicon.svg                 Browser tab icon
├── css/
│   └── style.css               Styles + @font-face declarations
├── js/
│   └── script.js                OAuth token fetch + invite submission logic
└── assets/
    ├── images/
    │   ├── logo.png            Winsoo wordmark
    │   └── illustration.png    Hero illustration
    └── fonts/
        └── iransans/           IRANSans font family (all weights/variants)

Running locally

The page must be served over http:// or https://, not opened directly as a file:// URL — the invite API's CORS policy is keyed to real origins, and some browsers handle a file:// "null" origin inconsistently.

python3 -m http.server 8765

Then open:

http://localhost:8765/index.html?inviteCode=<uuid>

The inviteCode query parameter is required — it's the invite code that gets sent to the API along with the submitted phone number.

How it works

  1. On page load, js/script.js requests an OAuth2 access token from Gooyal's client_credentials token endpoint, using Basic auth (base64 of client_id:client_secret) and scope accounts.invite:submit. The token is cached in memory for reuse.
  2. On submit, the phone number is normalized to E.164 format:
    • Persian (۰-۹) and Arabic-Indic (٠-٩) digits are converted to Latin digits.
    • Common prefixes (0, 98, +98, 0098) are stripped and replaced with +98.
    • The result must match a 10-digit Iranian mobile number (9XXXXXXXXX).
    • Invalid input highlights the field and shows an inline error instead of calling the API.
  3. The invite code is read from the page's own URL (?inviteCode=...) and POSTed together with the mobile number to the invite request endpoint, authenticated with the bearer token from step 1.
  4. If the invite request fails with 401 (expired/invalid token), the token is refetched once and the request is retried automatically. Any other failure surfaces the API's own status_message (falling back to a generic Persian error) in the inline message area below the input. A successful submission shows a success message.

Known limitation

js/script.js contains the OAuth client ID and client secret in plain text, which means anyone viewing the page source can read them. This is acceptable for the current staging environment, but before shipping to production, the client_credentials token exchange should move to a small backend/proxy that holds the secret server-side; the browser should never see it directly.