diff --git a/STRIPE_SETUP.md b/STRIPE_SETUP.md new file mode 100644 index 0000000..f3cf6d5 --- /dev/null +++ b/STRIPE_SETUP.md @@ -0,0 +1,179 @@ +# SunScope Extra — Stripe Subscription Setup +## Step-by-step guide to wiring up the £2/month paywall + +SunScope is a static site with no backend server, so this guide uses +**Stripe Payment Links** — a no-code/no-server approach where Stripe +hosts the checkout page for you. You get a URL, you paste it into the +app. Done. + +--- + +## STEP 1 — Create a Stripe account + +1. Go to **https://stripe.com** and sign up (free). +2. Complete the identity verification (required before going live). +3. You'll land in the Stripe Dashboard. Make sure you're in **Test mode** + first (toggle in the top-left) — you can do the whole setup in test + mode and flip to live when ready. + +--- + +## STEP 2 — Create the £2/month product + +1. In the Stripe Dashboard sidebar, go to **Product catalogue → + Add product**. +2. Fill in: + - **Name:** `SunScope Extra` + - **Description:** `Full 14-day forecast, specialist profiles, custom columns` + - **Pricing model:** `Recurring` + - **Price:** `£2.00` + - **Billing period:** `Monthly` +3. Click **Save product**. + +--- + +## STEP 3 — Create a Payment Link + +This is the URL you'll drop into the app — Stripe hosts the whole +checkout, card capture, and subscription management for you. + +1. In the product you just created, click **Create payment link**. +2. Settings to check: + - **Quantity:** 1 (fixed) + - **Collect customer email:** Yes (you'll need this to verify subscribers) + - **Allow promotion codes:** Up to you (handy for early adopters) + - **After payment:** set the redirect URL to `https://sunscope.net/?pro=1` + (we'll use this query param to activate Pro — see Step 5) +3. Click **Create link**. +4. Copy the URL — it looks like: `https://buy.stripe.com/test_xxxxxxxxxxxxxxxx` + +--- + +## STEP 4 — Update the upsell card in app.js + +Open `assets/js/app.js`. Find **line ~913** and **line ~923**. You need +to change two things: + +### 4a — Update the pricing line (line ~913) + +Find this: +``` +£2 / month · launching soon +``` +Change it to: +``` +£2 / month · cancel any time +``` + +### 4b — Replace the mailto: button with the Stripe link (line ~923) + +Find this block (starts around line 923): +```javascript +href=${`mailto:fraxle@yahoo.co.uk?subject=${encodeURIComponent('SunScope Extra — notify me at launch')}&body=${encodeURIComponent('Hi — please let me know when SunScope Extra launches. (Triggered by ' + dayLong + ')')}`} +``` + +Replace the entire `href=` value with your Stripe Payment Link: +```javascript +href="https://buy.stripe.com/YOUR_ACTUAL_LINK_HERE" +``` + +Also change the button label on line ~938 from: +``` +Notify me at launch +``` +To: +``` +Subscribe — £2/month +``` + +And add `target="_blank" rel="noopener noreferrer"` so it opens in a +new tab (the user stays on the forecast page): +```javascript + + Subscribe — £2/month + +``` + +--- + +## STEP 5 — Activate Pro after payment (simple URL param method) + +When Stripe redirects back to `https://sunscope.net/?pro=1` after a +successful payment, the app needs to detect that and set `isPro = true`. + +Open `assets/js/app.js` and find the `isPro` state (line ~116): +```javascript +const [isPro, setIsPro] = useState(false); +``` + +Replace that single line with: +```javascript +const [isPro, setIsPro] = useState(() => { + // Check URL param first (just returned from Stripe checkout) + const params = new URLSearchParams(window.location.search); + if (params.get('pro') === '1') { + localStorage.setItem('sunscope_pro', '1'); + // Clean the URL so the param doesn't stay visible + window.history.replaceState({}, '', window.location.pathname); + return true; + } + // Check localStorage (returning Pro subscriber) + return localStorage.getItem('sunscope_pro') === '1'; +}); +``` + +That's it. After subscribing, the user lands back on the site as Pro, +and localStorage remembers them on future visits. + +> **Important caveat:** This is a trust-based system — it stores Pro +> status in the user's browser. Anyone who knows the trick can set +> `localStorage.setItem('sunscope_pro','1')` in their console. For a +> £2/month product with a small audience this is probably fine. When +> you want proper enforcement, see Step 6. + +--- + +## STEP 6 — (Optional) Proper server-side verification + +When you're ready to properly enforce subscriptions (no console tricks): + +1. **Stripe Customer Portal** — enable it in the Stripe Dashboard so + subscribers can manage/cancel their own subscriptions. +2. **Webhook → backend** — Stripe fires a `checkout.session.completed` + event when someone subscribes. You'd need a tiny serverless function + (Cloudflare Worker, Vercel Edge Function, or Netlify Function) to: + - Receive the webhook + - Store the subscriber's email + Stripe customer ID in a small DB + (or even Airtable/Supabase free tier) + - Issue a signed token (JWT) back to the browser +3. **Replace localStorage** with checking that JWT against your backend + on page load. + +This is a future step — the localStorage approach is perfectly fine for +launch. + +--- + +## STEP 7 — Go live + +When you're happy with testing: + +1. In Stripe Dashboard, flip the **Test mode** toggle to **Live mode**. +2. Repeat Steps 2–3 in live mode to get a live Payment Link URL. +3. Replace the test URL in `app.js` with the live URL. +4. Deploy. + +--- + +## Quick reference — files changed + +| File | What changed | +|------|-------------| +| `assets/js/app.js` line ~116 | `isPro` state reads from localStorage + URL param | +| `assets/js/app.js` line ~913 | Pricing copy: "cancel any time" | +| `assets/js/app.js` line ~923 | `href` → Stripe Payment Link URL | +| `assets/js/app.js` line ~938 | Button label → "Subscribe — £2/month" | diff --git a/assets/css/base.css b/assets/css/base.css index 0a61df0..d07fc6e 100644 --- a/assets/css/base.css +++ b/assets/css/base.css @@ -34,7 +34,7 @@ /* ── 1. FONTS + RESET ───────────────────────────────────────────────── */ -@import "https://fonts.bunny.net/css2?family=Fraunces:opsz,wght@9..144,300;9..144,500;9..144,700;9..144,900&family=JetBrains+Mono:wght@400;600&family=Manrope:wght@400;500;600;700&display=swap"; +@import "https://fonts.bunny.net/css2?family=Fraunces:ital,wght@0,300;0,500;0,700;0,900;1,300;1,500;1,700;1,900&family=JetBrains+Mono:wght@400;600&family=Manrope:wght@400;500;600;700&display=swap"; *, *:before, diff --git a/assets/css/header.css b/assets/css/header.css index 3cad73a..4896f70 100644 --- a/assets/css/header.css +++ b/assets/css/header.css @@ -39,9 +39,9 @@ font-style: normal; } -.utci-title .title-scope { /* "Scope" — italic */ +.utci-title .title-scope { /* "Scope" — italic light, matching about page */ font-style: italic; - font-weight: 900; + font-weight: 300; color: #1e1208; } diff --git a/assets/js/app.js b/assets/js/app.js index 3a27c82..10df9ab 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -113,7 +113,19 @@ export function UTCIForecast() { // FLIP THE `false` BELOW TO `true` TO PREVIEW THE PRO EXPERIENCE. // When this is wired to real billing/auth, replace `useState(false)` // with a check against the logged-in user. - const [isPro, setIsPro] = useState(false); + //const [isPro, setIsPro] = useState(false); + const [isPro, setIsPro] = useState(() => { + // Check URL param first (just returned from Stripe checkout) + const params = new URLSearchParams(window.location.search); + if (params.get('pro') === '1') { + localStorage.setItem('sunscope_pro', '1'); + // Clean the URL so the param doesn't stay visible + window.history.replaceState({}, '', window.location.pathname); + return true; + } + // Check localStorage (returning Pro subscriber) + return localStorage.getItem('sunscope_pro') === '1'; + }); // How many days the free tier shows. Days beyond this get a 🔒. // Bump this number if you want to give free users more access. @@ -910,7 +922,7 @@ export function UTCIForecast() { color: '#c8922a', marginTop: '10px', }}> - £2 / month · launching soon + £2 / month · cancel any time
- Notify me at launch + Subscribe — £2/month + + + Already subscribed? Restore access → + + + +
+ + +
← Back to SunScope
+
+ +