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-slot": "^1.0.2",
|
||||||
"@radix-ui/react-switch": "^1.0.3",
|
"@radix-ui/react-switch": "^1.0.3",
|
||||||
"@radix-ui/react-tabs": "^1.0.4",
|
"@radix-ui/react-tabs": "^1.0.4",
|
||||||
|
"@stripe/stripe-js": "^2.2.2",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
|
|||||||
@ -8,10 +8,14 @@ import {
|
|||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "../ui/dialog";
|
} from "../ui/dialog";
|
||||||
import { FaCheckCircle } from "react-icons/fa";
|
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 LOGOS = ["microsoft", "amazon", "mit", "stanford", "bytedance", "baidu"];
|
||||||
|
|
||||||
const PricingDialog: React.FC = () => {
|
const PricingDialog: React.FC = () => {
|
||||||
|
const { checkout, isLoadingCheckout } = useStripeCheckout();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger
|
<DialogTrigger
|
||||||
@ -86,6 +90,34 @@ const PricingDialog: React.FC = () => {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-center text-xs text-gray-600 mt-1">
|
<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 =
|
export const CLERK_PUBLISHABLE_KEY =
|
||||||
import.meta.env.VITE_CLERK_PUBLISHABLE_KEY || null;
|
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"
|
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
|
||||||
integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
|
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":
|
"@types/babel__core@^7.20.3":
|
||||||
version "7.20.4"
|
version "7.20.4"
|
||||||
resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz"
|
resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user