Conversation
…e options - Added new pricing calculations for Hobby and Startup tiers, including server quantity handling. - Implemented upgrade functionality for users on legacy plans to switch to Hobby or Startup plans. - Updated Stripe webhook to correctly calculate server quantities based on subscription items. - Refactored getStripeItems utility to accommodate new billing tiers and server quantity logic.
…ast notifications - Integrated a confirmation dialog for upgrading plans, providing users with clear details about their current and new plans. - Added toast notifications to inform users of successful upgrades or errors during the process. - Updated UI elements for better styling and user experience in the billing section.
…ervers - Modified the billing section to clarify that users can add more servers at the specified monthly rate. - Added monthly pricing display for both Hobby and Startup tiers when the annual plan is selected. - Enhanced user experience by providing clearer pricing information based on server quantity.
… Stripe router - Updated the product ID array construction to directly include Hobby and Startup product IDs. - Streamlined the refinement logic for server quantity validation, improving readability without altering functionality. - Ensured consistent validation messages for the Startup plan requirements.
| const updateItems: Stripe.SubscriptionUpdateParams["items"] = | ||
| currentItems.map((item, i) => { | ||
| if (i < newItems.length) { | ||
| return { | ||
| id: item.id, | ||
| price: newItems[i]!.price, | ||
| quantity: newItems[i]!.quantity, | ||
| }; | ||
| } | ||
| return { id: item.id, deleted: true }; | ||
| }); |
There was a problem hiding this comment.
Subscription update logic can fail with mismatched item counts
The mapping assumes currentItems and newItems are aligned by index, but Startup tier creates 2 line items (base + extra servers) while Hobby creates 1. When upgrading from Startup (2 items) to Hobby (1 item), the second item will be marked deleted: true, but if downgrading from Hobby (1 item) to Startup (2 items), the loop at lines 263-267 adds the missing item. This works, but consider what happens if current subscription has unexpected items (e.g., manual adjustments in Stripe dashboard) - the index-based mapping could create incorrect results.
| const updateItems: Stripe.SubscriptionUpdateParams["items"] = | |
| currentItems.map((item, i) => { | |
| if (i < newItems.length) { | |
| return { | |
| id: item.id, | |
| price: newItems[i]!.price, | |
| quantity: newItems[i]!.quantity, | |
| }; | |
| } | |
| return { id: item.id, deleted: true }; | |
| }); | |
| const updateItems: Stripe.SubscriptionUpdateParams["items"] = [ | |
| ...currentItems.map((item) => ({ id: item.id, deleted: true })), | |
| ...newItems.map((newItem) => ({ price: newItem.price, quantity: newItem.quantity })), | |
| ]; |
| const extraServerPrice = isAnnual | ||
| ? HOBBY_PRICE_ANNUAL_ID || BASE_ANNUAL_MONTHLY_ID | ||
| : HOBBY_PRICE_MONTHLY_ID || BASE_PRICE_MONTHLY_ID; | ||
| if (basePrice) items.push({ price: basePrice, quantity: 1 }); |
There was a problem hiding this comment.
Empty basePrice creates incomplete subscription
If STARTUP_BASE_PRICE_MONTHLY_ID or STARTUP_BASE_PRICE_ANNUAL_ID is empty/undefined, the code only pushes base price if (basePrice). This means a Startup subscription could be created with only the extra servers line item, missing the base that includes the first 3 servers. This would result in incorrect billing.
Add validation to ensure required price IDs exist before creating startup subscriptions.
| variant="outline" | ||
| size="icon" | ||
| className="h-8 w-8" | ||
| onClick={() => setUpgradeServerQty((q) => q + 1)} |
There was a problem hiding this comment.
Hardcoded 3-second delay is a code smell
Using arbitrary delays to wait for data propagation is unreliable. If Stripe webhook processing takes longer than 3 seconds, the user will see stale data. If it's faster, the user waits unnecessarily. Consider using polling or webhook confirmation instead.
Additional Comments (1)
|
- Eliminated outdated comments related to legacy plan detection and current pricing calculations. - Improved code readability by removing unnecessary comments that no longer serve a purpose.
- Revised pricing calculations for Hobby and Startup tiers to reflect accurate annual rates with 20% discount. - Updated comments for clarity and consistency in pricing logic. - Adjusted billing display to show the correct annual pricing for additional servers.
What is this PR about?
Please describe in a short paragraph what this PR is about.
Checklist
Before submitting this PR, please make sure that:
canarybranch.Issues related (if applicable)
Screenshots (if applicable)
Greptile Summary
This PR introduces a new tiered pricing system (Hobby and Startup) alongside the existing legacy plan. It adds UI for upgrading/changing plans, updates Stripe webhook handling to calculate server quantities correctly for multi-item subscriptions, and implements an
upgradeSubscriptionmutation for in-app plan changes.Key changes:
upgradeSubscriptionendpoint to change plans via Stripe subscription update APICritical issues found:
Confidence Score: 2/5
apps/dokploy/components/dashboard/settings/billing/show-billing.tsx(pricing calculations),apps/dokploy/server/api/routers/stripe.ts(subscription update logic),apps/dokploy/server/utils/stripe.ts(price ID validation), andapps/dokploy/pages/api/stripe/webhook.ts(stale data usage)Last reviewed commit: 6619043