diff --git a/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2-with-discount.png b/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2-with-discount.png
index 737014b62b7f5..8eeeafbc340a1 100644
Binary files a/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2-with-discount.png and b/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2-with-discount.png differ
diff --git a/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2.png b/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2.png
index 4922e2d6ca640..3df423c0ff684 100644
Binary files a/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2.png and b/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2.png differ
diff --git a/frontend/src/lib/components/BillingAlertsV2.tsx b/frontend/src/lib/components/BillingAlertsV2.tsx
index 1748f0c91d4c2..21a4023a6262a 100644
--- a/frontend/src/lib/components/BillingAlertsV2.tsx
+++ b/frontend/src/lib/components/BillingAlertsV2.tsx
@@ -7,7 +7,7 @@ import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
export function BillingAlertsV2(): JSX.Element | null {
const { billingAlert } = useValues(billingLogic)
- const { reportBillingAlertShown } = useActions(billingLogic)
+ const { reportBillingAlertShown, reportBillingAlertActionClicked } = useActions(billingLogic)
const { currentLocation } = useValues(router)
const [alertHidden, setAlertHidden] = useState(false)
@@ -21,14 +21,19 @@ export function BillingAlertsV2(): JSX.Element | null {
return null
}
- const showButton = currentLocation.pathname !== urls.organizationBilling()
+ const showButton = billingAlert.contactSupport || currentLocation.pathname !== urls.organizationBilling()
const buttonProps = billingAlert.contactSupport
? {
to: 'mailto:sales@posthog.com',
- children: 'Contact support',
+ children: billingAlert.buttonCTA || 'Contact support',
+ onClick: () => reportBillingAlertActionClicked(billingAlert),
+ }
+ : {
+ to: urls.organizationBilling(),
+ children: 'Manage billing',
+ onClick: () => reportBillingAlertActionClicked(billingAlert),
}
- : { to: urls.organizationBilling(), children: 'Manage billing' }
return (
@@ -36,6 +41,7 @@ export function BillingAlertsV2(): JSX.Element | null {
type={billingAlert.status}
action={showButton ? buttonProps : undefined}
onClose={billingAlert.status !== 'error' ? () => setAlertHidden(true) : undefined}
+ dismissKey={billingAlert.dismissKey}
>
{billingAlert.title}
diff --git a/frontend/src/scenes/billing/Billing.tsx b/frontend/src/scenes/billing/Billing.tsx
index 87f2ab775409b..481b89aa9defd 100644
--- a/frontend/src/scenes/billing/Billing.tsx
+++ b/frontend/src/scenes/billing/Billing.tsx
@@ -264,7 +264,7 @@ export function Billing(): JSX.Element {
-
+
Products
{isOnboarding && upgradeAllProductsLink && (
): BillingV2Type => {
@@ -54,6 +56,7 @@ export const billingLogic = kea([
actions({
setShowLicenseDirectInput: (show: boolean) => ({ show }),
reportBillingAlertShown: (alertConfig: BillingAlertConfig) => ({ alertConfig }),
+ reportBillingAlertActionClicked: (alertConfig: BillingAlertConfig) => ({ alertConfig }),
reportBillingV2Shown: true,
registerInstrumentationProps: true,
setRedirectPath: true,
@@ -130,9 +133,22 @@ export const billingLogic = kea([
(s) => [s.preflight, s.billing],
(preflight, billing): boolean => !!preflight?.is_debug && !billing?.billing_period,
],
+ projectedTotalAmountUsd: [
+ (s) => [s.billing],
+ (billing: BillingV2Type): number => {
+ if (!billing) {
+ return 0
+ }
+ let projectedTotal = 0
+ for (const product of billing.products || []) {
+ projectedTotal += parseFloat(product.projected_amount_usd || '0')
+ }
+ return projectedTotal
+ },
+ ],
billingAlert: [
- (s) => [s.billing, s.preflight],
- (billing, preflight): BillingAlertConfig | undefined => {
+ (s) => [s.billing, s.preflight, s.projectedTotalAmountUsd],
+ (billing, preflight, projectedTotalAmountUsd): BillingAlertConfig | undefined => {
if (!billing || !preflight?.cloud) {
return
}
@@ -163,7 +179,7 @@ export const billingLogic = kea([
}
}
- const productOverLimit = billing.products?.find((x) => {
+ const productOverLimit = billing.products?.find((x: BillingProductV2Type) => {
return x.percentage_usage > 1
})
@@ -190,6 +206,21 @@ export const billingLogic = kea([
)}% of your ${productApproachingLimit.usage_key.toLowerCase()} allocation.`,
}
}
+
+ if (
+ billing.current_total_amount_usd_after_discount &&
+ (parseFloat(billing.current_total_amount_usd_after_discount) > 1000 ||
+ projectedTotalAmountUsd > 1000) &&
+ billing.billing_period?.interval === 'month'
+ ) {
+ return {
+ status: 'info',
+ title: `Switch to annual up-front billing to save up to 20% on your bill.`,
+ contactSupport: true,
+ buttonCTA: 'Contact sales',
+ dismissKey: 'annual-billing-cta',
+ }
+ }
},
],
}),
@@ -231,6 +262,11 @@ export const billingLogic = kea([
...alertConfig,
})
},
+ reportBillingAlertActionClicked: ({ alertConfig }) => {
+ posthog.capture('billing alert action clicked', {
+ ...alertConfig,
+ })
+ },
loadBillingSuccess: () => {
if (
router.values.location.pathname.includes('/organization/billing') &&
@@ -276,7 +312,6 @@ export const billingLogic = kea([
}
},
})),
-
afterMount(({ actions }) => {
actions.loadBilling()
}),