Creem Setup
Configure Creem products, Edge secrets, and the webhook.
Creem runs outside the desktop app. The Tauri starter ships only public product IDs in the webview plus Supabase Edge Functions that keep the API key server side, verify webhooks, and write entitlement rows.
Complete Supabase Setup first, including the billing migration, then enable the app-side flag described on Billing.
Create products
Create the products you intend to sell in the Creem dashboard:
| Plan in the starter | Creem product type | App env |
|---|---|---|
| Pro monthly | Subscription | VITE_CREEM_PRODUCT_PRO_MONTHLY=prod_... |
| Pro yearly | Subscription | VITE_CREEM_PRODUCT_PRO_YEARLY=prod_... |
| Lifetime | One-time | VITE_CREEM_PRODUCT_LIFETIME=prod_... |
Put the product IDs in the desktop app’s .env. They are public identifiers
that ship in the renderer; never put CREEM_API_KEY there.
CREEM_SERVER_IDX chooses the Creem API environment for Edge Functions:
| Value | API base |
|---|---|
0 | Production: https://api.creem.io |
1 | Test: https://test-api.creem.io |
Use test mode while validating checkout and the webhook. Product IDs, API key, and webhook configuration must all belong to the same Creem environment.
Set Edge secrets
Set secrets on your Supabase project, not in the app’s .env:
supabase secrets set \
CREEM_API_KEY=creem_test_xxx \
CREEM_WEBHOOK_SECRET=whsec_xxx \
CREEM_SERVER_IDX=1 \
--project-ref <ref>You can instead copy supabase/functions/.env.example to the gitignored
supabase/functions/.env, fill it in, and upload it:
supabase secrets set --env-file supabase/functions/.env --project-ref <ref>| Secret | Used by | Notes |
|---|---|---|
CREEM_API_KEY | create-checkout, customer-portal, activate-license | Server-side Creem API key. |
CREEM_WEBHOOK_SECRET | creem-webhook | Must match the signing secret configured in Creem. |
CREEM_SERVER_IDX | All Creem functions | 0 production; 1 test. |
BILLING_SUCCESS_URL | create-checkout | Optional hosted checkout-return page. |
Supabase injects SUPABASE_URL, SUPABASE_ANON_KEY, and
SUPABASE_SERVICE_ROLE_KEY into Edge Functions. Do not add them to the app
configuration or secret file yourself.
Deploy the functions
Apply the billing migration before deploying:
supabase db push --project-ref <ref>Then deploy every function:
pnpm supabase:deploy
pnpm supabase:deploy <project-ref>The helper deploys every non-underscore folder in supabase/functions/.
_shared/ is bundled into its consumers and is never deployed by itself.
creem-webhook is the single public function; the helper gives it
--no-verify-jwt, while the other three remain JWT-verified through
supabase/config.toml.
| Function | JWT | Role |
|---|---|---|
create-checkout | On | Creates a checkout for the signed-in user and records a pending payment. |
customer-portal | On | Returns a portal URL for the signed-in user's stored Creem customer ID. |
activate-license | On | Optional license-key activation and deactivation. |
creem-webhook | Off | Receives Creem deliveries and writes the read model after signature verification. |
Verify the deployment:
supabase functions list --project-ref <ref>Configure the webhook
In Creem, register this deployed endpoint and set its signing secret to the
same value as CREEM_WEBHOOK_SECRET:
https://<ref>.supabase.co/functions/v1/creem-webhookThe current handler intentionally has a narrow event set:
| Event | Behavior |
|---|---|
checkout.completed | Marks the payment matching object.request_id / order_no as success, then records Creem order and customer IDs. |
subscription.paid | Upserts the user’s subscription from object.metadata.userId, product, customer, and subscription-period data. |
Other event types are ignored today. Extend supabase/functions/_shared/creem.ts
if your product needs cancellation, refund, pause, or past-due state reflected
in the app; keep the client-facing subscriptions read-model as the boundary.
The webhook reads the raw body before parsing JSON, recomputes its HMAC-SHA256
against CREEM_WEBHOOK_SECRET, and compares it with the creem-signature
header. The unique payments.order_no update and subscriptions.user_id
upsert make replayed deliveries converge; handler failures return 500 so
Creem can retry.
Optional checkout return page
Desktop checkout runs in the system browser, so a hosted bounce page is a useful
return path. The starter includes supabase/pay-success.sample.html; it already
uses the default soar-tauri://billing scheme and forwards the order ID.
Host the file on any static host, change soar-tauri to your rebranded protocol
when applicable, then set the hosted URL as an Edge secret:
supabase secrets set \
BILLING_SUCCESS_URL=https://your-domain.com/pay-success \
--project-ref <ref>create-checkout appends status=success&orderId=<orderNo>. Without this
secret, Creem uses the redirect configured on the product instead. In either
case, the app refreshes its server-backed entitlement on the billing route and
on window focus.
Test the path
Serve functions locally with a local Edge env file:
supabase functions serve --env-file supabase/functions/.envThey listen at:
http://localhost:54321/functions/v1/<name>Run supabase start too when you need local Postgres and Auth. A deployed test
project is generally simpler for an end-to-end sandbox checkout because Creem
must reach the webhook URL.
After a sandbox payment, inspect the writer-side evidence:
select order_no, user_id, product_id, status, customer_id, updated_at
from public.payments
order by created_at desc
limit 10;
select user_id, product_id, product_type, status, period_end, customer_id, subscription_id
from public.subscriptions
order by updated_at desc
limit 10;Production checklist
-
VITE_BILLING_ENABLED=trueis set for the desktop build. -
VITE_CREEM_PRODUCT_*matches the selected Creem environment. - The billing migration is applied.
- Creem secrets are stored only in Supabase Edge secrets.
-
pnpm supabase:deploydeployed all four functions. - Creem points to
https://<ref>.supabase.co/functions/v1/creem-webhook. - A hosted return page uses your current deep-link scheme, if you use one.
- A sandbox payment writes the expected
paymentsandsubscriptionsrows. - You implemented further webhook events if your business requires them.
