${thumbnail}
@@ -147,7 +146,7 @@ export class Banner extends LitElement {
${descriptionElement}
`
}
+
+ get extensionUrl(): string {
+ return getExtensionHref('banner')
+ }
}
interface BannerState {
diff --git a/components/src/offerwall/components/install-required/install-required.ts b/components/src/offerwall/components/install-required/install-required.ts
index 9ec7abb9..4e061c9e 100644
--- a/components/src/offerwall/components/install-required/install-required.ts
+++ b/components/src/offerwall/components/install-required/install-required.ts
@@ -6,11 +6,10 @@ import iconWallet from '@c/assets/icon_wallet.svg?raw'
import iconClose from '@c/assets/icon_x_close.svg?raw'
import { PoweredByInterledger } from '@c/shared/powered-by-interledger'
import { WebMonetizationHeader } from '@c/shared/web-monetization-header'
-import { getWebMonetizationLinkHref } from '@c/utils'
+import { getExtensionHref } from '@shared/utils/extension'
import styles from './install-required.css?raw'
import styleTokens from '../../vars.css?raw'
-const TITLE = 'Web Monetization'
const HEADER_TEXT = `Get the Web Monetization Extension to be able to support us`
const STEP_1 = `You'll need a Web Monetization compatible wallet to use with the extension.`
const STEP_2 = `Install the Web Monetization extension from your browser's web store. This includes getting and/or connecting to your wallet.`
@@ -37,7 +36,7 @@ export class InstallRequired extends LitElement {
render() {
return html`
-
+
${HEADER_TEXT}
@@ -91,10 +90,7 @@ export class InstallRequired extends LitElement {
}
get extensionUrl(): string {
- const url = new URL(getWebMonetizationLinkHref(navigator.userAgent))
- url.searchParams.set('utm_source', window.location.origin)
- url.searchParams.set('utm_medium', 'offerwall-embed')
- return url.href
+ return getExtensionHref('offerwall')
}
#onExtensionLinkClick = (ev: MouseEvent) => {
diff --git a/components/src/utils.ts b/components/src/utils.ts
index 1b32c228..3d20f9eb 100644
--- a/components/src/utils.ts
+++ b/components/src/utils.ts
@@ -55,23 +55,3 @@ function getCustomFontData(fontName: FontFamilyKey, baseUrl: string) {
}
}
}
-
-/**
- * Gets the appropriate Web Monetization extension download link based on the user agent
- * @param userAgent - The user agent string from navigator.userAgent
- * @returns The download URL for the Web Monetization extension
- */
-export const getWebMonetizationLinkHref = (userAgent: string): string => {
- if (userAgent.includes('Firefox')) {
- return 'https://addons.mozilla.org/en-US/firefox/addon/web-monetization-extension/'
- } else if (
- userAgent.includes('Chrome') &&
- !userAgent.includes('Edg') &&
- !userAgent.includes('OPR')
- ) {
- return 'https://chromewebstore.google.com/detail/web-monetization/oiabcfomehhigdepbbclppomkhlknpii'
- } else if (userAgent.includes('Edg')) {
- return 'https://microsoftedge.microsoft.com/addons/detail/web-monetization/imjgemgmeoioefpmfefmffbboogighjl'
- }
- return 'https://webmonetization.org/'
-}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f38ffd42..552813c8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -351,6 +351,9 @@ importers:
'@interledger/open-payments':
specifier: ^7.1.3
version: 7.1.3
+ '@shared/types':
+ specifier: workspace:^
+ version: link:../types
packages:
diff --git a/shared/types/index.ts b/shared/types/index.ts
index c2fdb1cd..532143b7 100644
--- a/shared/types/index.ts
+++ b/shared/types/index.ts
@@ -288,3 +288,11 @@ export const FONT_FAMILY_OPTIONS = [
] as const
export type FontFamilyKey = (typeof FONT_FAMILY_OPTIONS)[number]
+
+export type UtmParams = {
+ utm_source: string
+ utm_medium: string
+ utm_campaign?: string
+ utm_content?: string
+ utm_term?: string
+}
diff --git a/shared/utils/extension.ts b/shared/utils/extension.ts
new file mode 100644
index 00000000..ecdb75cd
--- /dev/null
+++ b/shared/utils/extension.ts
@@ -0,0 +1,80 @@
+import type { Tool, UtmParams } from '@shared/types'
+import { urlWithParams } from './index'
+
+type BrowserId = NonNullable>
+
+const URL_MAP: Record = {
+ chrome: `https://chromewebstore.google.com/detail/web-monetization/oiabcfomehhigdepbbclppomkhlknpii`,
+ chromium: `https://chromewebstore.google.com/detail/web-monetization/oiabcfomehhigdepbbclppomkhlknpii`,
+ edge: `https://microsoftedge.microsoft.com/addons/detail/web-monetization/imjgemgmeoioefpmfefmffbboogighjl`,
+ firefox: `https://addons.mozilla.org/en-US/firefox/addon/web-monetization-extension/`,
+ safari: `https://apps.apple.com/app/web-monetization/id6754325288`,
+}
+
+export function getBrowserSupportForExtension(ua: string, vendor = '') {
+ const isMobile =
+ /Mobi|Android|iPhone|iPad|iPod/i.test(ua) ||
+ (navigator.maxTouchPoints > 0 && /Macintosh/.test(ua))
+ const isMacOS = /Macintosh/i.test(ua) && !isMobile
+
+ // Firefox (Desktop & Android supported, iOS excluded)
+ if (/Firefox/i.test(ua) && !/FxiOS/i.test(ua)) {
+ return 'firefox' // Both Desktop and Android
+ }
+
+ // Safari (macOS supported, iOS/iPadOS excluded)
+ if (
+ /Safari/i.test(ua) &&
+ /Apple Computer/i.test(vendor) &&
+ !/Chrome|CriOS|Android/i.test(ua)
+ ) {
+ if (isMacOS) {
+ return 'safari'
+ } else {
+ return null // Identified as Safari, but on mobile/iPad
+ }
+ }
+
+ // Chromium-based Browsers
+ // Chromium Rule: Supported on Desktop Only
+ // (Excludes Chrome Android, Chrome iOS, Edge Mobile, etc.)
+ if (/Chrome|CriOS|Edg|Vivaldi|Opr|Brave/i.test(ua)) {
+ // Determine the specific flavor
+ if (/Edg/i.test(ua)) {
+ return isMobile ? null : 'edge'
+ } else if (/Vivaldi/i.test(ua) || /Opr/i.test(ua) || /Brave/i.test(ua)) {
+ return isMobile ? null : 'chromium'
+ } else {
+ return isMobile ? null : 'chrome'
+ }
+ }
+
+ return null
+}
+
+export function getExtensionUrl(browserId: BrowserId, utm?: UtmParams): URL {
+ const url = URL_MAP[browserId]
+ return urlWithParams(url, utm || {})
+}
+
+export function getExtensionHref(
+ tool: Tool,
+ options?: Partial<{ fallbackUrl: string; utm: UtmParams }>,
+): string {
+ const utm: UtmParams = {
+ utm_source: window.location.hostname,
+ utm_medium: `tools.embed.${tool}`,
+ ...options?.utm,
+ }
+ const fallbackUrl = options?.fallbackUrl || 'https://webmonetization.org'
+
+ const browserId = getBrowserSupportForExtension(
+ navigator.userAgent,
+ navigator.vendor,
+ )
+ if (!browserId) {
+ console.warn('Using on browser that does not have WM extension support')
+ return urlWithParams(fallbackUrl, utm).href
+ }
+ return getExtensionUrl(browserId, utm).href
+}
diff --git a/shared/utils/package.json b/shared/utils/package.json
index 8c191be2..9925cd35 100644
--- a/shared/utils/package.json
+++ b/shared/utils/package.json
@@ -3,6 +3,7 @@
"version": "0.0.1",
"private": true,
"devDependencies": {
- "@interledger/open-payments": "^7.1.3"
+ "@interledger/open-payments": "^7.1.3",
+ "@shared/types": "workspace:^"
}
}