add direct link to Stripe Customer Portal
This commit is contained in:
parent
71ceeb533f
commit
56c90d9c83
@ -10,6 +10,7 @@ import {
|
|||||||
} from "../ui/dropdown-menu";
|
} from "../ui/dropdown-menu";
|
||||||
import { useStore } from "../../store/store";
|
import { useStore } from "../../store/store";
|
||||||
import { capitalize } from "./utils";
|
import { capitalize } from "./utils";
|
||||||
|
import StripeCustomerPortalLink from "./StripeCustomerPortalLink";
|
||||||
|
|
||||||
export default function AvatarDropdown() {
|
export default function AvatarDropdown() {
|
||||||
const { user, isLoaded, isSignedIn } = useUser();
|
const { user, isLoaded, isSignedIn } = useUser();
|
||||||
@ -55,12 +56,7 @@ export default function AvatarDropdown() {
|
|||||||
</a>
|
</a>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem asChild={true}>
|
<DropdownMenuItem asChild={true}>
|
||||||
<a
|
<StripeCustomerPortalLink label="Cancel subscription" />
|
||||||
href="https://billing.stripe.com/p/login/dR65nxfkLgvldyg9AA"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
Cancel subscription
|
|
||||||
</a>
|
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
48
frontend/src/components/hosted/StripeCustomerPortalLink.tsx
Normal file
48
frontend/src/components/hosted/StripeCustomerPortalLink.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import toast from "react-hot-toast";
|
||||||
|
import { useAuthenticatedFetch } from "./useAuthenticatedFetch";
|
||||||
|
import { addEvent } from "../../lib/analytics";
|
||||||
|
import { SAAS_BACKEND_URL } from "../../config";
|
||||||
|
import { PortalSessionResponse } from "./types";
|
||||||
|
import { forwardRef, useState } from "react";
|
||||||
|
import Spinner from "../custom-ui/Spinner";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StripeCustomerPortalLink = forwardRef<HTMLAnchorElement, Props>(
|
||||||
|
({ label, ...props }, ref) => {
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const authenticatedFetch = useAuthenticatedFetch();
|
||||||
|
|
||||||
|
const redirectToBillingPortal = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const res: PortalSessionResponse = await authenticatedFetch(
|
||||||
|
SAAS_BACKEND_URL + "/payments/create_portal_session",
|
||||||
|
"POST"
|
||||||
|
);
|
||||||
|
window.location.href = res.url;
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(
|
||||||
|
"Error directing you to the billing portal. Please email support and we'll get it fixed right away."
|
||||||
|
);
|
||||||
|
addEvent("StripeBillingPortalError");
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a {...props} ref={ref} onClick={redirectToBillingPortal}>
|
||||||
|
<div className="flex gap-x-2">
|
||||||
|
{label} {isLoading && <Spinner />}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
StripeCustomerPortalLink.displayName = "StripeCustomerPortalLink";
|
||||||
|
|
||||||
|
export default StripeCustomerPortalLink;
|
||||||
@ -6,3 +6,7 @@ export interface UserResponse {
|
|||||||
subscriber_tier: string;
|
subscriber_tier: string;
|
||||||
stripe_customer_id: string;
|
stripe_customer_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PortalSessionResponse {
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|||||||
@ -2,6 +2,9 @@ import { useAuth } from "@clerk/clerk-react";
|
|||||||
|
|
||||||
type FetchMethod = "GET" | "POST" | "PUT" | "DELETE";
|
type FetchMethod = "GET" | "POST" | "PUT" | "DELETE";
|
||||||
|
|
||||||
|
// Assumes that the backend is using JWTs for authentication
|
||||||
|
// and assumes JSON responses
|
||||||
|
// *If response code is not 200 OK or if there's any other error, throws an error
|
||||||
export const useAuthenticatedFetch = () => {
|
export const useAuthenticatedFetch = () => {
|
||||||
const { getToken } = useAuth();
|
const { getToken } = useAuth();
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user