Skip to content

Conversation

@kimmyxpow
Copy link

@kimmyxpow kimmyxpow commented Dec 31, 2025

Fixes #33

Adds initial support for usage-based billing and product benefits from the Polar API.


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Summary by CodeRabbit

  • New Features

    • Added richer pricing options: seat-based tiers, metered/unit pricing with caps, custom price ranges, unit amounts, and explicit price source.
    • Introduced a product benefits system to define and manage benefits with metadata and selectable/deletable properties.
  • Refactor

    • Pricing output made more consistent and explicit (unified base price plus per-model details) for clearer display and export.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 31, 2025

📝 Walkthrough

Walkthrough

Added usage-based pricing support (seat-based and metered unit) and product benefits. Extended product price schema with fields: source, seatTiers, unitAmount, capAmount, meterId, and meter. Refactored price conversion to build a shared basePrice and apply per-amountType augmentations; recurring metadata now derived from product.

Changes

Cohort / File(s) Summary
Schema Extensions
src/component/schema.ts
Added optional fields to products.prices: source (enum `"catalog"
Price Transformation Logic
src/component/util.ts
Rewrote convertToDatabaseProduct prices mapping to create a basePrice (common fields including recurringInterval derived from product and source) and then augment per price.amountType: fixed, custom, free, seat_based (maps seatTiers to minSeats/maxSeats/pricePerSeat), and metered_unit (adds unitAmount, capAmount, meterId, meter). Recurring type is derived from product.isRecurring; price-level recurring logic removed. Benefits mapping preserved and placed after prices mapping.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Hopping through fields both new and bright,
seat tiers stack and meters hum at night—
benefits bloom in tidy rows,
basePrice steadies where logic flows.
Cheers from a rabbit for schema delight!

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Support new Polar price types and product benefits' clearly and concisely summarizes the main changes: adding support for new price types (seat-based, metered) and introducing product benefits.
Linked Issues check ✅ Passed The PR fully addresses issue #33's requirements: adds support for new Polar price types (seat_based, metered_unit) beyond legacy types (fixed, custom, free), and implements product benefits persistence as requested.
Out of Scope Changes check ✅ Passed All changes in schema.ts and util.ts are directly scoped to the linked issue requirements: adding new price type fields and benefits support. No unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e08cfb4 and 7e045d9.

⛔ Files ignored due to path filters (1)
  • src/component/_generated/component.ts is excluded by !**/_generated/**
📒 Files selected for processing (2)
  • src/component/schema.ts
  • src/component/util.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: kimmyxpow
Repo: get-convex/polar PR: 34
File: src/component/schema.ts:47-64
Timestamp: 2025-12-31T13:36:41.603Z
Learning: In the Polar API, the `unitAmount` field for metered pricing (ProductPriceMeteredUnit) is returned as a string type, not a number. The schema definition should use `v.optional(v.string())` for this field.
📚 Learning: 2025-12-31T13:36:41.603Z
Learnt from: kimmyxpow
Repo: get-convex/polar PR: 34
File: src/component/schema.ts:47-64
Timestamp: 2025-12-31T13:36:41.603Z
Learning: In the Polar API, the `unitAmount` field for metered pricing (ProductPriceMeteredUnit) is returned as a string type, not a number. The schema definition should use `v.optional(v.string())` for this field.

Applied to files:

  • src/component/util.ts
  • src/component/schema.ts
🔇 Additional comments (5)
src/component/schema.ts (2)

44-67: LGTM! New price type fields are well-structured.

The schema extensions properly support the new Polar price types:

  • source distinguishes catalog vs ad-hoc prices
  • seatTiers correctly models tiered seat-based pricing with nullable maxSeats for unlimited tiers
  • Metered pricing fields (unitAmount, capAmount, meterId, meter) are appropriately typed
  • unitAmount as v.optional(v.string()) is correct per the Polar API (as confirmed in learnings)

These definitions align well with the conversion logic in util.ts.


70-83: Benefits schema captures comprehensive product benefit metadata.

The benefits array schema properly models all required fields from the Polar API, including the flexible properties field typed as v.optional(v.any()) for extensibility. The structure supports product benefit persistence as specified in the PR objectives.

src/component/util.ts (3)

64-75: Excellent refactoring to basePrice pattern.

The basePrice approach effectively consolidates common fields across all price types. Deriving recurringInterval and type from the product level (lines 72-73) rather than per-price is a deliberate design choice mentioned in the PR objectives ("recurring metadata sourced from product"), which simplifies the data model and ensures consistency across a product's prices.


77-120: Type-specific price handlers comprehensively cover all Polar price types.

The branch-based augmentation pattern cleanly extends basePrice for each price type:

  • fixed & custom: Standard amount fields
  • free: Correctly returns basePrice only (addresses previous review comment)
  • seat_based: seatTiers?.tiers.map(...) structure is correct per Polar SDK (confirmed in previous review)
  • metered_unit: Properly maps usage-based fields (unitAmount, capAmount, meterId, meter)

The fallback (line 122) safely handles unexpected types by returning basePrice.


124-135: Benefits mapping correctly persists product benefit data.

The transformation properly maps all benefit fields including the new properties field, fulfilling the PR objective to persist product benefits returned by the Polar API.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1da2d45 and e08cfb4.

⛔ Files ignored due to path filters (1)
  • src/component/_generated/component.ts is excluded by !**/_generated/**
📒 Files selected for processing (2)
  • src/component/schema.ts
  • src/component/util.ts
🔇 Additional comments (5)
src/component/util.ts (4)

64-74: LGTM: basePrice construction is clean and consistent.

The refactored approach with a shared basePrice object improves code maintainability. Deriving recurringInterval and type from the product level ensures consistency across all price entries.


76-92: LGTM: fixed and custom price type handlers are correct.

Both branches properly extend basePrice with their respective type-specific fields, maintaining consistency with the established pattern.


106-115: LGTM: metered_unit price type handler is comprehensive.

All relevant metered unit fields are properly mapped, enabling full support for usage-based billing.


119-130: LGTM: Benefits mapping is comprehensive.

All benefit fields are properly mapped with appropriate type conversions (dates to ISO strings, nullable fields handled correctly).

src/component/schema.ts (1)

67-80: LGTM: Benefits schema is comprehensive.

The benefits schema correctly captures all benefit fields with appropriate types. The use of v.any() for properties (line 78) and v.record(v.string(), v.any()) for metadata (line 77) is acceptable for flexible metadata fields, though it reduces type safety. This is a common pattern for handling dynamic API responses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing support for usage-based pricing and benefits

1 participant