Skip to content

fix(sync): preserve shape-matched Stripe prices - #2340

Open
charlietlamb wants to merge 1 commit into
devfrom
charlie/mobbin-sync-coverage
Open

fix(sync): preserve shape-matched Stripe prices#2340
charlietlamb wants to merge 1 commit into
devfrom
charlie/mobbin-sync-coverage

Conversation

@charlietlamb

@charlietlamb charlietlamb commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • preserve shape-matched Stripe base prices as custom prices with their observed Stripe price IDs
  • keep exact stored Stripe price identities selecting catalog prices and versions
  • cover Mobbin Team quarterly/yearly seat sync, 600 credits per assigned seat, quantity updates, repeated sync idempotency, and Enterprise seat isolation

Tests

  • bunx tsgo --build --noEmit
  • bun test tests/unit/billing/sync/stripe-sync-currency.test.ts
  • ./run.sh tests/integration/billing/sync/sync-multi-currency.test.ts
  • ./run.sh tests/integration/billing/sync/sync-plan-version.test.ts
  • ./run.sh tests/integration/billing/stripe-webhooks/subscription-created/licenses/sub-created-mobbin-license-backsync.test.ts

Summary by cubic

Preserves shape-matched Stripe prices during sync by keeping them as custom prices with the observed Stripe price IDs, and only uses the catalog base price when an exact stored Stripe price identity exists. This fixes plan selection and seat syncing for Mobbin Team quarterly/yearly and keeps Enterprise seats isolated.

  • Bug Fixes
    • Shape-only base price matches are now stored as custom prices with the observed Stripe price ID.
    • Catalog base price is selected only when a stored Stripe price identity is present.
    • Correctly selects Team Quarterly vs Yearly, updates seat quantities, ensures idempotent re-syncs, and prevents Enterprise Seat from resolving to Team.

Written for commit 7e5c4d0. Summary will update on new commits.

Review in cubic

Greptile Summary

This PR preserves shape-matched Stripe prices during subscription back-sync. The main changes are:

  • Bug fixes: Classify shape-only base-price matches as custom prices.
  • Improvements: Add quarterly and yearly shared-license back-sync tests.
  • Improvements: Cover seat updates, repeated syncs, and plan isolation.

Confidence Score: 5/5

The change is mostly safe, but mapped-product shape matches may need to retain their catalog identity.

  • Exact Stripe price identity matches remain unchanged.
  • Shape matches from the plan's own mapped Stripe product now become custom prices.
  • The added tests cover the intended shared-license workflows.

server/src/internal/billing/v2/providers/stripe/utils/sync/matchUtils/classifyItemMatch.ts

Important Files Changed

Filename Overview
server/src/internal/billing/v2/providers/stripe/utils/sync/matchUtils/classifyItemMatch.ts Changes base-price classification so every shape match is treated as a custom base, including matches from the plan's mapped Stripe product.
server/tests/integration/billing/stripe-webhooks/subscription-created/licenses/sub-created-mobbin-license-backsync.test.ts Adds integration tests for quarterly, yearly, and cross-plan license back-sync behavior.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
server/src/internal/billing/v2/providers/stripe/utils/sync/matchUtils/classifyItemMatch.ts:53
**Mapped Catalog Price Becomes Custom**

When an unknown Stripe price belongs to the plan's own mapped Stripe product and matches its catalog base shape, this condition now routes it to `isCustomBaseMatch`. That changes the plan base from a catalog match with an `autumn_price_id` to a generated customization, so back-sync can persist or provision a custom price even though the owning catalog price was identified.

Reviews (1): Last reviewed commit: "fix(sync): preserve shape-matched Stripe..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Context used:

  • Context used - When generating the key changes section of the sum... (source)

/** Only a stored Stripe price identity selects the catalog base price. */
export const isBasePriceMatch = (match: ItemMatch): match is AutumnPriceMatch =>
isAutumnPriceMatch(match) &&
matchesOnBasePrice(match) &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Mapped Catalog Price Becomes Custom

When an unknown Stripe price belongs to the plan's own mapped Stripe product and matches its catalog base shape, this condition now routes it to isCustomBaseMatch. That changes the plan base from a catalog match with an autumn_price_id to a generated customization, so back-sync can persist or provision a custom price even though the owning catalog price was identified.

Prompt To Fix With AI
This is a comment left during a code review.
Path: server/src/internal/billing/v2/providers/stripe/utils/sync/matchUtils/classifyItemMatch.ts
Line: 53

Comment:
**Mapped Catalog Price Becomes Custom**

When an unknown Stripe price belongs to the plan's own mapped Stripe product and matches its catalog base shape, this condition now routes it to `isCustomBaseMatch`. That changes the plan base from a catalog match with an `autumn_price_id` to a generated customization, so back-sync can persist or provision a custom price even though the owning catalog price was identified.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 2 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Re-trigger cubic

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.

1 participant