Skip to content

Commit 3543387

Browse files
committed
feat: add stripe portal
1 parent 8e8f78a commit 3543387

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

src/components/billing/pricing/index.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { getStripeClient } from "@/client-only/stripe";
44
import type { PricingPlanInterval, PricingType } from "@/prisma/enums";
55
import { api } from "@/trpc/react";
66
import type { RouterOutputs } from "@/trpc/shared";
7+
import { useRouter } from "next/navigation";
78
import { useState } from "react";
89
import { EmptyPlans } from "./empty-plans";
910
import { PricingButton } from "./pricing-button";
@@ -24,6 +25,7 @@ interface handleStripeCheckoutOptions {
2425
}
2526

2627
function Plans({ products, subscription }: PricingProps) {
28+
const router = useRouter();
2729
const intervals = Array.from(
2830
new Set(
2931
products.flatMap((product) =>
@@ -43,6 +45,12 @@ function Plans({ products, subscription }: PricingProps) {
4345
},
4446
});
4547

48+
const { mutateAsync: stripePortal } = api.billing.stripePortal.useMutation({
49+
onSuccess: ({ url }) => {
50+
router.push(url);
51+
},
52+
});
53+
4654
const handleBilling = (interval: PricingPlanInterval) => {
4755
setBillingInterval(interval);
4856
};
@@ -55,6 +63,10 @@ function Plans({ products, subscription }: PricingProps) {
5563
setPriceIdLoading(undefined);
5664
};
5765

66+
const handleBillingPortal = async () => {
67+
await stripePortal();
68+
};
69+
5870
return (
5971
<div>
6072
<div className="inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground">
@@ -98,13 +110,18 @@ function Plans({ products, subscription }: PricingProps) {
98110
price={priceString}
99111
interval={billingInterval}
100112
subscribed={!!subscription}
101-
onClick={() =>
102-
handleStripeCheckout({
113+
onClick={() => {
114+
if (subscription) {
115+
return handleBillingPortal();
116+
}
117+
return handleStripeCheckout({
103118
priceId: price.id,
104119
priceType: price.type,
105-
})
106-
}
120+
});
121+
}}
107122
loading={priceIdLoading === price.id}
123+
subscribedUnitAmount={subscription?.price.unitAmount}
124+
unitAmount={unitAmount}
108125
/>
109126
);
110127
})}

src/components/billing/pricing/pricing-card.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ interface PricingCardProps extends Omit<ButtonProps, "children"> {
1616
interval: PricingPlanInterval;
1717
subscribed: boolean;
1818
active: boolean;
19+
subscribedUnitAmount?: bigint | null;
20+
unitAmount: number;
1921
}
2022

2123
const humanizedInterval: Record<PricingPlanInterval, string> = {
@@ -32,8 +34,13 @@ export function PricingCard({
3234
price,
3335
subscribed,
3436
active,
37+
subscribedUnitAmount: subscribedUnitAmount_,
38+
unitAmount,
3539
...rest
3640
}: PricingCardProps) {
41+
const subscribedUnitAmount = subscribedUnitAmount_
42+
? Number(subscribedUnitAmount_)
43+
: null;
3744
return (
3845
<Card>
3946
<CardHeader>
@@ -47,7 +54,15 @@ export function PricingCard({
4754
<CardDescription>{description}</CardDescription>
4855
</CardHeader>
4956
<CardFooter>
50-
<Button {...rest}>{subscribed ? "Manage" : "Subscribe"}</Button>
57+
<Button {...rest}>
58+
{subscribedUnitAmount
59+
? subscribedUnitAmount < unitAmount
60+
? "Upgrade Plan"
61+
: subscribedUnitAmount > unitAmount
62+
? "Downgrade Plan"
63+
: "Manage Current Plan"
64+
: "Subscribe"}
65+
</Button>
5166
</CardFooter>
5267
</Card>
5368
);

src/trpc/routers/billing-router/procedures/get-subscription.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export const getSubscriptionProcedure = withAuth.query(async ({ ctx }) => {
3535
name: true,
3636
},
3737
},
38+
unitAmount: true,
3839
},
3940
},
4041
},

0 commit comments

Comments
 (0)