Skip to main content
This runbook covers connecting an Inventory storefront to Polar for real checkout, pointing Polar’s webhook at Tuturuuu, and how a paid sale flows into the workspace finance ledger.
Never paste Polar tokens, client secrets, or webhook secrets into source, docs, or chat. They are supplied only through environment variables and the per-workspace integration panel, which stores them encrypted.

How the flow works

  1. A shopper checks out on a checkoutMode: 'polar' storefront. Tuturuuu creates a Polar checkout and stores polar_checkout_id on the private.inventory_checkout_sessions row (status reserved).
  2. The shopper pays on Polar. Polar sends an order.updated webhook to Tuturuuu.
  3. syncInventoryPolarOrder marks the checkout completed and, when the order is paid, books the revenue into the workspace finance ledger (wallet_transactions) using the product’s finance category and the workspace default wallet. This is idempotent — a sale books at most one transaction.

Money and currency

Inventory commerce money (storefront listing prices, bundle prices, compare_at_price, checkout session/line amounts, settlement ledger entries, and costing figures) is stored in integer minor units of the row’s currency — cents for USD/EUR, but whole units for zero-decimal currencies like JPY/VND. This matches how Polar represents amounts, so the product/bundle/ checkout sync passes the stored value straight through without scaling. Conversion is centralized in @tuturuuu/utils/money:
  • majorToMinor(amount, currency) / minorToMajor(minor, currency) — convert at input/storage and read/display boundaries (currency-aware: USD ×100, JPY/VND ×1).
  • formatMoneyFromMinor(minor, currency) — the canonical display formatter.
  • The shared MoneyInput (@tuturuuu/ui/money-input) takes and emits minor units while editing in localized major units.
Two boundaries convert explicitly:
  • The finance ledger (wallet_transactions.amount) stores major units, so recordInventorySaleFinanceTransaction converts the checkout’s minor-unit total back to major units when booking revenue.
  • Promotions (workspace_promotions.value) remain in major units (shared with finance invoices); promotions-polar multiplies by 100 at the Polar boundary.
Migrating from the previous whole-dollar storage: the ..._inventory_commerce_minor_units migration multiplies existing inventory commerce amounts by each row’s currency factor. Shared tables (inventory_products, workspace_promotions, finance wallets) are left as-is.

Bundles and listings ↔ Polar products

Publishing is automatic: creating or updating a listing/bundle schedules a best-effort push to Polar (a product with a fixed price per currency). Standalone bundles with no storefront publish against the workspace-level Polar integration and are priced in USD. Two-way sync:
  • App → Polar: name, description, and price sync on every write. Archiving a listing/bundle (or deleting it) archives its Polar product so it is no longer buyable; re-publishing un-archives it.
  • Polar → App: product.created/updated webhooks apply name, description, and price back onto the mapped inventory row.

Environment variables

Set these on apps/pay (reference by name only). Inventory-only deployments that create Polar products also need the access token and sandbox/currency configuration in their owning runtime:
VariablePurpose
POLAR_SANDBOXtrue to use Polar’s sandbox; unset/false for production.
POLAR_ACCESS_TOKENPlatform-level Polar organization access token.
POLAR_WEBHOOK_SECRETSecret used to verify incoming Polar webhook signatures. Must match the value Polar shows when you create the webhook.
POLAR_CURRENCIESOptional allowlist of supported storefront currencies.
For sandbox testing, create the credentials at https://sandbox.polar.sh/dashboard/<org>/settings and the OAuth app / organization token under User Settings → Developer.

Connect a workspace to Polar

  1. Open Inventory → Overview → Polar settings in the workspace.
  2. Choose the environment (sandbox or production) and paste the Polar organization access token. It is encrypted per workspace before storage; only the last 4 characters are ever shown again.
  3. Save. Tuturuuu validates the token and provisions a private inventory_checkout product in your Polar org.

Configure the Polar webhook

In the Polar dashboard for your org → Settings → Webhooks → Add Endpoint:
  1. URL — the canonical Pay webhook endpoint:
    https://pay.tuturuuu.com/api/payment/webhooks
    
    For local development, expose localhost with a tunnel (e.g. cloudflared or ngrok) and use the tunnel’s HTTPS URL with the same path.
  2. FormatRaw.
  3. Events — subscribe at minimum to:
    • checkout.created
    • checkout.updated
    • order.created
    • order.updated
  4. Copy the webhook secret Polar generates and set it as POLAR_WEBHOOK_SECRET on apps/pay. Signature verification fails closed if it does not match.
Sandbox changes never touch your live Polar account and never move real money — the dashboard shows a “Payments are not processed” banner.

Verify a paid sale books finance

  1. Place a sandbox checkout on a polar storefront and complete payment.
  2. Confirm Polar delivered the order.updated event (Webhooks → Deliveries).
  3. The checkout session flips to completed with polar_status = paid.
  4. A wallet_transactions row appears in the workspace finance ledger for the sale total, linked back via inventory_checkout_sessions.finance_transaction_id.
If no transaction appears, check that the workspace has a default wallet set (finance config default_wallet_id) — booking is skipped without one.