Get started
Pay for misomoon Cloud
misomoon Cloud plans are paid by card through Polar, the merchant of record. Polar sells you the plan, charges the card, adds the sales tax or VAT your country needs, and sends the invoices. misomoon never sees your card.
A plan belongs to a team (Plans). The team's owners and billing members pay for it, in Settings > Team. Admins see the plans and their prices there without the buttons. Open one of the team's sites first: Settings always shows the team of the open site.
Choose a plan
- Open Settings > Team.
- In the Plan card, select Choose Starter, Choose Growth, or Choose Scale.
- Pay in Polar's checkout. Your account's email is filled in, and Polar shows the tax for your country and the total before you pay.
- Polar sends you back to Settings > Team, which says Payment received until Polar confirms the payment, usually within a minute. The plan's limits then apply. The tracker endpoint keeps what it knows about a team for up to a minute, so a team that had stopped recording starts again within a minute after that.
A team never pays for two plans. Before it opens a checkout, misomoon asks Polar whether the team already pays for a subscription, and switches that one instead. While the team's last checkout is still taking the payment, choosing a plan again says Polar is still taking the payment for this team and opens nothing. That ends as soon as the subscription the checkout made reaches misomoon, so a team whose subscription ended right after paying can pay again at once.
Prices are in US dollars a month, as on misomoon.com/pricing.
Switch plans
- In the Plan card, select Switch to on the plan you want, such as Switch to Scale.
- Check the plan and its price, then select Switch now.
misomoon changes the subscription the team already pays for, so a team never pays for two plans. The card on file pays the new price. Polar works out the difference for the rest of the period: it charges the card for a bigger plan and credits a smaller one. If Polar schedules the switch for the next renewal, the dashboard says on which day the new plan starts, and the team stays on its current plan until then.
If Polar can't charge the card for the switch, nothing changes. Update the card in Manage billing, then switch again. A move to a smaller plan keeps every site and setting (Move to a smaller plan).
Change the card, get invoices, or cancel
Select Manage billing in the Plan card. It opens Polar's customer portal, where you can change the card, download invoices and receipts, and cancel the subscription. The portal shows every subscription paid by the same Polar customer, so a person who pays for two teams sees both. Manage billing shows once the team has paid for a plan.
Cancel a plan
Cancel in Manage billing. The plan stays until the end of the period you paid for. The Plan card shows that the plan is canceled and the day it ends. To keep it, select the plan's Keep button before that day, such as Keep Growth. Polar then charges the card again at renewal. Switching to another plan before that day also keeps the subscription.
When the period ends, the team has no plan. A team whose trial has ended then lapses: misomoon stops recording its pageviews and events, and it can't add sites (Try it free for 7 days). A team still in its trial keeps the trial's limits until the trial ends. Your sites, settings, and numbers stay, and choosing a plan again starts recording within a minute.
When a payment fails
When Polar can't charge a renewal, the Plan card says that the last payment failed, and the team keeps its plan. Polar emails the person who paid, with a link to the portal, and tries the card again 2, 7, 14, and 21 days after the first failure. Update the card in Manage billing, and Polar charges it again at once.
If every attempt fails, Polar ends the subscription and the team has no plan, as after a cancel. Choose a plan to pay again.
How Polar and misomoon keep the plan in step
This section is for whoever runs a misomoon server with billing, and for support.
Polar sends a webhook to POST /api/billing/polar/webhook for every change to a
subscription. Each one carries the whole subscription, and misomoon sets the team's plan
from it:
| Polar event | What happens to the team |
|---|---|
subscription.created, subscription.active | The plan of the subscription's product, plan_status active, and the end of the paid period. An incomplete subscription, one not paid yet, changes nothing. |
subscription.updated | The same, from the subscription as it is now: a switch, a renewal, or a recovered payment. |
subscription.canceled | The plan stays. plan_status is canceled, and the period end is the day the plan ends. |
subscription.uncanceled | plan_status is active again. |
subscription.past_due | The plan stays. plan_status is past_due while Polar tries the card again. |
subscription.revoked | The plan ends: teams.plan is NULL. The team keeps its Polar customer, so Manage billing still opens its invoices. |
misomoon finds the team from the team_id it puts in the checkout's metadata, which
Polar copies to the subscription. A subscription stays with the team it first came with.
Webhooks can arrive late and out of order. misomoon keeps every subscription in its newest
state, by Polar's modified_at, and a webhook with an older state changes nothing. A
delivery Polar retries carries the same webhook-id, and misomoon handles each id once.
A checkout asks Polar first, so a team doesn't end up with two subscriptions: Polar's
subscriptions carrying the team's id in their metadata, and the team's last checkout. A
subscription found there whose webhook hasn't come yet sets the plan, and the team
switches it instead of paying for a second one. A checkout that took the card in the last
hour holds off a new one until its subscription shows up. If a team still pays for two,
for example after a switch in the customer portal, the newer one sets the plan and the
log shows team pays for more than one subscription. Cancel and refund the other in
Polar.
A lost webhook can't keep a plan going. Once a day, at the cleanup at 03:15 UTC,
misomoon reads again from Polar each team whose paid period ended more than a day ago,
up to 50 teams a day. A renewal moves the team to the new period, and a subscription that
ended ends the plan, as subscription.revoked would.
If Polar answers that the team's subscription doesn't exist, choosing a plan opens a new checkout. misomoon marks the old subscription as ended only once Polar takes that checkout, because a token for the wrong organization also gets that answer. The daily check leaves such a subscription alone and logs it.
Plans that support sets
Support can still set teams.plan by hand (Set a team's plan):
- A team that never had a subscription keeps the plan support sets. Webhooks never touch it.
- A team with a subscription takes its plan from Polar. A change by hand lasts until Polar's next webhook for that subscription. To change such a team's plan, switch the subscription in Polar instead.
- When a team's subscription ends, its plan becomes
NULL, even one set by hand. Set it again after the revocation if the team should keep a plan. - A team on a plan set by hand can still choose a plan and pay. The subscription's plan then replaces the one set by hand.
Set up billing on your server
misomoon Cloud runs this setup. A self-hosted server has no plans and no billing: the
self-hosted template apps/api/wrangler.jsonc and the deploy bundle leave out PLANS and
every POLAR_* setting. To try billing, do these
steps in Polar's sandbox first, with POLAR_SERVER=sandbox.
The sandbox needs its own account, token, products, and webhook.
In Polar, create three products with a monthly recurring price in US dollars: Starter, Growth, and Scale, with the prices in
PLANSinpackages/core/src/plans.ts. Copy each product's id.In the Polar organization's settings, create an organization access token with the scopes
checkouts:write,customer_sessions:write,subscriptions:read, andsubscriptions:write.In Polar, open Settings > Webhooks and add an endpoint:
- URL: your dashboard's address with
/api/billing/polar/webhook, for examplehttps://app.misomoon.com/api/billing/polar/webhook. - Format: Raw.
- API version:
2026-10. - Events:
subscription.created,subscription.active,subscription.updated,subscription.canceled,subscription.uncanceled,subscription.past_due, andsubscription.revoked.
Copy the endpoint's secret, which starts with
whsec_.- URL: your dashboard's address with
In
apps/api, set the two secrets. Each command asks for the value, and each one deploys a new version of the Worker at once:wrangler secret put POLAR_ACCESS_TOKEN wrangler secret put POLAR_WEBHOOK_SECRETIn
varsinapps/api/wrangler.cloud.jsonc, addPOLAR_PRODUCT_STARTER,POLAR_PRODUCT_GROWTH, andPOLAR_PRODUCT_SCALEwith the product ids, andPOLAR_SERVERset tosandboxwhile you test. KeepPLANSset toon. Deploy withpnpm --filter @misomoon/api run deploy:cloud.Sign in to the dashboard.
GET /api/v1/menow says"billing": true, and the Plan card shows the plans. Choose one and pay with a test card from Polar's sandbox docs. The plan shows within a minute.
Three Polar settings change what customers see:
- Settings > Subscriptions has the proration setting. It decides how a switch is billed, from the dashboard and from the portal alike. misomoon sends no proration of its own.
- The default tax behavior, in the payment settings, decides whether prices include tax or Polar adds it at checkout.
- The customer portal settings decide what customers can do there. A customer who switches plans in the portal changes the team's plan the same way.
Configuration lists each setting.
Test billing on your machine
The Polar CLI forwards the sandbox
organization's webhooks to wrangler dev, so you can pay for a plan without deploying and
without a webhook endpoint in Polar.
Do steps 1 and 2 above in the sandbox.
Install the CLI, and sign in to the sandbox:
npm install -g @polar-sh/cli polar auth login --sandboxStart forwarding, and leave it running. It prints a secret.
polar webhook listen http://localhost:8787/api/billing/polar/webhookIn
apps/api/.dev.vars, setPLANStoon,POLAR_ACCESS_TOKENto the sandbox token,POLAR_WEBHOOK_SECRETto the secret the CLI printed,POLAR_SERVERtosandbox, the threePOLAR_PRODUCT_*ids, andPUBLIC_URLtohttp://localhost:5173, so Polar sends you back to the dashboard rather than to the API's port. Start the API and the dashboard as CONTRIBUTING.md shows, and restart the API after any change to.dev.vars.Open the dashboard at
http://localhost:5173, choose a plan, and pay with a test card.
The CLI logs each event with misomoon's answer: 200 when it set the team's plan, and
202 for an event that changes nothing, such as order.paid.
When something fails
- Without
PLANS=on, the billing routes answer404and the dashboard shows no billing. - With
PLANS=onand a Polar setting missing, the routes answer501and name the missing settings, the webhook included, and the dashboard shows no billing. - A webhook with a wrong signature, or a
webhook-timestampmore than 5 minutes old, answers401and changes nothing. CheckPOLAR_WEBHOOK_SECRET. Polar retries a failed delivery up to 10 times, and after 10 failures in a row it turns the endpoint off and emails the organization. Turn it back on in Settings > Webhooks once it works. - A subscription to a product that is not one of the three answers
202, changes nothing, and logsunknown Polar product. A subscription without ateam_id, or of a deleted team, logspolar subscription without a teamorpolar subscription of a deleted team. - When Polar refuses a request, the route answers
502with Polar's message, and the log haspolar errorwith Polar's status. A wrong token or a token for the otherPOLAR_SERVERshows up here. - Polar refuses an email whose domain doesn't exist. misomoon then opens the checkout without the email, and Polar asks for one there.
- Choosing a plan while the team's last checkout is still taking the payment answers
409with the codepayment_pending. It clears once the subscription that checkout made reaches misomoon, or an hour after that checkout started. polar subscription missingin the log: the daily check found a team whose paid period ended, and Polar answered404for its subscription, or it is for another product. Check the token andPOLAR_SERVER, then the subscription in Polar. The team keeps its plan until support sets it.polar subscription gonemeans a checkout replaced a subscription Polar no longer had.polar reconcile failedin the log: the daily check couldn't read a team's subscription. It tries again the next day.
What misomoon stores
For each team that pays, misomoon stores Polar's customer id and subscription id, the
subscription's status and product, the end of the paid period, and the id of the last
checkout opened for the team, in the control database (teams and
polar_subscriptions). It also keeps the webhook-id of each
webhook for 30 days. Polar keeps the card, the billing address, the tax details, and the
invoices.