CodeWorlds
Powrót do kolekcji
Przewodnik14 min czytaniaZespół CodeWorlds

Polar, merchant of record dla twórców oprogramowania

Polar rozlicza VAT i sales tax za Ciebie. Plan darmowy to 5 procent plus 50 centów, serwer jest na Apache 2.0, SDK na MIT w wersji 0.49.0.

Polar, merchant of record dla twórców oprogramowania

Polar sprzedaje Twoje produkty cyfrowe we własnym imieniu i to on, a nie Ty, odpowiada przed urzędami skarbowymi za VAT oraz sales tax. Plan darmowy kosztuje 5 procent plus 50 centów od transakcji, trzy plany płatne schodzą do 3,4 procent plus 30 centów przy 400 dolarach miesięcznie. Serwer jest na licencji Apache 2.0, a SDK dla TypeScriptu na MIT, w wersji 0.49.0 z 20 lipca 2026 roku.

Czym Polar jest, czym nie jest i dlaczego mylisz go z polars

Zacznijmy od nieporozumienia, które kosztuje ludzi kwadrans. Wpisanie słowa "polar" w wyszukiwarkę zwraca dwa zupełnie różne projekty. Ten opisywany tutaj to platforma płatności spod adresu polar.sh. Drugi to polars, biblioteka ramek danych napisana w Rust i używana głównie z Pythona, w rejestrze PyPI w wersji 1.43.2 na licencji MIT. Mają wspólne pierwsze pięć liter i nic poza tym.

Polar jest merchant of record, czyli sprzedawcą formalnym. To rozróżnienie decyduje o wszystkim innym. Zwykły procesor płatności, na przykład Stripe w układzie standardowym, przenosi pieniądze i tyle. Umowa sprzedaży wiąże Twoją firmę z klientem końcowym, więc rejestracja do VAT OSS, progi nexus w poszczególnych stanach USA i comiesięczne deklaracje zostają Twoim obowiązkiem. Merchant of record wchodzi między Ciebie a klienta jako odsprzedawca. Faktura idzie z jego numerem podatkowym, on nalicza i odprowadza podatek, a Ty dostajesz wypłatę i jedno rozliczenie z jednym podmiotem.

Zakres produktu jest szerszy niż sam koszyk. Polar obsługuje subskrypcje z okresami próbnymi i proporcjonalnym rozliczeniem, rozliczanie według zużycia z własnymi miernikami zdarzeń, sprzedaż miejsc w zespole, przedpłacone kredyty oraz zniżki. Do tego dochodzi mechanizm świadczeń dołączanych do produktu. W kodzie SDK lista typów świadczeń jest zamknięta i wygląda tak: custom, discord, github_repository, downloadables, license_keys, meter_credit, feature_flag oraz slack_shared_channel. Praktycznie oznacza to, że po zakupie klient dostaje automatycznie zaproszenie do prywatnego repozytorium GitHub, rolę na Discordzie, klucz licencyjny albo plik do pobrania o rozmiarze do 10 GB, bez pisania żadnej logiki po Twojej stronie.

Czego Polar nie robi. Nie jest bramką płatniczą dla sprzedaży fizycznej i nie jest systemem fakturowania dla usług doradczych. Nie zastąpi też własnej bazy danych, bo identyfikatory zamówień i subskrypcji lepiej trzymać u siebie, choćby w Supabase, niż odpytywać dostawcę przy każdym żądaniu. Nie wysyła również własnych maili transakcyjnych poza tymi z potwierdzeniem zakupu, więc powiadomienia produktowe i tak zbudujesz na czymś w rodzaju Resend.

Licencja sprawdzona z trzech źródeł

Przy platformach tego typu licencja klienta i licencja serwera to dwie różne sprawy i mylenie ich prowadzi do złych decyzji. Sprawdziłem trzy niezależne miejsca.

Pierwsze źródło to plik LICENSE w repozytorium. W polarsource/polar, czyli w repozytorium serwera, leży pełny tekst Apache License 2.0. Interfejs GitHuba pokazuje przy tym repozytorium zakładkę oznaczoną Apache-2.0, repozytorium ma około 10,2 tysiąca gwiazdek i nie jest zarchiwizowane. SDK dla TypeScriptu mieszka w osobnym repozytorium polarsource/polar-js, gdzie plik LICENSE zawiera tekst MIT z notą "Copyright (c) 2026 Polar Software Inc.".

Drugie źródło to pole license w rejestrach pakietów. W npm @polar-sh/sdk w wersji 0.49.0 ma MIT. W PyPI polar-sdk w wersji 0.32.0 ma MIT oraz klasyfikator License :: OSI Approved :: MIT License.

Trzecie źródło to zawartość opublikowanej paczki, bo deklaracja w rejestrze bywa fikcją. Archiwum npm dla 0.49.0 waży 1,67 MB, po rozpakowaniu blisko 20 MB i zawiera 9693 pliki, w tym 2152 pliki .js, katalog src ze źródłami TypeScript oraz dist w dwóch wariantach, CommonJS i ESM. Plik LICENSE jest w środku. Koło ratunkowe w postaci sprawdzenia, czy w paczce w ogóle jest kod, wypada tu pozytywnie. Podobnie po stronie Pythona: polar_sdk-0.32.0-py3-none-any.whl waży 909 811 bajtów, zawiera 881 plików .py oraz plik polar_sdk-0.32.0.dist-info/licenses/LICENSE, a w METADATA widnieje License: MIT.

Rozjazdy jednak są i dotyczą pakietów pomocniczych. @polar-sh/nextjs w wersji 0.9.6 oraz @polar-sh/better-auth w wersji 1.8.4 nie mają w ogóle pola license w package.json, więc rejestr npm nie pokazuje dla nich żadnej licencji. Po rozpakowaniu archiwum @polar-sh/nextjs okazuje się, że plik LICENSE jednak w paczce jest i zawiera tekst Apache 2.0. Z kolei @polar-sh/checkout w wersji 0.4.1 i @polar-sh/adapter-utils w wersji 0.4.6 deklarują Apache-2.0 wprost. W efekcie w jednym drzewie zależności dostajesz MIT w rdzeniu, Apache 2.0 w adapterach i dwa pakiety bez deklaracji, które automatyczny audyt licencji oznaczy jako nieznane. Jeśli w firmie prowadzisz taką listę, wpisz ręcznie Apache 2.0 na podstawie pliku w paczce, a nie na podstawie metadanych.

Apache 2.0 na serwerze to dobra wiadomość, bo bywa tam SSPL albo AGPL, które wykluczają część zastosowań komercyjnych. Praktyczna wartość samodzielnego hostowania jest tu jednak niewielka, bo produktem nie jest kod, tylko status merchant of record. Uruchomienie repozytorium na własnym serwerze nie sprawi, że ktoś inny odprowadzi za Ciebie VAT.

SDK poniżej wersji 1.0 i co to znaczy w praktyce

Numer 0.49.0 nie jest przypadkiem ani skromnością. W wersjonowaniu semantycznym wszystko poniżej 1.0 zwalnia autora z obietnicy zgodności wstecznej: zmiana łamiąca kod może przyjść w wydaniu podrzędnym. I przychodziła. README paczki zawiera wprost ostrzeżenie, że począwszy od wersji wyższych niż 0.6.0 zmieniono generator SDK i nowa wersja nie jest zgodna wstecz z poprzednimi.

Historia wydań pokazuje tempo. Od 0.1.0 z 13 października 2023 roku do dziś ukazało się 161 wersji. Równolegle z linią 0.x biegnie linia zapowiadająca jedynkę: znacznik next w npm wskazuje na 1.0.0-alpha.17 z 19 sierpnia 2026 roku. To znaczy, że wersja 1.0 jest w przygotowaniu, ale w chwili pisania nadal jest alfą, a produkcyjnie instalujesz linię przed jedynką.

SDK jest generowane przez Speakeasy ze specyfikacji OpenAPI, co widać w nagłówkach plików źródłowych. Konsekwencja jest taka, że każda zmiana w API przekłada się na wygenerowany kod bez ludzkiego filtra. Zależności są skromne: zod w zakresie ^3.25.65 || ^4.0.0 oraz standardwebhooks w wersji ^1.0.0. Wersja pythonowa wymaga Pythona co najmniej 3.9.2 i ciągnie httpx, pydantic, jsonpath-python oraz standardwebhooks.

Wniosek operacyjny jest prosty. Przypnij dokładną wersję zamiast zakresu, trzymaj wywołania SDK za własną cienką warstwą adaptera i czytaj listę zmian przed każdą aktualizacją. To ta sama dyscyplina, którą stosujesz przy większych skokach wersji w Next.js, tylko tutaj ryzyko jest większe, bo numer główny jeszcze nie wystartował.

Cennik: plany, dopłaty i wypłaty

Cennik jest opublikowany w dwóch miejscach, w dokumentacji pod adresem docs.polar.sh oraz na stronie marketingowej. Sprawdziłem oba i tym razem liczby się zgadzają, co przy dostawcach płatności nie jest regułą.

PlanOpłata miesięcznaOd transakcjiWsparcie
Starter0 USD5 procent plus 50 centówstandardowe
Pro20 USD3,8 procent plus 40 centówpriorytetowe
Growth100 USD3,6 procent plus 35 centówpriorytetowe
Scale400 USD3,4 procent plus 30 centówpriorytetowe plus Slack i SSO

Do tego dochodzi stawka historyczna. Organizacje założone przed 27 maja 2026 roku pozostają na taryfie Early Member, czyli 4 procent plus 40 centów oraz dopłata 0,5 procent od płatności subskrypcyjnych, bez opłaty miesięcznej. Haczyk jest opisany wprost: przejście na plan płatny kasuje Early Member bezpowrotnie, a powrót w dół ląduje na Starterze z nową stawką 5 procent plus 50 centów. Nowe organizacje zakładane po tej dacie zaczynają od Startera, nawet jeśli zakłada je konto z dłuższym stażem.

Dopłaty są dwie i tylko dwie. Plus 1,5 procent za karty spoza Stanów Zjednoczonych oraz plus 0,5 procent za płatności subskrypcyjne wyłącznie na taryfie Early Member. Na planach Starter, Pro, Growth i Scale osobnej opłaty subskrypcyjnej nie ma i to jest realna różnica wobec konkurencji.

Wypłaty rozlicza Stripe i Polar deklaruje, że nie dokłada własnej marży. Stawki to 2 dolary za każdy miesiąc, w którym wystąpiła wypłata, plus 0,25 procent i 25 centów od pojedynczej wypłaty, plus przewalutowanie w wysokości 0,25 procent w Unii Europejskiej i do 1 procent w pozostałych krajach. Spór z bankiem klienta kosztuje 15 dolarów niezależnie od rozstrzygnięcia. Przy zwrocie pieniędzy klientowi pierwotna prowizja transakcyjna nie wraca do Ciebie.

Osobno sprawdziłem arytmetykę progów opłacalności, bo dostawca podaje je jako gotowe liczby: 1379 dolarów sprzedaży miesięcznie dla planu Pro, 5634 dla Growth i 19 048 dla Scale. Te wartości wychodzą tylko przy założeniu, że średnia transakcja to około 40 dolarów, czego strona nigdzie nie pisze. Dla planu Pro różnica stawki procentowej wynosi 1,2 punktu, różnica opłaty stałej 10 centów, więc równanie 0,012 razy sprzedaż plus 0,10 razy liczba transakcji równa się 20 daje 1379 dolarów dopiero przy 34,5 transakcji miesięcznie. Ten sam współczynnik 40 dolarów odtwarza pozostałe dwa progi co do dolara. Jeśli sprzedajesz produkt za 9 dolarów, Twój własny próg wypadnie zupełnie gdzie indziej i policz go samodzielnie.

Polar kontra Lemon Squeezy na konkretnej kwocie

To jest pytanie, które realnie stoi za wyborem. Lemon Squeezy bierze 5 procent plus 50 centów, należy do Stripe i sam zapowiedział budowę następcy oraz migrację użytkowników do Stripe Managed Payments. Polar bierze tyle samo na planie darmowym, ale różni się strukturą dopłat.

PozycjaPolar StarterLemon Squeezy
Opłata podstawowa5 procent plus 50 centów5 procent plus 50 centów
Karty międzynarodoweplus 1,5 procentplus 1,5 procent
Płatność subskrypcyjnabrak dopłatyplus 0,5 procent
Płatność przez PayPalbrak obsługiplus 1,5 procent
Niższe stawki za abonamenttak, od 20 USD miesięczniebrak takiej opcji
Wypłata poza USA2 USD za miesiąc plus 0,25 procent i 25 centów plus przewalutowanie1 procent od kwoty wypłaty
Spór z bankiem15 USD15 USD
Właścicielniezależna spółkaStripe

Policzmy subskrypcję za 29 dolarów sprzedaną klientowi w Polsce, z VAT 23 procent i kartą spoza Stanów Zjednoczonych. Wartość transakcji to 35,67 dolara. U Polara na Starterze prowizja wynosi 1,78 plus 0,50 plus 0,54, razem 2,82 dolara. W Lemon Squeezy dochodzi dopłata subskrypcyjna 0,18 dolara, więc wychodzi 3,00 dolara. Różnica na jednej transakcji to 18 centów.

W skali miesiąca robi się z tego więcej, bo dochodzą wypłaty. Przy dwudziestu takich subskrypcjach, czyli 580 dolarach przychodu bazowego, Polar zabiera 56,37 dolara prowizji plus około 4,87 dolara za jedną wypłatę na konto w Unii, razem 61,24 dolara, czyli 10,6 procent. Lemon Squeezy zabiera 59,93 dolara prowizji plus 5,20 dolara wypłaty, razem 65,13 dolara, czyli 11,2 procent. Zysk z przeniesienia to niecałe cztery dolary miesięcznie i sam w sobie nie uzasadnia migracji.

Sytuacja zmienia się przy większej skali. Przy dwustu subskrypcjach, czyli 5800 dolarach miesięcznie, plan Growth kosztuje 433,82 dolara prowizji plus 100 dolarów abonamentu plus około 29 dolarów wypłaty, razem 562,90 dolara, czyli 9,7 procent. Lemon Squeezy w tych samych warunkach kosztuje 651,35 dolara, czyli 11,2 procent. Różnica to 88 dolarów miesięcznie i ponad tysiąc rocznie. Dopiero tutaj rachunek ma sens.

Odwrotnie też trzeba to powiedzieć. Przy 580 dolarach miesięcznie plan Pro kosztowałby 70,73 dolara zamiast 61,24 na Starterze, bo abonament 20 dolarów nie zwraca się przy tej sprzedaży. Płatny plan poniżej progu to strata pieniędzy, a próg zależy od średniej wartości koszyka, nie od samego obrotu.

Integracja w praktyce

Instalacja i wybór środowiska. Polar udostępnia osobne środowisko testowe i przełącza się je jednym parametrem, bez zmiany adresów w kodzie.

Code
Bash
npm install @polar-sh/sdk
npm install @polar-sh/nextjs
export POLAR_ACCESS_TOKEN="polar_oat_..."
export POLAR_WEBHOOK_SECRET="whsec_..."

Utworzenie sesji płatności. Pole products jest wymagane i przyjmuje tablicę identyfikatorów, reszta jest opcjonalna. Wartości domyślne w schemacie to allowDiscountCodes ustawione na prawdę i requireBillingAddress ustawione na fałsz.

Code
TypeScript
import { Polar } from '@polar-sh/sdk'

const polar = new Polar({
  accessToken: process.env.POLAR_ACCESS_TOKEN,
  server: 'sandbox'
})

const checkout = await polar.checkouts.create({
  products: ['5b2a0f4c-3f11-4a1e-9c33-2f0a4d8b1f77'],
  successUrl: 'https://twojaapka.pl/dzieki?checkout_id={CHECKOUT_ID}',
  customerEmail: 'jan@example.com',
  externalCustomerId: 'user_8213',
  allowDiscountCodes: true,
  requireBillingAddress: false,
  metadata: { plan: 'pro', source: 'pricing-page' }
})

console.log(checkout.url, checkout.clientSecret, checkout.totalAmount)

Odbiór zdarzeń. Podpis weryfikuje standard Standard Webhooks, a funkcja validateEvent zwraca sparsowane zdarzenie albo rzuca WebhookVerificationError. Nazwy zdarzeń mają postać z kropką, między innymi order.paid, subscription.active, subscription.past_due, checkout.updated oraz customer.state_changed.

Code
TypeScript
import { validateEvent, WebhookVerificationError } from '@polar-sh/sdk/webhooks'

export async function POST(request: Request) {
  const body = await request.text()
  const headers = Object.fromEntries(request.headers)

  try {
    const event = validateEvent(body, headers, process.env.POLAR_WEBHOOK_SECRET!)

    if (event.type === 'order.paid') {
      await nadajDostep(event.data.customerId, event.data.productId)
    }

    return new Response('', { status: 202 })
  } catch (error) {
    if (error instanceof WebhookVerificationError) {
      return new Response('', { status: 403 })
    }
    throw error
  }
}

Klucze licencyjne dla aplikacji instalowanej lokalnie. Metoda validate przyjmuje obowiązkowe key i organizationId, a opcjonalnie activationId, benefitId, customerId, incrementUsage oraz conditions. W odpowiedzi dostajesz między innymi status, usage, limitUsage, limitActivations, validations, lastValidatedAt i expiresAt.

Code
TypeScript
const licencja = await polar.licenseKeys.validate({
  key: 'POLAR-1A2B-3C4D-5E6F',
  organizationId: 'e2ff1d55-84e2-4b47-9d9e-8b8a1c0f2a11',
  incrementUsage: 1,
  conditions: { major_version: 3 }
})

if (licencja.status !== 'granted') {
  throw new Error('Licencja nieaktywna')
}

console.log(licencja.usage, licencja.limitUsage, licencja.expiresAt)

Jeśli logowanie masz już na Better Auth, pakiet @polar-sh/better-auth w wersji 1.8.4 podłącza się jako wtyczka i wymaga jako zależności równoległych @polar-sh/sdk w zakresie ^0.47.0, better-auth w zakresie ^1.4.12 oraz zod. Adapter @polar-sh/nextjs w wersji 0.9.6 wymaga Next w wersji ^15.0.0 || ^16.0.0 i daje gotowe funkcje Checkout, CustomerPortal i Webhooks do wpięcia w trasy. Wdrożenie na Vercel nie wymaga niczego dodatkowego poza zmiennymi środowiskowymi.

Typowe błędy

Liczenie przychodu ze stawki z nagłówka. Przy sprzedaży subskrypcji do Europy realny koszt na planie darmowym wychodzi około 10,6 procent, a nie 5 procent, bo dochodzi dopłata za kartę międzynarodową i koszt wypłaty. Do prognozy przyjmij liczbę policzoną dla swojego przypadku.

Kupowanie planu płatnego za wcześnie. Abonament 20 dolarów zwraca się dopiero powyżej progu, który dla średniego koszyka 40 dolarów wypada przy 1379 dolarach sprzedaży miesięcznie. Poniżej tej granicy plan płatny jest droższy niż darmowy.

Traktowanie progów opłacalności jako uniwersalnych. Liczby podane przez dostawcę zakładają średnią transakcję około 40 dolarów. Przy tańszym produkcie opłata stała waży więcej i próg przesuwa się w górę.

Poleganie na polu license przy audycie zależności. Dwa pakiety adapterów nie deklarują licencji w metadanych, choć plik Apache 2.0 leży w paczce. Automat oznaczy je jako nieznane i ktoś będzie musiał to rozstrzygnąć ręcznie.

Instalowanie SDK z zakresem wersji. Przy numerze głównym równym zero zmiana łamiąca może przyjść w wydaniu podrzędnym, co już się zdarzyło przy zmianie generatora po wersji 0.6.0. Przypnij dokładną wersję.

Zakładanie, że Apache 2.0 na serwerze pozwala uciec od dostawcy. Kod możesz uruchomić u siebie, ale roli merchant of record nie skopiujesz, bo to umowa i rejestracje podatkowe, a nie repozytorium.

Pomijanie kosztu sporów. Piętnaście dolarów za spór z bankiem przy produkcie za 19 dolarów oznacza, że dwa spory na sto transakcji zjadają cały zysk z pięciu sprzedaży.

FAQ

Czy Polar jest tańszy od Lemon Squeezy?

Na planie darmowym nieznacznie, bo obie platformy mają 5 procent plus 50 centów i tę samą dopłatę 1,5 procent za karty międzynarodowe, a różnicę robi brak dopłaty subskrypcyjnej 0,5 procent i tańsze wypłaty. Przy 580 dolarach miesięcznie oszczędność to około 4 dolary. Przy 5800 dolarach i planie Growth różnica rośnie do około 88 dolarów miesięcznie.

Co oznacza wersja 0.49.0 dla stabilności integracji?

Numer główny równy zero zwalnia autora z obietnicy zgodności wstecznej, więc zmiana łamiąca może przyjść w zwykłym wydaniu podrzędnym. Tak było przy zmianie generatora po wersji 0.6.0. Linia 1.0.0-alpha.17 istnieje, ale w chwili pisania jest alfą pod znacznikiem next, a jako latest publikowana jest linia 0.x.

Na jakiej licencji jest serwer Polara?

Repozytorium polarsource/polar ma pełny tekst Apache License 2.0 w pliku LICENSE. To licencja permisywna z klauzulą patentową, więc bez ograniczeń typowych dla AGPL czy SSPL. SDK dla TypeScriptu jest na MIT, a część pakietów pomocniczych na Apache 2.0 albo bez deklaracji w metadanych npm.

Czy Polar obsługuje sprzedaż dostępu do prywatnego repozytorium?

Tak, w SDK typ świadczenia nazywa się github_repository i po zakupie klient dostaje automatycznie zaproszenie do wskazanego prywatnego repozytorium. Obok tego dostępne są klucze licencyjne, pliki do pobrania o rozmiarze do 10 GB, role na Discordzie, flagi funkcji i kredyty do miernika zużycia.

Jakie jest ryzyko związania się z Polarem na lata?

Spółka jest młodsza i mniejsza niż Stripe czy Paddle, repozytorium ma około 10,2 tysiąca gwiazdek, a społeczność jest odpowiednio mniejsza, więc gotowych odpowiedzi na nietypowy problem znajdziesz mniej. Do tego dochodzi SDK przed wersją 1.0. Trzymaj identyfikatory zamówień u siebie i schowaj wywołania dostawcy za własnym interfejsem, żeby wymiana warstwy płatności zajęła dzień, a nie kwartał.

Czytaj dalej

Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie