Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { formatTrialEnd, useTrialChangeOutcome } from '@/utils/trial-change'
import { Client, schemas } from '@polar-sh/client'
import { Button } from '@polar-sh/orbit'
import { List, ListItem } from '@polar-sh/orbit'
import { Checkbox } from '@polar-sh/orbit'
import { useRouter } from 'next/navigation'
import { useCallback, useMemo, useState } from 'react'
import { resolveBenefitIcon } from '../Benefit/utils'
Expand Down Expand Up @@ -136,7 +135,7 @@ const CustomerChangePlanModal = ({
return [willTrigger, chargeOrCredit]
}, [selectedProduct, prorationBehavior, subscription, isTrialing])

const invoicingMessage = useMemo(() => {
const invoicingMessage = useMemo((): string | null => {
if (!selectedProduct) return null

if (trialOutcome?.kind === 'continues') {
Expand All @@ -152,19 +151,21 @@ const CustomerChangePlanModal = ({
selectedProduct.recurring_interval === 'month' ? 'monthly' : 'yearly'

if (nextInvoiceType === 'charge') {
return `I'll be charged immediately for the new ${newPeriod} plan.`
return `You'll be charged immediately for the new ${newPeriod} plan.`
} else {
return `My previous payment will appear as a credit on my next invoice.`
return `Your previous payment will appear as a credit on your next invoice.`
}
}

switch (prorationBehavior) {
case 'invoice':
return "I'll be charged immediately with a proration for the current month."
return "You'll be charged immediately, with a proration for the current period."
case 'prorate':
return 'Your next invoice will include the new plan plus the proration for the current month.'
return 'Your next invoice will include the new plan plus the proration for the current period.'
case 'next_period':
return 'The new plan will be applied on your next billing cycle.'
case 'reset':
return "You'll be charged the full amount for the new plan immediately, and your billing period restarts today."
}
}, [
selectedProduct,
Expand All @@ -174,31 +175,17 @@ const CustomerChangePlanModal = ({
trialOutcome,
])

const willIssueInvoice =
trialOutcome?.kind === 'ends' ||
willTriggerImmediateCycle ||
prorationBehavior === 'invoice'
const [approveImmediateInvoice, setApproveImmediateInvoice] = useState(false)

const canChangePlan = useMemo(() => {
if (!selectedProduct) return false
const isSamePlan = selectedProduct?.id === subscription.product_id
if (isSamePlan) return false

if (willIssueInvoice && !approveImmediateInvoice) return false

const selectedPlanIsFree = selectedProduct?.prices.some(isFreePrice)

if (selectedPlanIsFree) return true

return hasPaymentMethod
}, [
hasPaymentMethod,
selectedProduct,
subscription,
willIssueInvoice,
approveImmediateInvoice,
])
}, [hasPaymentMethod, selectedProduct, subscription])

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.

P1: For reset proration changes, including same-interval changes, canChangePlan no longer requires customer approval even though the modal says the customer will be charged immediately. Restore the approval checkbox and gate submission for immediate invoice/reset changes, while keeping trial updates exempt from that gate.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At clients/apps/web/src/components/CustomerPortal/CustomerChangePlanModal.tsx, line 188:

<comment>For reset proration changes, including same-interval changes, `canChangePlan` no longer requires customer approval even though the modal says the customer will be charged immediately. Restore the approval checkbox and gate submission for immediate invoice/reset changes, while keeping trial updates exempt from that gate.</comment>

<file context>
@@ -176,32 +175,17 @@ const CustomerChangePlanModal = ({
-    willIssueInvoice,
-    approveImmediateInvoice,
-  ])
+  }, [hasPaymentMethod, selectedProduct, subscription])
 
   const updateSubscription = useCustomerUpdateSubscription(api)
</file context>


const updateSubscription = useCustomerUpdateSubscription(api)
const onConfirm = useCallback(async () => {
Expand Down Expand Up @@ -318,22 +305,9 @@ const CustomerChangePlanModal = ({
</div>
)}
{invoicingMessage && (
<label className="flex flex-row items-start gap-x-2">
{willIssueInvoice && (
<div>
<Checkbox
checked={approveImmediateInvoice}
onCheckedChange={(checked) =>
setApproveImmediateInvoice(checked === true)
}
/>
</div>
)}

<span className="dark:text-polar-500 text-sm text-pretty text-gray-500">
{invoicingMessage}
</span>
</label>
<span className="dark:text-polar-500 text-sm text-pretty text-gray-500">
{invoicingMessage}
</span>
)}
</div>
{needToAddPaymentMethod && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,17 @@ export const CustomerSeatQuantityManager = ({
const canDecrease = seats !== undefined && seats > assignedSeats
const hasChanges = seats !== totalSeats

const invoicingMessage = useMemo(() => {
const invoicingMessage = useMemo((): string | null => {
if (!prorationBehavior) return null
switch (prorationBehavior) {
case 'invoice':
return "I'll be charged immediately with a proration for the current month."
return "You'll be charged immediately, with a proration for the current period."
case 'prorate':
return 'Your next invoice will include the updated seats plus the proration for the current month.'
return 'Your next invoice will include the updated seats plus the proration for the current period.'
case 'next_period':
return 'The seat update will be applied on your next billing cycle.'
case 'reset':
return "You'll be charged the full new amount immediately, and your billing period restarts today."
}
}, [prorationBehavior])

Expand Down
Loading