diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..72f7bfc --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +# Polar.sh Integration +POLAR_ACCESS_TOKEN=your_polar_access_token_here +POLAR_30SEC_PRODUCT_ID=your_30sec_product_id_here +POLAR_60SEC_PRODUCT_ID=your_60sec_product_id_here +POLAR_SUCCESS_URL=https://whiskey.fm/sponsor/success + +# Optional: Set to "sandbox" for testing, "production" for live (defaults to production) +PUBLIC_POLAR_SERVER=production diff --git a/.gitignore b/.gitignore index 9c3b079..8c34ec8 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ test-results/ # macOS-specific files .DS_Store + +# AI settings +AGENTS.md diff --git a/README.md b/README.md index 1d868cc..7e19262 100644 --- a/README.md +++ b/README.md @@ -110,3 +110,38 @@ see fit. We use Turso and Astro DB to setup guests per episode. If you would also like to do this, you will need a Turso account. + +## Polar.sh Checkout Integration + +This site uses Polar.sh for sponsor checkout. To set it up: + +1. **Get your Polar credentials:** + - Log in to your [Polar dashboard](https://polar.sh) + - Go to Settings → API to get your access token + - Create two products for your sponsorship packages (30-second and 60-second ads) + - Note the product IDs from each product's page + +2. **Configure environment variables:** + Create a `.env` file in the root directory with: + ```env + POLAR_ACCESS_TOKEN=your_polar_access_token_here + POLAR_30SEC_PRODUCT_ID=your_30sec_product_id_here + POLAR_60SEC_PRODUCT_ID=your_60sec_product_id_here + POLAR_SUCCESS_URL=https://whiskey.fm/sponsor/success + ``` + +3. **Test the integration:** + - For testing, you can set `PUBLIC_POLAR_SERVER=sandbox` in your `.env` + - Visit `/sponsor` and click on either sponsorship option + - You'll be redirected to Polar's checkout page + - After successful payment, users return to `/sponsor/success` + +4. **Go live:** + - Remove `PUBLIC_POLAR_SERVER` or set it to `production` + - Ensure your product IDs are for production products + - Test with a real payment to confirm everything works + +The integration uses the `@polar-sh/astro` package which provides: +- Server-side checkout session creation at `/api/checkout` +- Automatic tax compliance through Polar's Merchant of Record service +- Support for multiple products and dynamic pricing diff --git a/package.json b/package.json index dea2ecb..7e19d5e 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,11 @@ "test:unit": "vitest" }, "dependencies": { + "@astrojs/db": "^0.18.1", "@astrojs/preact": "^4.1.1", + "@astrojs/sitemap": "^3.6.0", "@astrojs/vercel": "^8.2.11", + "@polar-sh/astro": "^0.5.0", "@preact/signals": "^2.3.2", "@vercel/analytics": "^1.5.0", "@vercel/speed-insights": "^1.2.0", @@ -32,12 +35,11 @@ "preact": "^10.27.2", "rss-to-json": "^2.1.1", "schema-dts": "^1.1.5", - "valibot": "^1.1.0" + "valibot": "^1.1.0", + "zod": "^4.1.12" }, "devDependencies": { "@astrojs/check": "^0.9.5", - "@astrojs/db": "^0.18.1", - "@astrojs/sitemap": "^3.6.0", "@eslint/js": "^9.38.0", "@playwright/test": "^1.56.1", "@tailwindcss/forms": "^0.5.10", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a44cd8f..322d600 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,21 @@ importers: .: dependencies: + '@astrojs/db': + specifier: ^0.18.1 + version: 0.18.1 '@astrojs/preact': specifier: ^4.1.1 version: 4.1.1(@babel/core@7.28.4)(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(preact@10.27.2)(yaml@2.8.1) + '@astrojs/sitemap': + specifier: ^3.6.0 + version: 3.6.0 '@astrojs/vercel': specifier: ^8.2.11 version: 8.2.11(astro@5.15.0(@types/node@24.9.1)(@vercel/functions@2.2.13)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.52.5)(typescript@5.9.3)(yaml@2.8.1))(react@19.0.0)(rollup@4.52.5) + '@polar-sh/astro': + specifier: ^0.5.0 + version: 0.5.0(astro@5.15.0(@types/node@24.9.1)(@vercel/functions@2.2.13)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.52.5)(typescript@5.9.3)(yaml@2.8.1)) '@preact/signals': specifier: ^2.3.2 version: 2.3.2(preact@10.27.2) @@ -44,16 +53,13 @@ importers: valibot: specifier: ^1.1.0 version: 1.1.0(typescript@5.9.3) + zod: + specifier: ^4.1.12 + version: 4.1.12 devDependencies: '@astrojs/check': specifier: ^0.9.5 version: 0.9.5(prettier-plugin-astro@0.14.1)(prettier@3.6.2)(typescript@5.9.3) - '@astrojs/db': - specifier: ^0.18.1 - version: 0.18.1 - '@astrojs/sitemap': - specifier: ^3.6.0 - version: 3.6.0 '@eslint/js': specifier: ^9.38.0 version: 9.38.0 @@ -813,6 +819,18 @@ packages: engines: {node: '>=18'} hasBin: true + '@polar-sh/adapter-utils@0.3.0': + resolution: {integrity: sha512-4B8uVlB6u2iwbF7COtAcaYbtW98+pSHEKwHIf0bUKC6xyClqa/3NivrtFXaoVv/tmFz6LRmWm7yloIlFjIR63g==} + + '@polar-sh/astro@0.5.0': + resolution: {integrity: sha512-Bk9SNuD55rrob8BsAu787r+OtM1m+/XnmpwK+5ulp0eXiB5eiCvSXau61wzLIpvsfybU/8X5h15xSA6rP8S+Kw==} + engines: {node: '>=16'} + peerDependencies: + astro: ^5.0.0 + + '@polar-sh/sdk@0.40.2': + resolution: {integrity: sha512-c6KLRAuJ4DhqBEBt2TCoGe0azXY41EuQDA3dI6FRUdhS3WzXFUJSUNiOji40qLzm4k/ey+OgKKuoZ1s7foZdnA==} + '@preact/preset-vite@2.10.2': resolution: {integrity: sha512-K9wHlJOtkE+cGqlyQ5v9kL3Ge0Ql4LlIZjkUTL+1zf3nNdF88F9UZN6VTV8jdzBX9Fl7WSzeNMSDG7qECPmSmg==} peerDependencies: @@ -991,6 +1009,9 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@stablelib/base64@1.0.1': + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} @@ -2000,6 +2021,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-sha256@1.3.0: + resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} @@ -3290,6 +3314,9 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + standardwebhooks@1.0.0: + resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} + std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} @@ -3991,6 +4018,9 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.1.12: + resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -4737,6 +4767,21 @@ snapshots: dependencies: playwright: 1.56.1 + '@polar-sh/adapter-utils@0.3.0': + dependencies: + '@polar-sh/sdk': 0.40.2 + + '@polar-sh/astro@0.5.0(astro@5.15.0(@types/node@24.9.1)(@vercel/functions@2.2.13)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.52.5)(typescript@5.9.3)(yaml@2.8.1))': + dependencies: + '@polar-sh/adapter-utils': 0.3.0 + '@polar-sh/sdk': 0.40.2 + astro: 5.15.0(@types/node@24.9.1)(@vercel/functions@2.2.13)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.52.5)(typescript@5.9.3)(yaml@2.8.1) + + '@polar-sh/sdk@0.40.2': + dependencies: + standardwebhooks: 1.0.0 + zod: 3.25.76 + '@preact/preset-vite@2.10.2(@babel/core@7.28.4)(preact@10.27.2)(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.1))': dependencies: '@babel/core': 7.28.4 @@ -4897,6 +4942,8 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@stablelib/base64@1.0.1': {} + '@standard-schema/spec@1.0.0': {} '@swc/helpers@0.5.17': @@ -5971,6 +6018,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-sha256@1.3.0: {} + fast-uri@3.1.0: {} fast-xml-parser@4.5.3: @@ -7496,6 +7545,11 @@ snapshots: stackback@0.0.2: {} + standardwebhooks@1.0.0: + dependencies: + '@stablelib/base64': 1.0.1 + fast-sha256: 1.3.0 + std-env@3.10.0: {} stop-iteration-iterator@1.1.0: @@ -8107,4 +8161,6 @@ snapshots: zod@3.25.76: {} + zod@4.1.12: {} + zwitch@2.0.4: {} diff --git a/public/android-chrome-192x192.png b/public/android-chrome-192x192.png index 370f456..72e2a4b 100644 Binary files a/public/android-chrome-192x192.png and b/public/android-chrome-192x192.png differ diff --git a/public/android-chrome-384x384.png b/public/android-chrome-384x384.png deleted file mode 100644 index a0fe272..0000000 Binary files a/public/android-chrome-384x384.png and /dev/null differ diff --git a/public/android-chrome-512x512.png b/public/android-chrome-512x512.png new file mode 100644 index 0000000..15ce056 Binary files /dev/null and b/public/android-chrome-512x512.png differ diff --git a/public/apple-touch-icon-120x120.png b/public/apple-touch-icon-120x120.png deleted file mode 100644 index b18eaf0..0000000 Binary files a/public/apple-touch-icon-120x120.png and /dev/null differ diff --git a/public/apple-touch-icon-152x152.png b/public/apple-touch-icon-152x152.png deleted file mode 100644 index bf650bd..0000000 Binary files a/public/apple-touch-icon-152x152.png and /dev/null differ diff --git a/public/apple-touch-icon-180x180.png b/public/apple-touch-icon-180x180.png deleted file mode 100644 index faa78d2..0000000 Binary files a/public/apple-touch-icon-180x180.png and /dev/null differ diff --git a/public/apple-touch-icon-60x60.png b/public/apple-touch-icon-60x60.png deleted file mode 100644 index b06679e..0000000 Binary files a/public/apple-touch-icon-60x60.png and /dev/null differ diff --git a/public/apple-touch-icon-76x76.png b/public/apple-touch-icon-76x76.png deleted file mode 100644 index f8cc074..0000000 Binary files a/public/apple-touch-icon-76x76.png and /dev/null differ diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png index faa78d2..6873132 100644 Binary files a/public/apple-touch-icon.png and b/public/apple-touch-icon.png differ diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png index 23d11fd..10a2b25 100644 Binary files a/public/favicon-16x16.png and b/public/favicon-16x16.png differ diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png index 95d15e6..0cf94d1 100644 Binary files a/public/favicon-32x32.png and b/public/favicon-32x32.png differ diff --git a/public/favicon.ico b/public/favicon.ico index 03d85ed..ef1388e 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/mstile-150x150.png b/public/mstile-150x150.png index d81d0d0..3bdb633 100644 Binary files a/public/mstile-150x150.png and b/public/mstile-150x150.png differ diff --git a/public/safari-pinned-tab.svg b/public/safari-pinned-tab.svg index 5e5f081..f9c73ba 100644 --- a/public/safari-pinned-tab.svg +++ b/public/safari-pinned-tab.svg @@ -2,25 +2,524 @@ Created by potrace 1.14, written by Peter Selinger 2001-2017 - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/site.webmanifest b/public/site.webmanifest index a1553eb..1f1e1fb 100644 --- a/public/site.webmanifest +++ b/public/site.webmanifest @@ -1,6 +1,6 @@ { - "name": "", - "short_name": "", + "name": "WWW", + "short_name": "WWW", "icons": [ { "src": "/android-chrome-192x192.png", @@ -8,8 +8,8 @@ "type": "image/png" }, { - "src": "/android-chrome-384x384.png", - "sizes": "384x384", + "src": "/android-chrome-512x512.png", + "sizes": "512x512", "type": "image/png" } ], diff --git a/src/components/AdPackageCard.astro b/src/components/AdPackageCard.astro index 893e125..43755a7 100644 --- a/src/components/AdPackageCard.astro +++ b/src/components/AdPackageCard.astro @@ -3,9 +3,10 @@ export interface Props { bullets: Array; heading: string; price: string; + productId: string; } -const { bullets, heading, price } = Astro.props; +const { bullets, heading, price, productId } = Astro.props; ---
@@ -37,7 +38,7 @@ const { bullets, heading, price } = Astro.props; diff --git a/src/pages/sponsor/success.astro b/src/pages/sponsor/success.astro new file mode 100644 index 0000000..eec681e --- /dev/null +++ b/src/pages/sponsor/success.astro @@ -0,0 +1,36 @@ +--- +import Layout from '../../layouts/Layout.astro'; +--- + + + +