Skip to content

Commit

Permalink
feat: partner billing (supabase#18386)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevcodez authored Oct 24, 2023
1 parent b65b5b6 commit c4b20e1
Show file tree
Hide file tree
Showing 22 changed files with 973 additions and 1,706 deletions.
20 changes: 9 additions & 11 deletions studio/components/interfaces/BillingV2/Usage/UsageSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ const UsageSection = ({
subscription,
currentBillingCycleSelected,
}: UsageSectionProps) => {
const billingEnabled = useIsFeatureEnabled('billing:all')

const { data: usage } = useProjectUsageQuery({ projectRef })
const categoryMeta = USAGE_CATEGORIES.find((category) => category.key === categoryKey)

Expand Down Expand Up @@ -267,15 +265,15 @@ const UsageSection = ({
</p>
</div>
</div>
{billingEnabled && (
<Link
href={`/project/${projectRef}/settings/billing/subscription?panel=subscriptionPlan`}
>
<a>
<Button type="primary">Upgrade plan</Button>
</a>
</Link>
)}

<Link
href={`/project/${projectRef}/settings/billing/subscription?panel=subscriptionPlan`}
passHref
>
<Button type="primary" asChild>
<a>Upgrade plan</a>
</Button>
</Link>
</div>
</Panel.Content>
</Panel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ const AuditLogs = () => {
projects: [], // project_ref[]
})

const billingEnabled = useIsFeatureEnabled('billing:all')

const { data: projects } = useProjectsQuery()
const { data: organizations } = useOrganizationsQuery()
const { data: detailData } = useOrganizationDetailQuery({ slug })
Expand Down Expand Up @@ -235,13 +233,11 @@ const AuditLogs = () => {
</AlertDescription_Shadcn_>
</div>

{billingEnabled && (
<div className="flex items-center">
<Link href={`/org/${slug}/billing?panel=subscriptionPlan`}>
<Button type="primary">Upgrade subscription</Button>
</Link>
</div>
)}
<div className="flex items-center">
<Link href={`/org/${slug}/billing?panel=subscriptionPlan`}>
<Button type="primary">Upgrade subscription</Button>
</Link>
</div>
</div>
</Alert_Shadcn_>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Alert, Button } from 'ui'
import { BILLING_BREAKDOWN_METRICS } from './BillingBreakdown.constants'
import BillingMetric from './BillingMetric'
import UpcomingInvoice from './UpcomingInvoice'
import { useIsFeatureEnabled } from 'hooks'

const BillingBreakdown = () => {
const snap = useOrgSettingsPageStateSnapshot()
Expand All @@ -34,6 +35,8 @@ const BillingBreakdown = () => {
isSuccess: isSuccessSubscription,
} = useOrgSubscriptionQuery({ orgSlug })

const invoiceFeatureEnabled = useIsFeatureEnabled('billing:invoices')

const currentPlan = subscription?.plan
const isUsageBillingEnabled = subscription?.usage_billing_enabled
const billingCycleStart = dayjs.unix(subscription?.current_period_start ?? 0).utc()
Expand Down Expand Up @@ -145,17 +148,21 @@ const BillingBreakdown = () => {
</div>
)}

<p className="!mt-10 text-sm">Upcoming cost for next invoice</p>
<p className="text-sm text-foreground-light">
The following table shows your upcoming costs. Depending on your usage, the final
amount may vary. Next invoice on{' '}
<span className="text-foreground-light whitespace-nowrap">
{billingCycleEnd.format('MMM DD, YYYY')}
</span>
.
</p>
{invoiceFeatureEnabled && (
<>
<p className="!mt-10 text-sm">Upcoming cost for next invoice</p>
<p className="text-sm text-foreground-light">
The following table shows your upcoming costs. Depending on your usage, the final
amount may vary. Next invoice on{' '}
<span className="text-foreground-light whitespace-nowrap">
{billingCycleEnd.format('MMM DD, YYYY')}
</span>
.
</p>

<UpcomingInvoice slug={orgSlug} />
<UpcomingInvoice slug={orgSlug} />
</>
)}
</>
)}
</ScaffoldSectionContent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@ import PaymentMethods from './PaymentMethods/PaymentMethods'
import Subscription from './Subscription/Subscription'
import TaxID from './TaxID/TaxID'
import BillingBreakdown from './BillingBreakdown/BillingBreakdown'
import { useIsFeatureEnabled } from 'hooks'

const BillingSettings = () => {
const {
billingAccountData: billingAccountDataEnabled,
billingPaymentMethods: billingPaymentMethodsEnabled,
billingCredits: billingCreditsEnabled,
} = useIsFeatureEnabled(['billing:account_data', 'billing:payment_methods', 'billing:credits'])

return (
<>
<ScaffoldContainer id="subscription">
Expand All @@ -27,35 +34,47 @@ const BillingSettings = () => {
<BillingBreakdown />
</ScaffoldContainer>

<ScaffoldDivider />
{billingCreditsEnabled && (
<>
<ScaffoldDivider />

<ScaffoldContainer id="credits-balance">
<CreditBalance />
</ScaffoldContainer>
<ScaffoldContainer id="credits-balance">
<CreditBalance />
</ScaffoldContainer>
</>
)}

<ScaffoldDivider />
{billingPaymentMethodsEnabled && (
<>
<ScaffoldDivider />

<ScaffoldContainer id="payment-methods">
<PaymentMethods />
</ScaffoldContainer>
<ScaffoldContainer id="payment-methods">
<PaymentMethods />
</ScaffoldContainer>
</>
)}

<ScaffoldDivider />
{billingAccountDataEnabled && (
<>
<ScaffoldDivider />

<ScaffoldContainer id="email">
<BillingEmail />
</ScaffoldContainer>
<ScaffoldContainer id="email">
<BillingEmail />
</ScaffoldContainer>

<ScaffoldDivider />
<ScaffoldDivider />

<ScaffoldContainer id="address">
<BillingAddress />
</ScaffoldContainer>
<ScaffoldContainer id="address">
<BillingAddress />
</ScaffoldContainer>

<ScaffoldDivider />
<ScaffoldDivider />

<ScaffoldContainer id="taxId">
<TaxID />
</ScaffoldContainer>
<ScaffoldContainer id="taxId">
<TaxID />
</ScaffoldContainer>
</>
)}
</>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,20 @@ import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-que
import { useFlag } from 'hooks'
import { BASE_PATH } from 'lib/constants'
import { useOrgSettingsPageStateSnapshot } from 'state/organization-settings'
import { Alert, Button, IconExternalLink } from 'ui'
import {
Alert,
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
Alert_Shadcn_,
Button,
IconCalendar,
IconExternalLink,
} from 'ui'
import ProjectUpdateDisabledTooltip from '../../BillingSettings/ProjectUpdateDisabledTooltip'
import SpendCapSidePanel from './SpendCapSidePanel'
import Image from 'next/image'
import dayjs from 'dayjs'
import { useOrganizationBillingSubscriptionCancelSchedule } from 'data/subscriptions/org-subscription-cancel-schedule-mutation'

export interface CostControlProps {}

Expand All @@ -40,6 +50,9 @@ const CostControl = ({}: CostControlProps) => {
const canChangeTier =
!projectUpdateDisabled && !['team', 'enterprise'].includes(currentPlan?.id || '')

const { mutate: cancelSubscriptionSchedule, isLoading: cancelSubscriptionScheduleLoading } =
useOrganizationBillingSubscriptionCancelSchedule()

return (
<>
<ScaffoldSection>
Expand All @@ -52,7 +65,7 @@ const CostControl = ({}: CostControlProps) => {
</p>
</div>
<div className="space-y-2">
<p className="text-sm text-foreground-light">More information</p>
<p className="text-sm text-foreground-light m-0">More information</p>
<div>
<Link href="https://supabase.com/docs/guides/platform/spend-cap">
<a target="_blank" rel="noreferrer">
Expand Down Expand Up @@ -110,6 +123,36 @@ const CostControl = ({}: CostControlProps) => {
</p>
)}

{/** Toggled on spend cap, scheduled change for end-of-cycle */}
{subscription?.scheduled_plan_change?.target_plan === 'pro' &&
subscription?.scheduled_plan_change?.usage_billing_enabled === false &&
subscription?.plan.id === 'pro' && (
<Alert_Shadcn_ className="mb-2" title="Scheduled downgrade">
<IconCalendar className="h-4 w-4" />
<AlertTitle_Shadcn_>Scheduled change</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_ className="flex flex-col gap-3">
<div>
Your spend cap will be enabled on{' '}
{dayjs(subscription?.scheduled_plan_change?.at).format('MMMM D, YYYY')}. You
will not be charged for any over-usage moving on. If you would like to keep
the spend cap disabled and scale as you go, you may still cancel the
scheduled change.
</div>
<div>
<Button
type="default"
loading={cancelSubscriptionScheduleLoading}
onClick={() => {
return cancelSubscriptionSchedule({ slug: slug! })
}}
>
Cancel
</Button>
</div>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)}

<div className="flex space-x-6">
<div>
<div className="rounded-md bg-scale-100 dark:bg-scale-400 w-[160px] h-[96px] shadow">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ const DowngradeModal = ({
</div>
</li>
</ul>

{subscription?.billing_via_partner === true && (
<p className="mt-4 text-sm">
Your organization will be downgraded at the end of your current billing cycle.
</p>
)}
</div>
</Modal.Content>
</Modal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,12 @@ const PlanUpdateSidePanel = () => {
This organization is billed through one of our partners and you will be charged by
them directly.
</p>
{subscriptionPreview?.billed_via_partner &&
subscriptionPreview?.plan_change_type === 'downgrade' && (
<p className="text-sm">
Your organization will be downgraded at the end of your current billing cycle.
</p>
)}
</div>
)}
</Modal.Content>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,18 @@ import SparkBar from 'components/ui/SparkBar'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useFlag } from 'hooks'
import { useOrgSettingsPageStateSnapshot } from 'state/organization-settings'
import { Alert, Button, IconExternalLink } from 'ui'
import {
Alert,
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
Alert_Shadcn_,
Button,
IconCalendar,
IconExternalLink,
} from 'ui'
import ProjectUpdateDisabledTooltip from '../../BillingSettings/ProjectUpdateDisabledTooltip'
import PlanUpdateSidePanel from './PlanUpdateSidePanel'
import { useOrganizationBillingSubscriptionCancelSchedule } from 'data/subscriptions/org-subscription-cancel-schedule-mutation'

const Subscription = () => {
const { slug } = useParams()
Expand All @@ -30,6 +39,9 @@ const Subscription = () => {
isSuccess,
} = useOrgSubscriptionQuery({ orgSlug: slug })

const { mutate: cancelSubscriptionSchedule, isLoading: cancelSubscriptionScheduleLoading } =
useOrganizationBillingSubscriptionCancelSchedule()

const currentPlan = subscription?.plan
const planName = currentPlan?.name ?? 'Unknown'
const billingCycleStart = dayjs.unix(subscription?.current_period_start ?? 0).utc()
Expand Down Expand Up @@ -78,6 +90,38 @@ const Subscription = () => {
<p className="text-2xl text-brand uppercase">{currentPlan?.name ?? 'Unknown'}</p>
</div>

{subscription?.scheduled_plan_change &&
subscription?.scheduled_plan_change?.target_plan !== subscription.plan.id && (
<Alert_Shadcn_ className="mb-2" title="Scheduled downgrade">
<IconCalendar className="h-4 w-4" />
<AlertTitle_Shadcn_>Scheduled downgrade</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_ className="flex flex-col gap-3">
<div>
Your organization will automatically be downgraded from the{' '}
<span>{subscription.plan.name}</span> plan to the{' '}
<span className="capitalize">
{subscription?.scheduled_plan_change?.target_plan}
</span>{' '}
plan on{' '}
{dayjs(subscription?.scheduled_plan_change?.at).format('MMMM D, YYYY')}. If
you would like to stay on the <span>{subscription.plan.name}</span> plan,
cancel the scheduled downgrade.
</div>
<div>
<Button
type="default"
loading={cancelSubscriptionScheduleLoading}
onClick={() => {
return cancelSubscriptionSchedule({ slug: slug! })
}}
>
Cancel downgrade
</Button>
</div>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)}

<div>
<ProjectUpdateDisabledTooltip projectUpdateDisabled={projectUpdateDisabled}>
<Button
Expand Down
Loading

0 comments on commit c4b20e1

Please sign in to comment.