Getting Started

Payments

Learn how NuxtStart handles payments, including setup, processing, and managing webhooks.

Overview

We use Polar as our payments. Polar is modern payment provider that supports bunch of features out of the box like subscriptions, webhooks, entitlements and more.

Setting Up Polar

  • Create polar account
  • Create sandbox API keys for testing
  • Add API keys to your NuxtStart project configuration

Syncing Polar Data

We sync Polar products, orders and subscriptions to our database to make it available for our application.

Webhooks

We rely on Polar's webhooks to keep our database in sync with any changes happening in Polar. Whenever a relevant event happens in Polar (like new purchase, subscription update etc), Polar will send a webhook to our backend with the details of the event. We have webhook handlers set up to process these webhooks and update our database accordingly.

In critical events like new purchase or subscription plan change, we don't rely solely on webhooks as they may get delayed. Instead, we manually fetch the latest data from Polar API right after the action to ensure our database is updated in near real-time. For non-critical updates, we can rely on webhooks alone.

Checkout our blog on handling webhooks in Nuxt.

Products

As mentioned about Products are synced from Polar to our database via webhooks. However, if you already have products created on Polar before setting up NuxtStart & Webhooks, you can use syncPolarProducts nitro task to sync all existing products from Polar to your database. Note that, This is development only, for production we recommend you create products after deploying your project or make minor changes to existing products to trigger webhooks to sync data.

Using syncPolarProducts Nitro Task

While in development, easiest way to trigger nitro task is to use Nuxt Devtools. Nuxt Devtools already installed and navigate to Server Tasks tab either by clicking on three dots in the left sidebar and selecting Server Tasks or by using Command Palette (Cmd + K or Ctrl + K) and searching for Server Tasks. Once you're in Nitro Tasks tab, you should see syncPolarProducts task listed there. Once task is selected simply click on the Run button on right to execute the task. This will fetch all existing products from Polar and sync them to your database. You can also view list of sync tasks in output.

Orders

TBD

Subscriptions

TBD

Listening to Polar Webhooks

To listen to Polar webhooks, we already have webhook handlers set up in our backend. You can find them in server/api/webhooks/polar directory. However to receive webhooks in development, you need to expose your local server to the internet using tools like ngrok or localtunnel.

If you want free solution, you can use LocalTunnel. Check out our blog on Top 3 Free ngrok Alternatives for more details.

Fetching Data from Polar

We rely on Polar's webhooks to populate and update payment related data including products, orders and subscriptions. Whenever you create or update product in Polar, the changes will be reflected in NuxtStart database automatically via webhooks. Similarly, when a user makes a purchase or updates their subscription, the relevant data will be updated in NuxtStart.

Polar webhooks sends data within few seconds of the event happening, so you can expect near real-time updates in your NuxtStart application. For events like new purchase & plan changes we manually hit Polar API to fetch the latest data to ensure everything is in sync instead of waiting for webhook to arrive. For example, If a user upgrades their subscription plan, webhook may deliver within few seconds but until then user may try to access new plan features. To avoid such issues we fetch latest subscription data right after plan change. Same goes for new purchases.

We use Pinia store to manage payment related state in our NuxtStart application. It has plenty of helpful methods to sync data between Polar and NuxtStart database and also to fetch data from Database to Pinia store. This allows you to create seamless payment experiences in your NuxtStart application.

How Guest Checkout Works

flowchart TD
    %% ===== Pre-auth Purchase Flow =====
    subgraph G1["Guest Purchase (Pre-Auth)"]
        direction TB
        A1["User completes checkout"]
        A2["Payment provider (Polar) creates order & subscription"]
        A3["Webhook received by backend"]
        A4["Order stored with userId as null
and polarCustomerId from order.customer.id"] A1 --> A2 --> A3 --> A4 end %% ===== Signup Flow ===== subgraph G2["User Signup via BetterAuth"] direction TB B1["User signs up with email"] B2["BetterAuth creates user record"] B3["BetterAuth BeforeCreate / BeforeSignUp Hook"] B4["Lookup Polar customer by email"] B5{"Polar customer exists?"} B6["Attach polarCustomerId to user"] B7["Continue signup"] B1 --> B2 --> B3 --> B4 --> B5 B5 -- No --> B7 B5 -- Yes --> B6 --> B7 end %% ===== Post-Signup Linking ===== subgraph G3["Post-Signup Data Linking"] direction TB C1["BetterAuth AfterCreate / AfterSignUp Hook"] C2{"user.polarCustomerId present?"} C3["Find guest orders by polarCustomerId"] C4["Update orders: set userId"] C5["Find guest subscriptions by polarCustomerId"] C6["Update subscriptions: set userId"] C7["User now owns historical purchases"] C1 --> C2 C2 -- No --> C7 C2 -- Yes --> C3 --> C4 --> C5 --> C6 --> C7 end %% ===== Relationships (Linking Nodes forces Vertical Layout) ===== %% Linking the last node of G1 to the first of G2 A4 --> B1 %% Linking the last node of G2 to the first of G3 B7 --> C1

Tips

  • Do not rely on webhooks alone as it may get delayed. Only use them for non-critical updates. For critical updates like new purchase or plan change, manually fetch latest data from Polar API right after the action.
  • Webhook order of delivery is not guaranteed. For example, order.paid may arrive before order.created.
  • Ensure idempotency in webhook handlers to avoid duplicate processing.

Key Decisions

  • We store polarCustomerId in our user records whenever new user is registered by fetching Polar customer by email. This allows us to link any historical guest purchases (specifically for guest checkouts) made before sign up to the newly created user account. Hence, we don't have to listen for customer.created webhook which may never arrive for guest checkouts.

Copyright © 2026