create checkout sessions when subscribe is clicked
This commit is contained in:
parent
9bc5817aa4
commit
231d334679
@ -31,6 +31,7 @@
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@stripe/stripe-js": "^2.2.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"classnames": "^2.3.2",
|
||||
"clsx": "^2.0.0",
|
||||
|
||||
@ -8,10 +8,14 @@ import {
|
||||
DialogTrigger,
|
||||
} from "../ui/dialog";
|
||||
import { FaCheckCircle } from "react-icons/fa";
|
||||
import Spinner from "../custom-ui/Spinner";
|
||||
import useStripeCheckout from "./useStripeCheckout";
|
||||
|
||||
const LOGOS = ["microsoft", "amazon", "mit", "stanford", "bytedance", "baidu"];
|
||||
|
||||
const PricingDialog: React.FC = () => {
|
||||
const { checkout, isLoadingCheckout } = useStripeCheckout();
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger
|
||||
@ -86,6 +90,34 @@ const PricingDialog: React.FC = () => {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Economy Plan */}
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<h2 className="font-semibold">Hobby</h2>
|
||||
<p className="text-gray-500">Higher limits</p>
|
||||
<div className="my-4">
|
||||
<span className="text-4xl font-bold">$15</span>
|
||||
<span className="text-gray-500"> / month</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="bg-black text-white rounded py-2 px-4 w-full text-sm"
|
||||
onClick={() => checkout("test")}
|
||||
>
|
||||
Subscribe {isLoadingCheckout && <Spinner />}
|
||||
</button>
|
||||
|
||||
<ul className="mt-4 space-y-2">
|
||||
<li className="flex items-center">
|
||||
<FaCheckCircle className="text-black mr-2" />
|
||||
300 credits
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<FaCheckCircle className="text-black mr-2" />
|
||||
Slack support
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-center text-xs text-gray-600 mt-1">
|
||||
|
||||
66
frontend/src/components/payments/useStripeCheckout.ts
Normal file
66
frontend/src/components/payments/useStripeCheckout.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { SAAS_BACKEND_URL, STRIPE_PUBLISHABLE_KEY } from "../../config";
|
||||
import { addEvent } from "../../lib/analytics";
|
||||
import { Stripe, loadStripe } from "@stripe/stripe-js";
|
||||
import { useAuthenticatedFetch } from "../hosted/useAuthenticatedFetch";
|
||||
|
||||
interface CreateCheckoutSessionResponse {
|
||||
sessionId: string;
|
||||
}
|
||||
|
||||
export default function useStripeCheckout() {
|
||||
const authenticatedFetch = useAuthenticatedFetch();
|
||||
|
||||
const [stripe, setStripe] = useState<Stripe | null>(null);
|
||||
const [isLoadingCheckout, setIsLoadingCheckout] = useState(false);
|
||||
|
||||
const checkout = async (priceLookupKey: string) => {
|
||||
const rewardfulReferralId = "xxx"; // TODO: Use later with Rewardful
|
||||
|
||||
if (!stripe) {
|
||||
addEvent("StripeNotLoaded");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoadingCheckout(true);
|
||||
|
||||
// Create a Checkout Session
|
||||
const res: CreateCheckoutSessionResponse = await authenticatedFetch(
|
||||
`${SAAS_BACKEND_URL}/create_checkout_session` +
|
||||
`?price_lookup_key=${priceLookupKey}` +
|
||||
`&rewardful_referral_id=${rewardfulReferralId}`,
|
||||
"POST"
|
||||
);
|
||||
|
||||
// Redirect to Stripe Checkout
|
||||
const { error } = await stripe.redirectToCheckout({
|
||||
sessionId: res.sessionId,
|
||||
});
|
||||
if (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
} catch (e) {
|
||||
toast.error("Error directing you to checkout. Please contact support.");
|
||||
addEvent("StripeCheckoutError");
|
||||
} finally {
|
||||
setIsLoadingCheckout(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Load Stripe when the component mounts
|
||||
useEffect(() => {
|
||||
async function load() {
|
||||
try {
|
||||
setStripe(await loadStripe(STRIPE_PUBLISHABLE_KEY));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
addEvent("StripeFailedToLoad");
|
||||
}
|
||||
}
|
||||
load();
|
||||
}, []);
|
||||
|
||||
return { checkout, isLoadingCheckout };
|
||||
}
|
||||
@ -15,3 +15,8 @@ export const PICO_BACKEND_FORM_SECRET =
|
||||
|
||||
export const CLERK_PUBLISHABLE_KEY =
|
||||
import.meta.env.VITE_CLERK_PUBLISHABLE_KEY || null;
|
||||
|
||||
export const STRIPE_PUBLISHABLE_KEY =
|
||||
import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY || null;
|
||||
|
||||
export const SAAS_BACKEND_URL = import.meta.env.VITE_SAAS_BACKEND_URL || null;
|
||||
|
||||
@ -1287,6 +1287,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
|
||||
integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
|
||||
|
||||
"@stripe/stripe-js@^2.2.2":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-2.2.2.tgz#9c1318a3e09a6df0a667d54d7f6edb917670109d"
|
||||
integrity sha512-LvFZRZEBoMe6vXC6RoOAIbXWo/0JDdndq43ekL9M6affcM7PtF5KALmwt91BazW7q49sbSl0l7TunWhhSwEW4w==
|
||||
|
||||
"@types/babel__core@^7.20.3":
|
||||
version "7.20.4"
|
||||
resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user