E6 — Monetization (ALO-125)#111
Conversation
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (9)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR. |
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6bfd41e934
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // The Polar product is created on-demand at first checkout; here we | ||
| // record a local placeholder so the channel page can list tiers | ||
| // immediately. The webhook reconciles the polar_product_id when the | ||
| // first purchase completes. | ||
| const id = crypto.randomUUID(); |
There was a problem hiding this comment.
Create Polar catalog products before membership checkout
/api/polar/memberships stores a random UUID as polar_products.id and never creates a corresponding product in Polar, but /api/polar/checkout/membership later sends that ID to createCheckout as product_id. Polar checkout sessions require an existing Polar product ID, so newly created memberships will fail at checkout time (typically 4xx from Polar) and buyers cannot complete purchases.
Useful? React with 👍 / 👎.
| const tipId = crypto.randomUUID(); | ||
| await c.env.DB.prepare( | ||
| `INSERT INTO polar_products (id, user_id, kind, name, price_cents, currency) | ||
| VALUES (?, ?, 'tip', ?, ?, 'USD')`, |
There was a problem hiding this comment.
Stop using local UUIDs as Polar product IDs for tips
The tip checkout flow inserts a local tipId into polar_products and immediately passes that UUID to Polar createCheckout as productId, but no Polar product is created first. In this path, every tip checkout depends on a non-existent remote product, so tip payments will fail before a customer can pay.
Useful? React with 👍 / 👎.
| const billingTypes = new Set([ | ||
| 'order.created', | ||
| 'order.paid', | ||
| 'subscription.created', |
There was a problem hiding this comment.
Record only paid orders in the earnings ledger
Including order.created as a billable event writes ledger rows before payment is finalized; Polar documents that order.created can be pending. Because inserts are idempotent on polar_order_id, the later order.paid event for the same order is dropped, leaving unpaid/pending orders permanently counted in creator pending/lifetime payout totals.
Useful? React with 👍 / 👎.
…ts (ALO-125) Adds the E6 monetization spine: - D1 migration 0019 introduces creator_polar_accounts, membership_tiers, memberships, polar_events, monetization_ledger, and payouts. The ledger is append-only and keyed on polar_event_id so retried webhook deliveries no-op safely. - src/workers/polar.ts is a thin Polar REST client plus Standard-Webhooks signature verification (handles whsec_<base64> and plain secrets, and multi-sig rotation). Spooool's revenue share is configurable in basis points via POLAR_PLATFORM_FEE_BPS (default 1000 = 10%). - src/workers/polar-webhook.ts receives signed events at /api/webhooks/polar, persists every payload to polar_events for audit, and dispatches into the canonical ledger plus derived membership and payout state. Failures are recorded as process_error rather than retried indefinitely. - src/workers/monetization.ts exposes the creator + viewer surface: partner onboarding, membership tier CRUD, public tier listing, Polar Checkout for memberships and tips, viewer membership list, and the creator payouts dashboard with totals + ledger + payout history. - All routes are wired into the main worker; CSRF is bypassed for /api/webhooks/* as before. Doppler secrets are documented in wrangler.toml. Tests cover signature verification, fee math, ledger idempotency, membership upsert, payout recording, account state transitions, and the auth/lifecycle gates on every monetization route. 533 vitest tests pass; type-check, oxlint, and the AI-Gateway provider guard are all green. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
6bfd41e to
3f44919
Compare
|
ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR. |
Closes ALO-125 — https://linear.app/aloey/issue/ALO-125/e6-monetization
Summary
monetization_ledger) is append-only and idempotent onpolar_event_id, so retried Polar deliveries never double-credit.POLAR_PLATFORM_FEE_BPS, default 1000 = 10%) and recorded per ledger row, so future rate changes don't rewrite history.Files
src/db/migrations/0019_monetization.sql—creator_polar_accounts,membership_tiers,memberships,polar_events(raw audit),monetization_ledger,payouts.src/workers/polar.ts— thin Polar REST client + Standard-Webhooks signature verification (handleswhsec_<base64>and plain secrets, multi-sig rotation tolerance).src/workers/polar-webhook.ts—/api/webhooks/polarreceiver. Persists every payload topolar_events, dispatches into ledger / membership / payout / account state. Failures recorded asprocess_errorrather than retried indefinitely.src/workers/monetization.ts— creator + viewer surface (see Routes below).src/workers/index.ts,wrangler.toml— wiring + Doppler secret docs.Routes
POST /api/monetization/onboarding/start— creator → Polar partner linkGET /api/monetization/onboarding/status— onboarding statePOST /api/monetization/tiers·DELETE /api/monetization/tiers/:tierId— creator tier CRUDGET /api/monetization/channels/:username/tiers— public tier listPOST /api/monetization/memberships/checkout— viewer → Polar Checkout (recurring)POST /api/monetization/tips/checkout— viewer → Polar Checkout (one-time)GET /api/monetization/me/memberships— viewer dashboardGET /api/monetization/me/payouts— creator dashboard (totals + ledger + payouts)POST /api/webhooks/polar— signed webhook (CSRF-exempt via existing/api/webhooks/*rule)Secrets to set (Doppler /
wrangler secret put)POLAR_ACCESS_TOKEN— polar.sh API tokenPOLAR_ORGANIZATION_ID— Polar org id used as parent for products + partnersPOLAR_WEBHOOK_SECRET— Standard Webhooks signing secret (whsec_<base64>or plain)POLAR_PLATFORM_FEE_BPS— optional, defaults to 1000 (10%)PUBLIC_ORIGIN— public origin for Polar return / cancel URLsTest plan
npm test— 533 tests, 47 files passing; 42 new tests cover signature verification, fee math, ledger idempotency, membership upsert, payout recording, account state transitions, and the auth/lifecycle gates on every monetization routenpm run lintclean (oxlint + AI Gateway provider guard)npm run type-checkcleannpm run buildcleannpm run db:migrate:staginghttps://<env>/api/webhooks/polarand copy the signing secret intoPOLAR_WEBHOOK_SECRETOut of scope (deferred per ticket)
Ads, pay-per-view rentals.
🤖 Generated with Claude Code