-
Couldn't load subscription status.
- Fork 7
Added example with Nuxt #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ronitize
wants to merge
12
commits into
polarsource:main
Choose a base branch
from
ronitize:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 5 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
9bd6740
Added example with-nuxt
ronitize eb53645
Revert "Added example with-nuxt"
ronitize 1c3b4e5
Added example with-nuxt
ronitize d8f4874
Added example with-nuxt
ronitize 8f6c407
Added example with-nuxt
ronitize 16ac9a1
Removed script tag
ronitize bb1ef7c
Implemented scope.ts
ronitize c672571
Revert "Implemented scope.ts"
ronitize 0d01f76
Implemented scope.ts
ronitize c70d190
Revert "Implemented scope.ts"
ronitize 8baee6f
Implemented scope.ts
ronitize 05af0dd
Added File
ronitize File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # https://docs.polar.sh/integrate/oat | ||
| POLAR_ACCESS_TOKEN= | ||
|
|
||
| # https://docs.polar.sh/integrate/webhooks/endpoints#setup-webhooks | ||
| POLAR_WEBHOOK_SECRET= | ||
|
|
||
| # URL to redirect to after successful order | ||
| POLAR_SUCCESS_URL= | ||
|
|
||
| # use the above same approach to get the sandbox credentials. | ||
| SANDBOX_POLAR_ACCESS_TOKEN= | ||
|
|
||
| SANDBOX_POLAR_WEBHOOK_SECRET= | ||
|
|
||
| SANDBOX_POLAR_SUCCESS_URL= | ||
|
|
||
| # Polar server mode (production or sandbox) | ||
| POLAR_MODE= |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # Nuxt dev/build outputs | ||
| .output | ||
| .data | ||
| .nuxt | ||
| .nitro | ||
| .cache | ||
| dist | ||
|
|
||
| # Node dependencies | ||
| node_modules | ||
|
|
||
| # Logs | ||
| logs | ||
| *.log | ||
|
|
||
| # Misc | ||
| .DS_Store | ||
| .fleet | ||
| .idea | ||
|
|
||
| # Local env files | ||
| .env | ||
| .env.* | ||
| !.env.example |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| node_modules | ||
| .env | ||
| README.md |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "semi": false, | ||
| "printWidth": 180, | ||
| "singleQuote": true | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
|  | ||
|
|
||
| # Getting Started with Polar and Nuxt | ||
|
|
||
| This repo is a demonstration of the integration of Polar features such as Webhooks, Customer Portal and Checkout creation organization in Nuxt. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Node.js installed on your system | ||
| - Your POLAR_ACCESS_TOKEN, POLAR_WEBHOOK_SECRET, SUCCESS_URL and SANDBOX_POLAR_ACCESS_TOKEN, SANDBOX_POLAR_WEBHOOK_SECRET, SANDBOX_POLAR_SUCCESS_URL, and POLAR_MODE | ||
| > this is an optional configuration, adjust based on your needs | ||
|
|
||
|
|
||
|
|
||
| ## 1. Clone the repository | ||
|
|
||
| ```bash | ||
| npx degit polarsource/examples/with-nuxt ./with-nuxt | ||
| ``` | ||
|
|
||
| ## 2. Install dependencies: | ||
|
|
||
| ```bash | ||
| npm install | ||
| ``` | ||
|
|
||
| ## 3. Configure environment variables: | ||
|
|
||
| Create a `.env` file in the project root with your Polar credentials: | ||
|
|
||
| ```bash | ||
| cp .env.example .env | ||
| ``` | ||
|
|
||
| Add your Polar API credentials to the `.env` file: | ||
|
|
||
| ```env | ||
| POLAR_ACCESS_TOKEN= | ||
|
|
||
| POLAR_WEBHOOK_SECRET= | ||
|
|
||
| POLAR_SUCCESS_URL= | ||
|
|
||
| SANDBOX_POLAR_ACCESS_TOKEN= | ||
|
|
||
| SANDBOX_POLAR_WEBHOOK_SECRET= | ||
|
|
||
| SANDBOX_POLAR_SUCCESS_URL= | ||
|
|
||
| POLAR_MODE= | ||
| ``` | ||
|
|
||
| You can find your POLAR_ACCESS_TOKEN and POLAR_WEBHOOK_SECRET variables in your Polar dashboard settings. see `.env.example` | ||
|
|
||
| ## 4. Start Development Server | ||
|
|
||
| ```bash | ||
| npm run dev | ||
| ``` | ||
|
|
||
| Visit `http://localhost:4321` to see the demo interface. | ||
|
|
||
| ## Configuration | ||
|
|
||
| ### Polar Dashboard Setup | ||
|
|
||
| 1. **Create Products**: Set up products in your Polar dashboard | ||
| 2. **Configure Webhooks**: Add webhook endpoint `https://your-domain.com/api/webhooks/polar` | ||
| 3. **Get Credentials**: Copy your access token and webhook secret | ||
|
|
||
| ## Deployment | ||
|
|
||
| ### Vercel (Recommended) | ||
|
|
||
| 1. Connect your repository to Vercel | ||
| 1. Add environment variables in Vercel dashboard | ||
| 1. Deploy automatically on push | ||
|
|
||
| [](https://vercel.com/new/clone?repository-url=https://github.com/polarsource/examples/tree/main/with-nuxt&env=POLAR_ACCESS_TOKEN,POLAR_WEBHOOK_SECRET,POLAR_SUCCESS_URL,SANDBOX_POLAR_ACCESS_TOKEN,SANDBOX_POLAR_WEBHOOK_SECRET,SANDBOX_POLAR_SUCCESS_URL,POLAR_MODE&envDescription=Configure%20your%20Polar%20API%20credentials%20and%20mode.&envLink=https://docs.polar.sh/integrate/webhooks/endpoints#setup-webhooks) | ||
|
|
||
| ### Other Platforms | ||
|
|
||
| The project works with any platform that supports Nuxt: | ||
|
|
||
|
|
||
| - Cloudflare | ||
| - Netlify | ||
| - Node etc. | ||
|
|
||
| ## 5. Testing | ||
|
|
||
| ### Local Testing | ||
|
|
||
| 1. Use Polar's sandbox environment | ||
| 2. Test with sandbox product IDs | ||
| 3. Monitor webhook events payloads in your console and | ||
| 4. Verify your access tokens by running the `validateAccessToken.ts` test in `./scripts` | ||
|
|
||
| ```bash | ||
| npm run validate-token | ||
| ``` | ||
|
|
||
| ### Webhook Testing | ||
|
|
||
| 1. Use tools like ngrok for local webhook testing | ||
| 2. Configure webhook URL in Polar dashboard | ||
| 3. Configure `vite.server.allowedhosts` in `nuxt.config.ts` to allow it. | ||
| 4. Trigger test events from Polar dashboard | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| <template> | ||
| <div> | ||
| <NuxtLayout> | ||
| <NuxtPage /> | ||
| </NuxtLayout> | ||
| </div> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| // You can leave this script block empty. | ||
| // It's a good place for app-wide logic if you need it later. | ||
| </script> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| @import 'tailwindcss'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| <template> | ||
| <div class="bg-card border border-border overflow-hidden hover:shadow-lg transition-shadow"> | ||
| <NuxtImg :src="image || '/placeholder.svg'" :alt="name" class="w-full h-48 object-cover" /> | ||
| <div class="p-6"> | ||
| <h3 class="text-xl font-heading font-semibold text-card-foreground mb-2"> | ||
| {{ name }} | ||
| </h3> | ||
| <p class="text-muted-foreground mb-4">{{ description }}</p> | ||
| <div class="flex items-center justify-between"> | ||
| <span class="text-2xl font-medium text-primary">{{ formattedPrice }}</span> | ||
| <button @click="buyNow" class="px-4 py-2 bg-gray-200 text-primary-foreground font-medium rounded-lg hover:bg-primary/90 transition-colors cursor-pointer">Buy Now</button> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| const props = defineProps<{ | ||
| id: string | ||
| name: string | ||
| description?: string | null | ||
| image?: string | null | ||
| priceAmount?: number | ||
| priceCurrency?: string | ||
| }>() | ||
|
|
||
| const formattedPrice = computed(() => | ||
| props.priceAmount | ||
| ? new Intl.NumberFormat('en-US', { | ||
| style: 'currency', | ||
| currency: props.priceCurrency || 'USD', | ||
| }).format(props.priceAmount / 100) | ||
| : '—', | ||
| ) | ||
|
|
||
| const buyNow = () => { | ||
| const customerId = sessionStorage.getItem('customerId') | ||
| const customerEmail = sessionStorage.getItem('customerEmail') | ||
| const customerName = sessionStorage.getItem('customerName') | ||
| const url = new URL('/api/checkout', window.location.origin) | ||
|
|
||
| url.searchParams.append('products', props.id) | ||
| if (customerId) { | ||
| url.searchParams.append('customerId', customerId) | ||
| } | ||
| if (customerEmail) { | ||
| url.searchParams.append('customerEmail', customerEmail) | ||
| } | ||
| if (customerName) { | ||
| url.searchParams.append('customerName', customerName) | ||
| } | ||
| window.location.href = url.toString() | ||
| } | ||
| </script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| <template> | ||
| <section id="products" class="py-16"> | ||
| <div class="container mx-auto px-4"> | ||
| <div v-if="!products || products.length === 0" class="text-center"> | ||
| <p class="text-muted-foreground">No products available.</p> | ||
| </div> | ||
| <template v-else> | ||
| <h2 class="text-3xl font-heading font-medium text-center mb-12">Products</h2> | ||
| <div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8"> | ||
| <ProductCard | ||
| v-for="product in products" | ||
| :key="product.id" | ||
| :id="product.id" | ||
| :name="product.name" | ||
| :description="product.description" | ||
| :image="product.medias?.[0]?.publicUrl" | ||
| :priceAmount="product.prices?.[0]?.priceAmount" | ||
| :priceCurrency="product.prices?.[0]?.priceCurrency" | ||
| /> | ||
| </div> | ||
| </template> | ||
| </div> | ||
| </section> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| defineProps({ | ||
| products: Array, | ||
| }) | ||
| </script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| <template> | ||
| <div class="bg-background text-foreground font-sans"> | ||
| <header class="border-b border-border bg-card"> | ||
| <div class="container mx-auto px-4 py-4"> | ||
| <nav class="flex items-center justify-between"> | ||
| <NuxtLink to="/" class="text-2xl font-heading font-bold text-primary"> Polar with Nuxt Example </NuxtLink> | ||
| <div class="flex items-center gap-4"> | ||
| <NuxtLink to="/" class="text-muted-foreground hover:text-foreground transition-colors border p-3"> Products </NuxtLink> | ||
| <NuxtLink to="/customer-portal" class="text-muted-foreground hover:text-foreground transition-colors border p-3"> Customer Portal </NuxtLink> | ||
| </div> | ||
| </nav> | ||
| </div> | ||
| </header> | ||
| <main class="min-h-screen"> | ||
| <slot /> | ||
| </main> | ||
| </div> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| <template> | ||
| <section class="py-16"> | ||
| <div class="container mx-auto px-4"> | ||
| <div class="max-w-2xl mx-auto text-center"> | ||
| <h1 class="text-4xl font-heading font-bold text-primary mb-6">Customer Portal</h1> | ||
| <p class="text-xl text-muted-foreground mb-8">Access your purchases and manage your account</p> | ||
|
|
||
| <div class="bg-card border border-border rounded-lg p-6"> | ||
| <form @submit.prevent="accessPortal" class="space-y-4"> | ||
| <div> | ||
| <label for="name" class="block text-sm font-medium text-card-foreground mb-2"> Your Name </label> | ||
| <input | ||
| type="text" | ||
| id="name" | ||
| v-model="name" | ||
| class="w-full px-3 py-2 border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-ring bg-input text-foreground" | ||
| placeholder="Your Name" | ||
| /> | ||
| </div> | ||
| <div> | ||
| <label for="email" class="block text-sm font-medium text-card-foreground mb-2"> Enter your email address </label> | ||
| <input | ||
| type="email" | ||
| id="email" | ||
| v-model="email" | ||
| class="w-full px-3 py-2 border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-ring bg-input text-foreground" | ||
| placeholder="your@email.com" | ||
| /> | ||
| </div> | ||
| <button type="submit" class="w-full px-6 py-3 bg-gray-200 text-primary-foreground font-medium rounded-lg hover:bg-primary/90 transition-colors cursor-pointer"> | ||
| Access Portal | ||
| </button> | ||
| </form> | ||
|
|
||
| <p class="text-sm text-muted-foreground mt-4">You'll be redirected to your secure customer portal</p> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </section> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| import { ref, onMounted } from 'vue' | ||
|
|
||
| const name = ref('') | ||
| const email = ref('') | ||
|
|
||
| onMounted(() => { | ||
| name.value = sessionStorage.getItem('customerName') || '' | ||
| email.value = sessionStorage.getItem('customerEmail') || '' | ||
| }) | ||
|
|
||
| const accessPortal = () => { | ||
| const customerId = sessionStorage.getItem('customerId') | ||
|
|
||
| sessionStorage.setItem('customerName', name.value) | ||
| sessionStorage.setItem('customerEmail', email.value) | ||
|
|
||
| if (customerId) { | ||
| window.location.href = `/api/customer-portal?customerId=${customerId}` | ||
| } else if (email.value) { | ||
| window.location.href = `/api/customer-portal?customerEmail=${email.value}` | ||
| } else { | ||
| alert('Please provide an email address to access the portal.') | ||
| } | ||
| } | ||
| </script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| <template> | ||
| <div> | ||
| <ProductsGrid :products="products" /> | ||
| </div> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| const { data: products } = await useAsyncData('products', () => $fetch('/api/products')) | ||
| </script> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.