From 7c515bf8fae251b225180f4f5e47c51d02fe5ba8 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Fri, 31 Jul 2026 21:21:06 +0200 Subject: [PATCH] feat(website): serve Windows from the Microsoft Store and tag store campaigns The Windows button downloaded plezy-windows-installer.exe from the latest release. It now opens the Store listing, which brings Store-managed updates. The endpoint redirects to ms-windows-store://, so the button only resolves on Windows; macOS and Linux keep their direct release downloads. Store links carry campaign parameters (ct=Landing, utm_campaign=landing, cid=landing) so landing-page traffic separates from the README's in each store's own reporting. Play reports utm_source and utm_campaign, so no utm_medium is sent. Structured data keeps untagged canonical URLs: schema.org offers are consumed by search engines, and a rich-result click is not landing-page traffic. --- .../src/lib/components/DownloadButtons.svelte | 12 ++++++++++-- website/src/lib/content/downloads.ts | 17 +++++++++++++++-- website/src/lib/content/software_app_offers.ts | 10 +++++++--- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/website/src/lib/components/DownloadButtons.svelte b/website/src/lib/components/DownloadButtons.svelte index e554b30a..f280dec4 100644 --- a/website/src/lib/components/DownloadButtons.svelte +++ b/website/src/lib/components/DownloadButtons.svelte @@ -5,7 +5,13 @@ import AmazonIcon from "~icons/cib/amazon"; import ChevronDownIcon from "~icons/heroicons/chevron-down-solid"; import WindowsIcon from "./WindowsIcon.svelte"; - import { AMAZON_URL, linuxArchitectures, releaseAsset, storeOptions } from "$lib/content/downloads"; + import { + AMAZON_URL, + linuxArchitectures, + MICROSOFT_STORE_URL, + releaseAsset, + storeOptions, + } from "$lib/content/downloads"; const componentId = $props.id(); const linuxPanelId = `${componentId}-linux-downloads`; @@ -67,7 +73,9 @@
diff --git a/website/src/lib/content/downloads.ts b/website/src/lib/content/downloads.ts index f7f4e901..598492d0 100644 --- a/website/src/lib/content/downloads.ts +++ b/website/src/lib/content/downloads.ts @@ -16,6 +16,17 @@ export const APP_STORE_ID = '6754315964'; export const ANDROID_PACKAGE = 'com.edde746.plezy'; export const AMAZON_URL = 'https://www.amazon.com/gp/product/B0GK65CVS1'; +// Untagged product URLs for structured data. Campaign-tagged URLs belong on +// links a visitor actually clicks here; attributing a search rich-result click +// to the site's own campaign would be wrong. +export const APP_STORE_CANONICAL_URL = `https://apps.apple.com/us/app/id${APP_STORE_ID}`; +export const PLAY_STORE_CANONICAL_URL = `https://play.google.com/store/apps/details?id=${ANDROID_PACKAGE}`; + +// Microsoft Store. `cid` is the Partner Center campaign code; `referrer=appbadge` +// is the value Microsoft's own badge snippet emits for this endpoint. +export const MICROSOFT_STORE_URL = + 'https://get.microsoft.com/installer/download/9n5r1s1t68h7?referrer=appbadge&cid=landing'; + export const releaseAsset = (name: string) => `https://github.com/edde746/plezy/releases/latest/download/${name}`; @@ -23,12 +34,14 @@ export const storeOptions = { ios: { id: 'app-store', label: 'App Store', - url: `https://apps.apple.com/us/app/id${APP_STORE_ID}`, + // pt/ct/mt: App Store Analytics campaign link (Analytics > Acquisition > Campaigns). + url: `https://apps.apple.com/app/apple-store/id${APP_STORE_ID}?pt=128238902&ct=Landing&mt=8`, }, android: { id: 'play-store', label: 'Google Play', - url: `https://play.google.com/store/apps/details?id=${ANDROID_PACKAGE}`, + // referrer: URL-encoded UTM pair; Play Console reports utm_source/utm_campaign. + url: `${PLAY_STORE_CANONICAL_URL}&referrer=utm_source%3Dplezy.app%26utm_campaign%3Dlanding`, }, } as const satisfies Record<'ios' | 'android', StoreOption>; diff --git a/website/src/lib/content/software_app_offers.ts b/website/src/lib/content/software_app_offers.ts index b2234bc7..ad3149ce 100644 --- a/website/src/lib/content/software_app_offers.ts +++ b/website/src/lib/content/software_app_offers.ts @@ -1,4 +1,8 @@ -import { AMAZON_URL, storeOptions } from './downloads'; +import { + AMAZON_URL, + APP_STORE_CANONICAL_URL, + PLAY_STORE_CANONICAL_URL, +} from './downloads'; export type StorePrices = { appStorePrice: string | null; @@ -25,7 +29,7 @@ export function buildSoftwareApplicationOffers({ return [ { '@type': 'Offer', - url: storeOptions.ios.url, + url: APP_STORE_CANONICAL_URL, category: 'App Store', ...(appStorePrice === null ? {} @@ -36,7 +40,7 @@ export function buildSoftwareApplicationOffers({ }, { '@type': 'Offer', - url: storeOptions.android.url, + url: PLAY_STORE_CANONICAL_URL, category: 'Google Play', ...(playStorePrice === null ? {}