Skip to content

Commit 604612d

Browse files
committed
[Dashboard] Improve error handling in billing checkout process
1 parent 9643b8d commit 604612d

File tree

3 files changed

+54
-12
lines changed

3 files changed

+54
-12
lines changed

apps/dashboard/src/app/(app)/(stripe)/_components/StripeRedirectErrorPage.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
1+
"use client";
2+
3+
import { Button } from "@/components/ui/button";
4+
import { useDashboardRouter } from "@/lib/DashboardRouter";
15
import { AlertTriangleIcon } from "lucide-react";
26

37
export function StripeRedirectErrorPage(props: {
48
errorMessage: string;
59
}) {
10+
const router = useDashboardRouter();
11+
612
return (
713
<div className="flex min-h-dvh items-center justify-center">
814
<div className="flex flex-col items-center text-center text-sm">
915
<div className="mb-4 rounded-full border p-2">
1016
<AlertTriangleIcon className="size-5 text-destructive-text" />
1117
</div>
1218
<p className="font-medium text-base">{props.errorMessage}</p>
19+
20+
<Button
21+
variant="outline"
22+
className="mt-4"
23+
onClick={() => router.back()}
24+
>
25+
Go back
26+
</Button>
1327
</div>
1428
</div>
1529
);

apps/dashboard/src/app/(app)/(stripe)/checkout/[team_slug]/[sku]/page.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,16 @@ export default async function CheckoutPage(props: {
5858
break;
5959
}
6060
default: {
61-
const billingUrl = await getBillingCheckoutUrl({
61+
const response = await getBillingCheckoutUrl({
6262
teamSlug: params.team_slug,
6363
sku: decodeURIComponent(params.sku) as Exclude<ProductSKU, null>,
6464
});
6565

66-
if (!billingUrl) {
67-
return (
68-
<StripeRedirectErrorPage errorMessage="Failed to load checkout page" />
69-
);
66+
if (response.status === "error") {
67+
return <StripeRedirectErrorPage errorMessage={response.error} />;
7068
}
7169

72-
redirect(billingUrl);
70+
redirect(response.data);
7371
break;
7472
}
7573
}

apps/dashboard/src/app/(app)/(stripe)/utils/billing.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ import { getAuthToken } from "../../api/lib/getAuthToken";
77
export async function getBillingCheckoutUrl(options: {
88
teamSlug: string;
99
sku: Exclude<ProductSKU, null>;
10-
}): Promise<string | undefined> {
10+
}) {
1111
const token = await getAuthToken();
1212

1313
if (!token) {
14-
return undefined;
14+
return {
15+
status: "error",
16+
error: "You are not logged in",
17+
} as const;
1518
}
1619

1720
const res = await fetch(
@@ -29,16 +32,43 @@ export async function getBillingCheckoutUrl(options: {
2932
},
3033
);
3134
if (!res.ok) {
32-
console.error("Failed to create checkout link", await res.json());
33-
return undefined;
35+
const text = await res.text();
36+
console.error("Failed to create checkout link", text, res.status);
37+
switch (res.status) {
38+
case 402: {
39+
return {
40+
status: "error",
41+
error:
42+
"You have outstanding invoices, please pay these first before re-subscribing.",
43+
} as const;
44+
}
45+
case 429: {
46+
return {
47+
status: "error",
48+
error: "Too many requests, please try again later.",
49+
} as const;
50+
}
51+
default: {
52+
return {
53+
status: "error",
54+
error: "An unknown error occurred, please try again later.",
55+
} as const;
56+
}
57+
}
3458
}
3559

3660
const json = await res.json();
3761
if (!json.result) {
38-
return undefined;
62+
return {
63+
status: "error",
64+
error: "An unknown error occurred, please try again later.",
65+
} as const;
3966
}
4067

41-
return json.result as string;
68+
return {
69+
status: "success",
70+
data: json.result as string,
71+
} as const;
4272
}
4373

4474
export async function getPlanCancelUrl(options: {

0 commit comments

Comments
 (0)