fix: prefer recurring prepaid over one-off for feature_quantities - #3243
fix: prefer recurring prepaid over one-off for feature_quantities#3243SirTenzin wants to merge 2 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6f80e8b7e1
ℹ️ 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".
There was a problem hiding this comment.
All reported issues were addressed across 13 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…t cusEnt into quantity line items
Problem
cusProduct.options(FeatureOptions) is keyed by feature only. When a plan hosts both a recurring prepaid and a one-off prepaid price for the same feature, every consumer that resolves options or prices by feature (entToOptions,cusProductToFeatureOptions,findCusPriceByFeature) picks an arbitrary entry:feature_quantitiesapplied the quantity to both prices → double charge + double balance grant.Fix: tie-break, recurring wins
Stop-gap while the options model stays feature-keyed: when
feature_quantitiestargets a feature with multiple prepaid prices, the recurring price wins (shortest interval among recurring); the one-off contributes only its included usage. A feature with only a one-off prepaid is unaffected.New shared resolver
findPrepaidQuantityTargetPrice/isLosingPrepaidQuantityPrice(shared/utils/productUtils/priceUtils/findPrice/), applied at:setupFeatureQuantitiesContext— only the winning price consumes the params entry (attach, createSchedule, multiAttach and sync inherit this)initCustomerEntitlementBalance— losing prepaid ent grants allowance onlycustomerEntitlementToOptions— Stripe item specs for a losing price read no quantitycusEntToPrepaidQuantity— invoice/line-item quantity is 0 for a losing pricesetupUpdateSubscriptionIntent— ManualTopUp only when the tie-break target is the one-off (replacescustomerProductHasOneOffPrepaidForFeature, now removed)computeUpdateQuantityPlan/computeUpdateQuantityDetails— quantity updates resolve to the winning recurring pricehandleOneOffErrors— complex-update guard only fires when the one-off is the targetTests
manual-top-up/prepaid-tie-break.test.ts(integration, written red-first): attach on a mixed plan charges base + recurring only; update is absolute UpdateQuantity, not ManualTopUp delta; a one-off-only feature on the same plan still manual-tops-up.unit/products/findPrepaidQuantityTargetPrice.test.ts: recurring-beats-one-off, shortest interval (incl. interval_count), lone one-off wins its own feature, cross-feature isolation.Sweep of
update-subscription/{manual-top-up,update-quantity,one-off-prepaid-preserve}, feature-quantity error tests, and the unit suite: all failures observed also reproduce with the change stashed (pre-existing on this environment), none introduced by this PR.Summary by cubic
Fixes
feature_quantitieshandling when a plan has both a recurring prepaid and a one-off prepaid price for the same feature. Previously, quantity was applied to both prices (double charge) or routed to the wrong update intent; now the recurring prepaid wins, and one-off-only features still work as before.Changes
findPrepaidQuantityTargetPriceandisLosingPrepaidQuantityPriceto resolve the winning price for a feature-keyed quantity.customerProductHasOneOffPrepaidForFeaturein favor of the new resolver.Written for commit 34c4aaf. Summary will update on new commits.
Greptile Summary
This PR consistently resolves feature-level quantities to one prepaid price, preferring recurring prices over one-off prices and the shortest recurring interval when necessary.
Confidence Score: 5/5
The PR appears safe to merge.
No blocking failure remains.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD Request[feature_quantities request] --> Resolver[Resolve prepaid target for feature] Resolver --> HasRecurring{Recurring prepaid exists?} HasRecurring -->|Yes| Shortest[Select shortest recurring interval] HasRecurring -->|No| OneOff[Select one-off prepaid] Shortest --> QuantityUpdate[Apply absolute quantity update] OneOff --> ManualTopUp[Apply manual top-up] Resolver --> Losing[Other prepaid prices] Losing --> IncludedOnly[Preserve included allowance without consuming quantity]Reviews (2): Last reviewed commit: "fix: address review — zeroed options for..." | Re-trigger Greptile