Skip to main content
Glama

Convex MCP server

Official
by get-convex
useStripe.ts5.6 kB
import { loadStripe, StripeElementsOptions, StripeElements, } from "@stripe/stripe-js"; import { useCreateSetupIntent } from "api/billing"; import { useState, useEffect, useCallback } from "react"; import { Team } from "generatedApi"; import { useTheme } from "next-themes"; const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY!); export function useStripePaymentSetup( team: Team, paymentMethod: string | undefined, setPaymentMethod: (paymentMethod?: string) => Promise<void>, hasAdminPermissions = true, ) { const createSetupIntent = useCreateSetupIntent(team.id); const [clientSecret, setClientSecret] = useState(""); useEffect(() => { if (!paymentMethod && hasAdminPermissions) { setClientSecret(""); void createSetupIntent().then((data) => { setClientSecret(data.clientSecret); }); } // dont want to run this effect if createSetupIntent changes // eslint-disable-next-line react-hooks/exhaustive-deps }, [paymentMethod, hasAdminPermissions]); const { resolvedTheme: currentTheme } = useTheme(); const prefersDark = currentTheme === "dark"; // Unfortunately, the Stripe API does not allow for dynamic theming via CSS variables, // so we have to hardcode the theme variables and keep them up to date. const variables = { colorPrimary: "rgb(38, 135, 246)", colorBackground: prefersDark ? "rgb(42, 40, 37)" : "rgb(253, 252, 250)", colorText: prefersDark ? "rgb(255, 255, 255)" : "rgb(42, 40, 37)", colorDanger: prefersDark ? "rgb(254, 76, 65)" : "rgb(238, 52, 47)", colorWarning: prefersDark ? "rgb(230, 226, 168)" : "rgb(109, 82, 23)", colorSuccess: prefersDark ? "rgb(180, 236, 146)" : "rgb(44, 83, 20)", colorTextPlaceholder: prefersDark ? "rgb(143, 135, 128)" : "rgb(120, 118, 133)", fontFamily: `"Inter Variable", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"`, spacingUnit: "0.25rem", borderRadius: "0.25rem", }; const borderTransparent = prefersDark ? "rgba(163, 156, 148, 0.3)" : "rgba(33, 34, 30, 0.14)"; const borderSelected = prefersDark ? "rgb(225, 215, 205)" : "rgb(30, 28, 25)"; const options: StripeElementsOptions = { clientSecret, // TODO: Finish styling to have the Stripe form match the rest of the app appearance: { theme: "stripe", variables, rules: { ".Label": { fontSize: "0.875rem", }, ".Error": { fontSize: "0.75rem", }, ".Input": { lineHeight: "1.25rem", fontSize: "0.875rem", boxShadow: "none", border: `1px solid ${borderTransparent}`, padding: "0.5rem 1rem", }, ".Input--invalid": { border: `1px solid ${borderTransparent}`, color: variables.colorText, boxShadow: "none", }, ".Input--invalid:focus": { border: `1px solid ${variables.colorDanger}`, }, ".Input:focus": { boxShadow: "none", border: `1px solid ${borderSelected}`, }, }, }, }; // Retrives the setup intent from stripe const retrieveSetupIntent = useCallback(async () => { const stripe = await stripePromise; const result = stripe && (await stripe.retrieveSetupIntent(clientSecret)); if (!result || !result.setupIntent) { return null; } const { payment_method } = result.setupIntent; if (payment_method !== null && typeof payment_method !== "string") { throw new Error(`Unexpected payment method type: ${payment_method}`); } await setPaymentMethod(payment_method || undefined); return result.setupIntent; }, [clientSecret, setPaymentMethod]); // Validates payment details and saves a payment method in stripe. const confirmSetup = useCallback( async (elements: StripeElements) => { const stripe = await stripePromise; if (!stripe) { return; } const { error: submitError } = await elements.submit(); if (submitError) { return { error: submitError.message }; } const { error, setupIntent } = await stripe.confirmSetup({ elements, clientSecret, redirect: "if_required", }); if (error) { return { error: error.message }; } if (!setupIntent || typeof setupIntent.payment_method !== "string") { throw new Error( `Unexpected payment method type: ${setupIntent.payment_method}`, ); } await setPaymentMethod(setupIntent.payment_method); return { error: undefined, paymentMethod: setupIntent.payment_method }; }, [clientSecret, setPaymentMethod], ); return { stripePromise, options, resetClientSecret: () => { setClientSecret(""); }, retrieveSetupIntent, confirmSetup, }; } export function useStripeAddressSetup( team: Team, hasAdminPermissions: boolean, ) { // Reuse the existing stripe initialization for collecting the payment method, // the main difference is that we don't need the retrieveSetupIntent and // confirmSetup functions so we pass in dummy values for paymentMethod and // setPaymentMethod. const { options } = useStripePaymentSetup( team, undefined, async () => {}, hasAdminPermissions, ); return { stripePromise, options, }; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/get-convex/convex-backend'

If you have feedback or need assistance with the MCP directory API, please join our Discord server