diff --git a/apps/web/app/(use-page-wrapper)/payment/[uid]/PaymentPage.tsx b/apps/web/app/(use-page-wrapper)/payment/[uid]/PaymentPage.tsx index 71367a72a882ad..4936c22ccb3849 100644 --- a/apps/web/app/(use-page-wrapper)/payment/[uid]/PaymentPage.tsx +++ b/apps/web/app/(use-page-wrapper)/payment/[uid]/PaymentPage.tsx @@ -56,6 +56,13 @@ const BtcpayPaymentComponent = dynamic( } ); +const PaystackPaymentComponent = dynamic( + () => import("@calcom/app-store/paystack/components/PaystackPaymentComponent"), + { + ssr: false, + } +); + const PaymentPage: FC = (props) => { const { t, i18n } = useLocale(); const [is24h, setIs24h] = useState(isBrowserLocale24h()); @@ -174,6 +181,18 @@ const PaymentPage: FC = (props) => { {props.payment.appId === "btcpayserver" && !props.payment.success && ( )} + {props.payment.appId === "paystack" && !props.payment.success && ( + + )} {props.payment.refunded && (
{t("refunded")}
)} diff --git a/apps/web/components/apps/AppSetupPage.tsx b/apps/web/components/apps/AppSetupPage.tsx index 88acf8c89bbb76..7d52d74d86d55b 100644 --- a/apps/web/components/apps/AppSetupPage.tsx +++ b/apps/web/components/apps/AppSetupPage.tsx @@ -15,6 +15,7 @@ export const AppSetupMap = { paypal: dynamic(() => import("@calcom/web/components/apps/paypal/Setup")), hitpay: dynamic(() => import("@calcom/web/components/apps/hitpay/Setup")), btcpayserver: dynamic(() => import("@calcom/web/components/apps/btcpayserver/Setup")), + paystack: dynamic(() => import("@calcom/web/components/apps/paystack/Setup")), }; export const AppSetupPage = (props: { slug: string }) => { diff --git a/apps/web/components/apps/paystack/Setup.tsx b/apps/web/components/apps/paystack/Setup.tsx new file mode 100644 index 00000000000000..71484e73e1da80 --- /dev/null +++ b/apps/web/components/apps/paystack/Setup.tsx @@ -0,0 +1,132 @@ +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { Toaster } from "sonner"; + +import AppNotInstalledMessage from "@calcom/app-store/_components/AppNotInstalledMessage"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { trpc } from "@calcom/trpc/react"; +import { Button } from "@calcom/ui/components/button"; +import { TextField } from "@calcom/ui/components/form"; +import { showToast } from "@calcom/ui/components/toast"; + +export default function PaystackSetup() { + const [newPublicKey, setNewPublicKey] = useState(""); + const [newSecretKey, setNewSecretKey] = useState(""); + const router = useRouter(); + const { t } = useLocale(); + + const integrations = trpc.viewer.apps.integrations.useQuery({ + variant: "payment", + appId: "paystack", + }); + + const [paystackCredentials] = integrations.data?.items || []; + const [credentialId] = paystackCredentials?.userCredentialIds || [-1]; + + const showContent = !!integrations.data && integrations.isSuccess && !!credentialId; + + const saveKeysMutation = trpc.viewer.apps.updateAppCredentials.useMutation({ + onSuccess: () => { + showToast(t("keys_have_been_saved"), "success"); + router.push("/event-types"); + }, + onError: (error) => { + showToast(error.message, "error"); + }, + }); + + if (integrations.isPending) { + return
; + } + + return ( +
+ {showContent ? ( +
+
+
+ Paystack +

Paystack

+
+ +
{ + e.preventDefault(); + saveKeysMutation.mutate({ + credentialId, + key: { + public_key: newPublicKey, + secret_key: newSecretKey, + }, + }); + }}> + setNewPublicKey(e.target.value)} + role="presentation" + className="mb-6" + placeholder="pk_test_xxxxxxxxx" + /> + + setNewSecretKey(e.target.value)} + placeholder="sk_test_xxxxxxxxx" + /> + +
+ +
+ + +
+

{t("getting_started")}

+

+ {t("paystack_getting_started_description")}{" "} + + {t("paystack_dashboard")} + + . +

+ +

{t("paystack_webhook_setup")}

+

+ {t("paystack_webhook_setup_description")} +

+ + {typeof window !== "undefined" ? window.location.origin : "https://your-cal.com"} + /api/integrations/paystack/webhook + +
+
+
+ ) : ( + + )} + + +
+ ); +} diff --git a/packages/app-store/_pages/setup/_getServerSideProps.tsx b/packages/app-store/_pages/setup/_getServerSideProps.tsx index 8004f5c19ed4a9..31442cb75c8811 100644 --- a/packages/app-store/_pages/setup/_getServerSideProps.tsx +++ b/packages/app-store/_pages/setup/_getServerSideProps.tsx @@ -6,6 +6,7 @@ export const AppSetupPageMap = { stripe: import("../../stripepayment/pages/setup/_getServerSideProps"), hitpay: import("../../hitpay/pages/setup/_getServerSideProps"), btcpayserver: import("../../btcpayserver/pages/setup/_getServerSideProps"), + paystack: import("../../paystack/pages/setup/_getServerSideProps"), }; export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { diff --git a/packages/app-store/analytics.services.generated.ts b/packages/app-store/analytics.services.generated.ts index 92bc722083016c..8f101cded8ea14 100644 --- a/packages/app-store/analytics.services.generated.ts +++ b/packages/app-store/analytics.services.generated.ts @@ -2,9 +2,6 @@ This file is autogenerated using the command `yarn app-store:build --watch`. Don't modify this file manually. **/ -export const AnalyticsServiceMap = - process.env.NEXT_PUBLIC_IS_E2E === "1" - ? {} - : { - dub: import("./dub/lib/AnalyticsService"), - }; +export const AnalyticsServiceMap = process.env.NEXT_PUBLIC_IS_E2E === '1' ? {} : { +"dub": import("./dub/lib/AnalyticsService"), +}; \ No newline at end of file diff --git a/packages/app-store/apps.browser.generated.tsx b/packages/app-store/apps.browser.generated.tsx index cf6fef19fff46f..f2510a99095cb4 100644 --- a/packages/app-store/apps.browser.generated.tsx +++ b/packages/app-store/apps.browser.generated.tsx @@ -2,69 +2,63 @@ This file is autogenerated using the command `yarn app-store:build --watch`. Don't modify this file manually. **/ -import dynamic from "next/dynamic"; +import dynamic from "next/dynamic" export const InstallAppButtonMap = { - exchange2013calendar: dynamic(() => import("./exchange2013calendar/components/InstallAppButton")), - exchange2016calendar: dynamic(() => import("./exchange2016calendar/components/InstallAppButton")), - office365video: dynamic(() => import("./office365video/components/InstallAppButton")), - vital: dynamic(() => import("./vital/components/InstallAppButton")), +"exchange2013calendar": dynamic(() => import("./exchange2013calendar/components/InstallAppButton")), +"exchange2016calendar": dynamic(() => import("./exchange2016calendar/components/InstallAppButton")), +"office365video": dynamic(() => import("./office365video/components/InstallAppButton")), +"vital": dynamic(() => import("./vital/components/InstallAppButton")), }; export const AppSettingsComponentsMap = { - "general-app-settings": dynamic( - () => import("./templates/general-app-settings/components/AppSettingsInterface") - ), - weather_in_your_calendar: dynamic( - () => import("./weather_in_your_calendar/components/AppSettingsInterface") - ), - zapier: dynamic(() => import("./zapier/components/AppSettingsInterface")), +"general-app-settings": dynamic(() => import("./templates/general-app-settings/components/AppSettingsInterface")), +"weather_in_your_calendar": dynamic(() => import("./weather_in_your_calendar/components/AppSettingsInterface")), +"zapier": dynamic(() => import("./zapier/components/AppSettingsInterface")), }; export const EventTypeAddonMap = { - alby: dynamic(() => import("./alby/components/EventTypeAppCardInterface")), - basecamp3: dynamic(() => import("./basecamp3/components/EventTypeAppCardInterface")), - btcpayserver: dynamic(() => import("./btcpayserver/components/EventTypeAppCardInterface")), - closecom: dynamic(() => import("./closecom/components/EventTypeAppCardInterface")), - databuddy: dynamic(() => import("./databuddy/components/EventTypeAppCardInterface")), - fathom: dynamic(() => import("./fathom/components/EventTypeAppCardInterface")), - ga4: dynamic(() => import("./ga4/components/EventTypeAppCardInterface")), - giphy: dynamic(() => import("./giphy/components/EventTypeAppCardInterface")), - gtm: dynamic(() => import("./gtm/components/EventTypeAppCardInterface")), - hitpay: dynamic(() => import("./hitpay/components/EventTypeAppCardInterface")), - hubspot: dynamic(() => import("./hubspot/components/EventTypeAppCardInterface")), - insihts: dynamic(() => import("./insihts/components/EventTypeAppCardInterface")), - matomo: dynamic(() => import("./matomo/components/EventTypeAppCardInterface")), - metapixel: dynamic(() => import("./metapixel/components/EventTypeAppCardInterface")), - "mock-payment-app": dynamic(() => import("./mock-payment-app/components/EventTypeAppCardInterface")), - paypal: dynamic(() => import("./paypal/components/EventTypeAppCardInterface")), - "pipedrive-crm": dynamic(() => import("./pipedrive-crm/components/EventTypeAppCardInterface")), - plausible: dynamic(() => import("./plausible/components/EventTypeAppCardInterface")), - posthog: dynamic(() => import("./posthog/components/EventTypeAppCardInterface")), - qr_code: dynamic(() => import("./qr_code/components/EventTypeAppCardInterface")), - salesforce: dynamic(() => import("./salesforce/components/EventTypeAppCardInterface")), - stripepayment: dynamic(() => import("./stripepayment/components/EventTypeAppCardInterface")), - "booking-pages-tag": dynamic( - () => import("./templates/booking-pages-tag/components/EventTypeAppCardInterface") - ), - "event-type-app-card": dynamic( - () => import("./templates/event-type-app-card/components/EventTypeAppCardInterface") - ), - twipla: dynamic(() => import("./twipla/components/EventTypeAppCardInterface")), - umami: dynamic(() => import("./umami/components/EventTypeAppCardInterface")), - "zoho-bigin": dynamic(() => import("./zoho-bigin/components/EventTypeAppCardInterface")), - zohocrm: dynamic(() => import("./zohocrm/components/EventTypeAppCardInterface")), +"alby": dynamic(() => import("./alby/components/EventTypeAppCardInterface")), +"basecamp3": dynamic(() => import("./basecamp3/components/EventTypeAppCardInterface")), +"btcpayserver": dynamic(() => import("./btcpayserver/components/EventTypeAppCardInterface")), +"closecom": dynamic(() => import("./closecom/components/EventTypeAppCardInterface")), +"databuddy": dynamic(() => import("./databuddy/components/EventTypeAppCardInterface")), +"fathom": dynamic(() => import("./fathom/components/EventTypeAppCardInterface")), +"ga4": dynamic(() => import("./ga4/components/EventTypeAppCardInterface")), +"giphy": dynamic(() => import("./giphy/components/EventTypeAppCardInterface")), +"gtm": dynamic(() => import("./gtm/components/EventTypeAppCardInterface")), +"hitpay": dynamic(() => import("./hitpay/components/EventTypeAppCardInterface")), +"hubspot": dynamic(() => import("./hubspot/components/EventTypeAppCardInterface")), +"insihts": dynamic(() => import("./insihts/components/EventTypeAppCardInterface")), +"matomo": dynamic(() => import("./matomo/components/EventTypeAppCardInterface")), +"metapixel": dynamic(() => import("./metapixel/components/EventTypeAppCardInterface")), +"mock-payment-app": dynamic(() => import("./mock-payment-app/components/EventTypeAppCardInterface")), +"paypal": dynamic(() => import("./paypal/components/EventTypeAppCardInterface")), +"paystack": dynamic(() => import("./paystack/components/EventTypeAppCardInterface")), +"pipedrive-crm": dynamic(() => import("./pipedrive-crm/components/EventTypeAppCardInterface")), +"plausible": dynamic(() => import("./plausible/components/EventTypeAppCardInterface")), +"posthog": dynamic(() => import("./posthog/components/EventTypeAppCardInterface")), +"qr_code": dynamic(() => import("./qr_code/components/EventTypeAppCardInterface")), +"salesforce": dynamic(() => import("./salesforce/components/EventTypeAppCardInterface")), +"stripepayment": dynamic(() => import("./stripepayment/components/EventTypeAppCardInterface")), +"booking-pages-tag": dynamic(() => import("./templates/booking-pages-tag/components/EventTypeAppCardInterface")), +"event-type-app-card": dynamic(() => import("./templates/event-type-app-card/components/EventTypeAppCardInterface")), +"twipla": dynamic(() => import("./twipla/components/EventTypeAppCardInterface")), +"umami": dynamic(() => import("./umami/components/EventTypeAppCardInterface")), +"zoho-bigin": dynamic(() => import("./zoho-bigin/components/EventTypeAppCardInterface")), +"zohocrm": dynamic(() => import("./zohocrm/components/EventTypeAppCardInterface")), }; export const EventTypeSettingsMap = { - alby: dynamic(() => import("./alby/components/EventTypeAppSettingsInterface")), - basecamp3: dynamic(() => import("./basecamp3/components/EventTypeAppSettingsInterface")), - btcpayserver: dynamic(() => import("./btcpayserver/components/EventTypeAppSettingsInterface")), - databuddy: dynamic(() => import("./databuddy/components/EventTypeAppSettingsInterface")), - fathom: dynamic(() => import("./fathom/components/EventTypeAppSettingsInterface")), - ga4: dynamic(() => import("./ga4/components/EventTypeAppSettingsInterface")), - giphy: dynamic(() => import("./giphy/components/EventTypeAppSettingsInterface")), - gtm: dynamic(() => import("./gtm/components/EventTypeAppSettingsInterface")), - hitpay: dynamic(() => import("./hitpay/components/EventTypeAppSettingsInterface")), - metapixel: dynamic(() => import("./metapixel/components/EventTypeAppSettingsInterface")), - paypal: dynamic(() => import("./paypal/components/EventTypeAppSettingsInterface")), - plausible: dynamic(() => import("./plausible/components/EventTypeAppSettingsInterface")), - qr_code: dynamic(() => import("./qr_code/components/EventTypeAppSettingsInterface")), - stripepayment: dynamic(() => import("./stripepayment/components/EventTypeAppSettingsInterface")), -}; +"alby": dynamic(() => import("./alby/components/EventTypeAppSettingsInterface")), +"basecamp3": dynamic(() => import("./basecamp3/components/EventTypeAppSettingsInterface")), +"btcpayserver": dynamic(() => import("./btcpayserver/components/EventTypeAppSettingsInterface")), +"databuddy": dynamic(() => import("./databuddy/components/EventTypeAppSettingsInterface")), +"fathom": dynamic(() => import("./fathom/components/EventTypeAppSettingsInterface")), +"ga4": dynamic(() => import("./ga4/components/EventTypeAppSettingsInterface")), +"giphy": dynamic(() => import("./giphy/components/EventTypeAppSettingsInterface")), +"gtm": dynamic(() => import("./gtm/components/EventTypeAppSettingsInterface")), +"hitpay": dynamic(() => import("./hitpay/components/EventTypeAppSettingsInterface")), +"metapixel": dynamic(() => import("./metapixel/components/EventTypeAppSettingsInterface")), +"paypal": dynamic(() => import("./paypal/components/EventTypeAppSettingsInterface")), +"paystack": dynamic(() => import("./paystack/components/EventTypeAppSettingsInterface")), +"plausible": dynamic(() => import("./plausible/components/EventTypeAppSettingsInterface")), +"qr_code": dynamic(() => import("./qr_code/components/EventTypeAppSettingsInterface")), +"stripepayment": dynamic(() => import("./stripepayment/components/EventTypeAppSettingsInterface")), +}; \ No newline at end of file diff --git a/packages/app-store/apps.keys-schemas.generated.ts b/packages/app-store/apps.keys-schemas.generated.ts index 5df66d19f7411c..5f65e45a5407d3 100644 --- a/packages/app-store/apps.keys-schemas.generated.ts +++ b/packages/app-store/apps.keys-schemas.generated.ts @@ -2,107 +2,109 @@ This file is autogenerated using the command `yarn app-store:build --watch`. Don't modify this file manually. **/ -import { appKeysSchema as alby_zod_ts } from "./alby/zod"; -import { appKeysSchema as basecamp3_zod_ts } from "./basecamp3/zod"; -import { appKeysSchema as btcpayserver_zod_ts } from "./btcpayserver/zod"; -import { appKeysSchema as closecom_zod_ts } from "./closecom/zod"; -import { appKeysSchema as dailyvideo_zod_ts } from "./dailyvideo/zod"; -import { appKeysSchema as databuddy_zod_ts } from "./databuddy/zod"; -import { appKeysSchema as dub_zod_ts } from "./dub/zod"; -import { appKeysSchema as fathom_zod_ts } from "./fathom/zod"; -import { appKeysSchema as feishucalendar_zod_ts } from "./feishucalendar/zod"; -import { appKeysSchema as ga4_zod_ts } from "./ga4/zod"; -import { appKeysSchema as giphy_zod_ts } from "./giphy/zod"; -import { appKeysSchema as googlecalendar_zod_ts } from "./googlecalendar/zod"; -import { appKeysSchema as googlevideo_zod_ts } from "./googlevideo/zod"; -import { appKeysSchema as gtm_zod_ts } from "./gtm/zod"; -import { appKeysSchema as hitpay_zod_ts } from "./hitpay/zod"; -import { appKeysSchema as hubspot_zod_ts } from "./hubspot/zod"; -import { appKeysSchema as insihts_zod_ts } from "./insihts/zod"; -import { appKeysSchema as intercom_zod_ts } from "./intercom/zod"; -import { appKeysSchema as jelly_zod_ts } from "./jelly/zod"; -import { appKeysSchema as jitsivideo_zod_ts } from "./jitsivideo/zod"; -import { appKeysSchema as larkcalendar_zod_ts } from "./larkcalendar/zod"; -import { appKeysSchema as lyra_zod_ts } from "./lyra/zod"; -import { appKeysSchema as make_zod_ts } from "./make/zod"; -import { appKeysSchema as matomo_zod_ts } from "./matomo/zod"; -import { appKeysSchema as metapixel_zod_ts } from "./metapixel/zod"; -import { appKeysSchema as mock_payment_app_zod_ts } from "./mock-payment-app/zod"; -import { appKeysSchema as nextcloudtalk_zod_ts } from "./nextcloudtalk/zod"; -import { appKeysSchema as office365calendar_zod_ts } from "./office365calendar/zod"; -import { appKeysSchema as office365video_zod_ts } from "./office365video/zod"; -import { appKeysSchema as paypal_zod_ts } from "./paypal/zod"; -import { appKeysSchema as pipedrive_crm_zod_ts } from "./pipedrive-crm/zod"; -import { appKeysSchema as plausible_zod_ts } from "./plausible/zod"; -import { appKeysSchema as posthog_zod_ts } from "./posthog/zod"; -import { appKeysSchema as qr_code_zod_ts } from "./qr_code/zod"; -import { appKeysSchema as routing_forms_zod_ts } from "./routing-forms/zod"; -import { appKeysSchema as salesforce_zod_ts } from "./salesforce/zod"; -import { appKeysSchema as shimmervideo_zod_ts } from "./shimmervideo/zod"; -import { appKeysSchema as stripepayment_zod_ts } from "./stripepayment/zod"; -import { appKeysSchema as tandemvideo_zod_ts } from "./tandemvideo/zod"; -import { appKeysSchema as booking_pages_tag_zod_ts } from "./templates/booking-pages-tag/zod"; -import { appKeysSchema as event_type_app_card_zod_ts } from "./templates/event-type-app-card/zod"; -import { appKeysSchema as twipla_zod_ts } from "./twipla/zod"; -import { appKeysSchema as umami_zod_ts } from "./umami/zod"; -import { appKeysSchema as vital_zod_ts } from "./vital/zod"; -import { appKeysSchema as webex_zod_ts } from "./webex/zod"; -import { appKeysSchema as wordpress_zod_ts } from "./wordpress/zod"; -import { appKeysSchema as zapier_zod_ts } from "./zapier/zod"; -import { appKeysSchema as zoho_bigin_zod_ts } from "./zoho-bigin/zod"; -import { appKeysSchema as zohocalendar_zod_ts } from "./zohocalendar/zod"; -import { appKeysSchema as zohocrm_zod_ts } from "./zohocrm/zod"; -import { appKeysSchema as zoomvideo_zod_ts } from "./zoomvideo/zod"; +import { appKeysSchema as alby_zod_ts } from "./alby/zod" +import { appKeysSchema as basecamp3_zod_ts } from "./basecamp3/zod" +import { appKeysSchema as btcpayserver_zod_ts } from "./btcpayserver/zod" +import { appKeysSchema as closecom_zod_ts } from "./closecom/zod" +import { appKeysSchema as dailyvideo_zod_ts } from "./dailyvideo/zod" +import { appKeysSchema as databuddy_zod_ts } from "./databuddy/zod" +import { appKeysSchema as dub_zod_ts } from "./dub/zod" +import { appKeysSchema as fathom_zod_ts } from "./fathom/zod" +import { appKeysSchema as feishucalendar_zod_ts } from "./feishucalendar/zod" +import { appKeysSchema as ga4_zod_ts } from "./ga4/zod" +import { appKeysSchema as giphy_zod_ts } from "./giphy/zod" +import { appKeysSchema as googlecalendar_zod_ts } from "./googlecalendar/zod" +import { appKeysSchema as googlevideo_zod_ts } from "./googlevideo/zod" +import { appKeysSchema as gtm_zod_ts } from "./gtm/zod" +import { appKeysSchema as hitpay_zod_ts } from "./hitpay/zod" +import { appKeysSchema as hubspot_zod_ts } from "./hubspot/zod" +import { appKeysSchema as insihts_zod_ts } from "./insihts/zod" +import { appKeysSchema as intercom_zod_ts } from "./intercom/zod" +import { appKeysSchema as jelly_zod_ts } from "./jelly/zod" +import { appKeysSchema as jitsivideo_zod_ts } from "./jitsivideo/zod" +import { appKeysSchema as larkcalendar_zod_ts } from "./larkcalendar/zod" +import { appKeysSchema as lyra_zod_ts } from "./lyra/zod" +import { appKeysSchema as make_zod_ts } from "./make/zod" +import { appKeysSchema as matomo_zod_ts } from "./matomo/zod" +import { appKeysSchema as metapixel_zod_ts } from "./metapixel/zod" +import { appKeysSchema as mock_payment_app_zod_ts } from "./mock-payment-app/zod" +import { appKeysSchema as nextcloudtalk_zod_ts } from "./nextcloudtalk/zod" +import { appKeysSchema as office365calendar_zod_ts } from "./office365calendar/zod" +import { appKeysSchema as office365video_zod_ts } from "./office365video/zod" +import { appKeysSchema as paypal_zod_ts } from "./paypal/zod" +import { appKeysSchema as paystack_zod_ts } from "./paystack/zod" +import { appKeysSchema as pipedrive_crm_zod_ts } from "./pipedrive-crm/zod" +import { appKeysSchema as plausible_zod_ts } from "./plausible/zod" +import { appKeysSchema as posthog_zod_ts } from "./posthog/zod" +import { appKeysSchema as qr_code_zod_ts } from "./qr_code/zod" +import { appKeysSchema as routing_forms_zod_ts } from "./routing-forms/zod" +import { appKeysSchema as salesforce_zod_ts } from "./salesforce/zod" +import { appKeysSchema as shimmervideo_zod_ts } from "./shimmervideo/zod" +import { appKeysSchema as stripepayment_zod_ts } from "./stripepayment/zod" +import { appKeysSchema as tandemvideo_zod_ts } from "./tandemvideo/zod" +import { appKeysSchema as booking_pages_tag_zod_ts } from "./templates/booking-pages-tag/zod" +import { appKeysSchema as event_type_app_card_zod_ts } from "./templates/event-type-app-card/zod" +import { appKeysSchema as twipla_zod_ts } from "./twipla/zod" +import { appKeysSchema as umami_zod_ts } from "./umami/zod" +import { appKeysSchema as vital_zod_ts } from "./vital/zod" +import { appKeysSchema as webex_zod_ts } from "./webex/zod" +import { appKeysSchema as wordpress_zod_ts } from "./wordpress/zod" +import { appKeysSchema as zapier_zod_ts } from "./zapier/zod" +import { appKeysSchema as zoho_bigin_zod_ts } from "./zoho-bigin/zod" +import { appKeysSchema as zohocalendar_zod_ts } from "./zohocalendar/zod" +import { appKeysSchema as zohocrm_zod_ts } from "./zohocrm/zod" +import { appKeysSchema as zoomvideo_zod_ts } from "./zoomvideo/zod" export const appKeysSchemas = { - alby: alby_zod_ts, - basecamp3: basecamp3_zod_ts, - btcpayserver: btcpayserver_zod_ts, - closecom: closecom_zod_ts, - dailyvideo: dailyvideo_zod_ts, - databuddy: databuddy_zod_ts, - dub: dub_zod_ts, - fathom: fathom_zod_ts, - feishucalendar: feishucalendar_zod_ts, - ga4: ga4_zod_ts, - giphy: giphy_zod_ts, - googlecalendar: googlecalendar_zod_ts, - googlevideo: googlevideo_zod_ts, - gtm: gtm_zod_ts, - hitpay: hitpay_zod_ts, - hubspot: hubspot_zod_ts, - insihts: insihts_zod_ts, - intercom: intercom_zod_ts, - jelly: jelly_zod_ts, - jitsivideo: jitsivideo_zod_ts, - larkcalendar: larkcalendar_zod_ts, - lyra: lyra_zod_ts, - make: make_zod_ts, - matomo: matomo_zod_ts, - metapixel: metapixel_zod_ts, - "mock-payment-app": mock_payment_app_zod_ts, - nextcloudtalk: nextcloudtalk_zod_ts, - office365calendar: office365calendar_zod_ts, - office365video: office365video_zod_ts, - paypal: paypal_zod_ts, - "pipedrive-crm": pipedrive_crm_zod_ts, - plausible: plausible_zod_ts, - posthog: posthog_zod_ts, - qr_code: qr_code_zod_ts, - "routing-forms": routing_forms_zod_ts, - salesforce: salesforce_zod_ts, - shimmervideo: shimmervideo_zod_ts, - stripe: stripepayment_zod_ts, - tandemvideo: tandemvideo_zod_ts, - "booking-pages-tag": booking_pages_tag_zod_ts, - "event-type-app-card": event_type_app_card_zod_ts, - twipla: twipla_zod_ts, - umami: umami_zod_ts, - vital: vital_zod_ts, - webex: webex_zod_ts, - wordpress: wordpress_zod_ts, - zapier: zapier_zod_ts, - "zoho-bigin": zoho_bigin_zod_ts, - zohocalendar: zohocalendar_zod_ts, - zohocrm: zohocrm_zod_ts, - zoomvideo: zoomvideo_zod_ts, -}; +"alby": alby_zod_ts, +"basecamp3": basecamp3_zod_ts, +"btcpayserver": btcpayserver_zod_ts, +"closecom": closecom_zod_ts, +"dailyvideo": dailyvideo_zod_ts, +"databuddy": databuddy_zod_ts, +"dub": dub_zod_ts, +"fathom": fathom_zod_ts, +"feishucalendar": feishucalendar_zod_ts, +"ga4": ga4_zod_ts, +"giphy": giphy_zod_ts, +"googlecalendar": googlecalendar_zod_ts, +"googlevideo": googlevideo_zod_ts, +"gtm": gtm_zod_ts, +"hitpay": hitpay_zod_ts, +"hubspot": hubspot_zod_ts, +"insihts": insihts_zod_ts, +"intercom": intercom_zod_ts, +"jelly": jelly_zod_ts, +"jitsivideo": jitsivideo_zod_ts, +"larkcalendar": larkcalendar_zod_ts, +"lyra": lyra_zod_ts, +"make": make_zod_ts, +"matomo": matomo_zod_ts, +"metapixel": metapixel_zod_ts, +"mock-payment-app": mock_payment_app_zod_ts, +"nextcloudtalk": nextcloudtalk_zod_ts, +"office365calendar": office365calendar_zod_ts, +"office365video": office365video_zod_ts, +"paypal": paypal_zod_ts, +"paystack": paystack_zod_ts, +"pipedrive-crm": pipedrive_crm_zod_ts, +"plausible": plausible_zod_ts, +"posthog": posthog_zod_ts, +"qr_code": qr_code_zod_ts, +"routing-forms": routing_forms_zod_ts, +"salesforce": salesforce_zod_ts, +"shimmervideo": shimmervideo_zod_ts, +"stripe": stripepayment_zod_ts, +"tandemvideo": tandemvideo_zod_ts, +"booking-pages-tag": booking_pages_tag_zod_ts, +"event-type-app-card": event_type_app_card_zod_ts, +"twipla": twipla_zod_ts, +"umami": umami_zod_ts, +"vital": vital_zod_ts, +"webex": webex_zod_ts, +"wordpress": wordpress_zod_ts, +"zapier": zapier_zod_ts, +"zoho-bigin": zoho_bigin_zod_ts, +"zohocalendar": zohocalendar_zod_ts, +"zohocrm": zohocrm_zod_ts, +"zoomvideo": zoomvideo_zod_ts, +}; \ No newline at end of file diff --git a/packages/app-store/apps.metadata.generated.ts b/packages/app-store/apps.metadata.generated.ts index f964d1780504f3..74edbcbe5a5c9c 100644 --- a/packages/app-store/apps.metadata.generated.ts +++ b/packages/app-store/apps.metadata.generated.ts @@ -2,229 +2,231 @@ This file is autogenerated using the command `yarn app-store:build --watch`. Don't modify this file manually. **/ -import alby_config_json from "./alby/config.json"; -import amie_config_json from "./amie/config.json"; -import { metadata as applecalendar__metadata_ts } from "./applecalendar/_metadata"; -import attio_config_json from "./attio/config.json"; -import autocheckin_config_json from "./autocheckin/config.json"; -import baa_for_hipaa_config_json from "./baa-for-hipaa/config.json"; -import basecamp3_config_json from "./basecamp3/config.json"; -import bolna_config_json from "./bolna/config.json"; -import btcpayserver_config_json from "./btcpayserver/config.json"; -import { metadata as caldavcalendar__metadata_ts } from "./caldavcalendar/_metadata"; -import campfire_config_json from "./campfire/config.json"; -import caretta_config_json from "./caretta/config.json"; -import chatbase_config_json from "./chatbase/config.json"; -import clic_config_json from "./clic/config.json"; -import closecom_config_json from "./closecom/config.json"; -import cron_config_json from "./cron/config.json"; -import { metadata as dailyvideo__metadata_ts } from "./dailyvideo/_metadata"; -import databuddy_config_json from "./databuddy/config.json"; -import deel_config_json from "./deel/config.json"; -import demodesk_config_json from "./demodesk/config.json"; -import dialpad_config_json from "./dialpad/config.json"; -import discord_config_json from "./discord/config.json"; -import dub_config_json from "./dub/config.json"; -import eightxeight_config_json from "./eightxeight/config.json"; -import element_call_config_json from "./element-call/config.json"; -import elevenlabs_config_json from "./elevenlabs/config.json"; -import { metadata as exchange2013calendar__metadata_ts } from "./exchange2013calendar/_metadata"; -import { metadata as exchange2016calendar__metadata_ts } from "./exchange2016calendar/_metadata"; -import exchangecalendar_config_json from "./exchangecalendar/config.json"; -import facetime_config_json from "./facetime/config.json"; -import famulor_config_json from "./famulor/config.json"; -import fathom_config_json from "./fathom/config.json"; -import { metadata as feishucalendar__metadata_ts } from "./feishucalendar/_metadata"; -import fonio_ai_config_json from "./fonio-ai/config.json"; -import framer_config_json from "./framer/config.json"; -import ga4_config_json from "./ga4/config.json"; -import { metadata as giphy__metadata_ts } from "./giphy/_metadata"; -import { metadata as googlecalendar__metadata_ts } from "./googlecalendar/_metadata"; -import { metadata as googlevideo__metadata_ts } from "./googlevideo/_metadata"; -import granola_config_json from "./granola/config.json"; -import greetmate_ai_config_json from "./greetmate-ai/config.json"; -import gtm_config_json from "./gtm/config.json"; -import hitpay_config_json from "./hitpay/config.json"; -import horizon_workrooms_config_json from "./horizon-workrooms/config.json"; -import { metadata as hubspot__metadata_ts } from "./hubspot/_metadata"; -import { metadata as huddle01video__metadata_ts } from "./huddle01video/_metadata"; -import ics_feedcalendar_config_json from "./ics-feedcalendar/config.json"; -import insihts_config_json from "./insihts/config.json"; -import intercom_config_json from "./intercom/config.json"; -import jelly_config_json from "./jelly/config.json"; -import { metadata as jitsivideo__metadata_ts } from "./jitsivideo/_metadata"; -import { metadata as larkcalendar__metadata_ts } from "./larkcalendar/_metadata"; -import lindy_config_json from "./lindy/config.json"; -import linear_config_json from "./linear/config.json"; -import lyra_config_json from "./lyra/config.json"; -import make_config_json from "./make/config.json"; -import matomo_config_json from "./matomo/config.json"; -import metapixel_config_json from "./metapixel/config.json"; -import millis_ai_config_json from "./millis-ai/config.json"; -import mirotalk_config_json from "./mirotalk/config.json"; -import mock_payment_app_config_json from "./mock-payment-app/config.json"; -import monobot_config_json from "./monobot/config.json"; -import n8n_config_json from "./n8n/config.json"; -import nextcloudtalk_config_json from "./nextcloudtalk/config.json"; -import { metadata as office365calendar__metadata_ts } from "./office365calendar/_metadata"; -import office365video_config_json from "./office365video/config.json"; -import paypal_config_json from "./paypal/config.json"; -import ping_config_json from "./ping/config.json"; -import pipedream_config_json from "./pipedream/config.json"; -import pipedrive_crm_config_json from "./pipedrive-crm/config.json"; -import plausible_config_json from "./plausible/config.json"; -import posthog_config_json from "./posthog/config.json"; -import qr_code_config_json from "./qr_code/config.json"; -import raycast_config_json from "./raycast/config.json"; -import retell_ai_config_json from "./retell-ai/config.json"; -import riverside_config_json from "./riverside/config.json"; -import roam_config_json from "./roam/config.json"; -import routing_forms_config_json from "./routing-forms/config.json"; -import salesforce_config_json from "./salesforce/config.json"; -import salesroom_config_json from "./salesroom/config.json"; -import sendgrid_config_json from "./sendgrid/config.json"; -import shimmervideo_config_json from "./shimmervideo/config.json"; -import signal_config_json from "./signal/config.json"; -import sirius_video_config_json from "./sirius_video/config.json"; -import skype_config_json from "./skype/config.json"; -import { metadata as stripepayment__metadata_ts } from "./stripepayment/_metadata"; -import sylapsvideo_config_json from "./sylapsvideo/config.json"; -import synthflow_config_json from "./synthflow/config.json"; -import { metadata as tandemvideo__metadata_ts } from "./tandemvideo/_metadata"; -import telegram_config_json from "./telegram/config.json"; -import telli_config_json from "./telli/config.json"; -import basic_config_json from "./templates/basic/config.json"; -import booking_pages_tag_config_json from "./templates/booking-pages-tag/config.json"; -import event_type_app_card_config_json from "./templates/event-type-app-card/config.json"; -import event_type_location_video_static_config_json from "./templates/event-type-location-video-static/config.json"; -import general_app_settings_config_json from "./templates/general-app-settings/config.json"; -import link_as_an_app_config_json from "./templates/link-as-an-app/config.json"; -import twipla_config_json from "./twipla/config.json"; -import umami_config_json from "./umami/config.json"; -import vimcal_config_json from "./vimcal/config.json"; -import { metadata as vital__metadata_ts } from "./vital/_metadata"; -import weather_in_your_calendar_config_json from "./weather_in_your_calendar/config.json"; -import webex_config_json from "./webex/config.json"; -import whatsapp_config_json from "./whatsapp/config.json"; -import whereby_config_json from "./whereby/config.json"; -import { metadata as wipemycalother__metadata_ts } from "./wipemycalother/_metadata"; -import wordpress_config_json from "./wordpress/config.json"; -import zapier_config_json from "./zapier/config.json"; -import zoho_bigin_config_json from "./zoho-bigin/config.json"; -import zohocalendar_config_json from "./zohocalendar/config.json"; -import zohocrm_config_json from "./zohocrm/config.json"; -import { metadata as zoomvideo__metadata_ts } from "./zoomvideo/_metadata"; +import alby_config_json from "./alby/config.json" +import amie_config_json from "./amie/config.json" +import { metadata as applecalendar__metadata_ts } from "./applecalendar/_metadata" +import attio_config_json from "./attio/config.json" +import autocheckin_config_json from "./autocheckin/config.json" +import baa_for_hipaa_config_json from "./baa-for-hipaa/config.json" +import basecamp3_config_json from "./basecamp3/config.json" +import bolna_config_json from "./bolna/config.json" +import btcpayserver_config_json from "./btcpayserver/config.json" +import { metadata as caldavcalendar__metadata_ts } from "./caldavcalendar/_metadata" +import campfire_config_json from "./campfire/config.json" +import caretta_config_json from "./caretta/config.json" +import chatbase_config_json from "./chatbase/config.json" +import clic_config_json from "./clic/config.json" +import closecom_config_json from "./closecom/config.json" +import cron_config_json from "./cron/config.json" +import { metadata as dailyvideo__metadata_ts } from "./dailyvideo/_metadata" +import databuddy_config_json from "./databuddy/config.json" +import deel_config_json from "./deel/config.json" +import demodesk_config_json from "./demodesk/config.json" +import dialpad_config_json from "./dialpad/config.json" +import discord_config_json from "./discord/config.json" +import dub_config_json from "./dub/config.json" +import eightxeight_config_json from "./eightxeight/config.json" +import element_call_config_json from "./element-call/config.json" +import elevenlabs_config_json from "./elevenlabs/config.json" +import { metadata as exchange2013calendar__metadata_ts } from "./exchange2013calendar/_metadata" +import { metadata as exchange2016calendar__metadata_ts } from "./exchange2016calendar/_metadata" +import exchangecalendar_config_json from "./exchangecalendar/config.json" +import facetime_config_json from "./facetime/config.json" +import famulor_config_json from "./famulor/config.json" +import fathom_config_json from "./fathom/config.json" +import { metadata as feishucalendar__metadata_ts } from "./feishucalendar/_metadata" +import fonio_ai_config_json from "./fonio-ai/config.json" +import framer_config_json from "./framer/config.json" +import ga4_config_json from "./ga4/config.json" +import { metadata as giphy__metadata_ts } from "./giphy/_metadata" +import { metadata as googlecalendar__metadata_ts } from "./googlecalendar/_metadata" +import { metadata as googlevideo__metadata_ts } from "./googlevideo/_metadata" +import granola_config_json from "./granola/config.json" +import greetmate_ai_config_json from "./greetmate-ai/config.json" +import gtm_config_json from "./gtm/config.json" +import hitpay_config_json from "./hitpay/config.json" +import horizon_workrooms_config_json from "./horizon-workrooms/config.json" +import { metadata as hubspot__metadata_ts } from "./hubspot/_metadata" +import { metadata as huddle01video__metadata_ts } from "./huddle01video/_metadata" +import ics_feedcalendar_config_json from "./ics-feedcalendar/config.json" +import insihts_config_json from "./insihts/config.json" +import intercom_config_json from "./intercom/config.json" +import jelly_config_json from "./jelly/config.json" +import { metadata as jitsivideo__metadata_ts } from "./jitsivideo/_metadata" +import { metadata as larkcalendar__metadata_ts } from "./larkcalendar/_metadata" +import lindy_config_json from "./lindy/config.json" +import linear_config_json from "./linear/config.json" +import lyra_config_json from "./lyra/config.json" +import make_config_json from "./make/config.json" +import matomo_config_json from "./matomo/config.json" +import metapixel_config_json from "./metapixel/config.json" +import millis_ai_config_json from "./millis-ai/config.json" +import mirotalk_config_json from "./mirotalk/config.json" +import mock_payment_app_config_json from "./mock-payment-app/config.json" +import monobot_config_json from "./monobot/config.json" +import n8n_config_json from "./n8n/config.json" +import nextcloudtalk_config_json from "./nextcloudtalk/config.json" +import { metadata as office365calendar__metadata_ts } from "./office365calendar/_metadata" +import office365video_config_json from "./office365video/config.json" +import paypal_config_json from "./paypal/config.json" +import paystack_config_json from "./paystack/config.json" +import ping_config_json from "./ping/config.json" +import pipedream_config_json from "./pipedream/config.json" +import pipedrive_crm_config_json from "./pipedrive-crm/config.json" +import plausible_config_json from "./plausible/config.json" +import posthog_config_json from "./posthog/config.json" +import qr_code_config_json from "./qr_code/config.json" +import raycast_config_json from "./raycast/config.json" +import retell_ai_config_json from "./retell-ai/config.json" +import riverside_config_json from "./riverside/config.json" +import roam_config_json from "./roam/config.json" +import routing_forms_config_json from "./routing-forms/config.json" +import salesforce_config_json from "./salesforce/config.json" +import salesroom_config_json from "./salesroom/config.json" +import sendgrid_config_json from "./sendgrid/config.json" +import shimmervideo_config_json from "./shimmervideo/config.json" +import signal_config_json from "./signal/config.json" +import sirius_video_config_json from "./sirius_video/config.json" +import skype_config_json from "./skype/config.json" +import { metadata as stripepayment__metadata_ts } from "./stripepayment/_metadata" +import sylapsvideo_config_json from "./sylapsvideo/config.json" +import synthflow_config_json from "./synthflow/config.json" +import { metadata as tandemvideo__metadata_ts } from "./tandemvideo/_metadata" +import telegram_config_json from "./telegram/config.json" +import telli_config_json from "./telli/config.json" +import basic_config_json from "./templates/basic/config.json" +import booking_pages_tag_config_json from "./templates/booking-pages-tag/config.json" +import event_type_app_card_config_json from "./templates/event-type-app-card/config.json" +import event_type_location_video_static_config_json from "./templates/event-type-location-video-static/config.json" +import general_app_settings_config_json from "./templates/general-app-settings/config.json" +import link_as_an_app_config_json from "./templates/link-as-an-app/config.json" +import twipla_config_json from "./twipla/config.json" +import umami_config_json from "./umami/config.json" +import vimcal_config_json from "./vimcal/config.json" +import { metadata as vital__metadata_ts } from "./vital/_metadata" +import weather_in_your_calendar_config_json from "./weather_in_your_calendar/config.json" +import webex_config_json from "./webex/config.json" +import whatsapp_config_json from "./whatsapp/config.json" +import whereby_config_json from "./whereby/config.json" +import { metadata as wipemycalother__metadata_ts } from "./wipemycalother/_metadata" +import wordpress_config_json from "./wordpress/config.json" +import zapier_config_json from "./zapier/config.json" +import zoho_bigin_config_json from "./zoho-bigin/config.json" +import zohocalendar_config_json from "./zohocalendar/config.json" +import zohocrm_config_json from "./zohocrm/config.json" +import { metadata as zoomvideo__metadata_ts } from "./zoomvideo/_metadata" export const appStoreMetadata = { - alby: alby_config_json, - amie: amie_config_json, - applecalendar: applecalendar__metadata_ts, - attio: attio_config_json, - autocheckin: autocheckin_config_json, - "baa-for-hipaa": baa_for_hipaa_config_json, - basecamp3: basecamp3_config_json, - bolna: bolna_config_json, - btcpayserver: btcpayserver_config_json, - caldavcalendar: caldavcalendar__metadata_ts, - campfire: campfire_config_json, - caretta: caretta_config_json, - chatbase: chatbase_config_json, - clic: clic_config_json, - closecom: closecom_config_json, - cron: cron_config_json, - dailyvideo: dailyvideo__metadata_ts, - databuddy: databuddy_config_json, - deel: deel_config_json, - demodesk: demodesk_config_json, - dialpad: dialpad_config_json, - discord: discord_config_json, - dub: dub_config_json, - eightxeight: eightxeight_config_json, - "element-call": element_call_config_json, - elevenlabs: elevenlabs_config_json, - exchange2013calendar: exchange2013calendar__metadata_ts, - exchange2016calendar: exchange2016calendar__metadata_ts, - exchangecalendar: exchangecalendar_config_json, - facetime: facetime_config_json, - famulor: famulor_config_json, - fathom: fathom_config_json, - feishucalendar: feishucalendar__metadata_ts, - "fonio-ai": fonio_ai_config_json, - framer: framer_config_json, - ga4: ga4_config_json, - giphy: giphy__metadata_ts, - googlecalendar: googlecalendar__metadata_ts, - googlevideo: googlevideo__metadata_ts, - granola: granola_config_json, - "greetmate-ai": greetmate_ai_config_json, - gtm: gtm_config_json, - hitpay: hitpay_config_json, - "horizon-workrooms": horizon_workrooms_config_json, - hubspot: hubspot__metadata_ts, - huddle01video: huddle01video__metadata_ts, - "ics-feedcalendar": ics_feedcalendar_config_json, - insihts: insihts_config_json, - intercom: intercom_config_json, - jelly: jelly_config_json, - jitsivideo: jitsivideo__metadata_ts, - larkcalendar: larkcalendar__metadata_ts, - lindy: lindy_config_json, - linear: linear_config_json, - lyra: lyra_config_json, - make: make_config_json, - matomo: matomo_config_json, - metapixel: metapixel_config_json, - "millis-ai": millis_ai_config_json, - mirotalk: mirotalk_config_json, - "mock-payment-app": mock_payment_app_config_json, - monobot: monobot_config_json, - n8n: n8n_config_json, - nextcloudtalk: nextcloudtalk_config_json, - office365calendar: office365calendar__metadata_ts, - office365video: office365video_config_json, - paypal: paypal_config_json, - ping: ping_config_json, - pipedream: pipedream_config_json, - "pipedrive-crm": pipedrive_crm_config_json, - plausible: plausible_config_json, - posthog: posthog_config_json, - qr_code: qr_code_config_json, - raycast: raycast_config_json, - "retell-ai": retell_ai_config_json, - riverside: riverside_config_json, - roam: roam_config_json, - "routing-forms": routing_forms_config_json, - salesforce: salesforce_config_json, - salesroom: salesroom_config_json, - sendgrid: sendgrid_config_json, - shimmervideo: shimmervideo_config_json, - signal: signal_config_json, - sirius_video: sirius_video_config_json, - skype: skype_config_json, - stripepayment: stripepayment__metadata_ts, - sylapsvideo: sylapsvideo_config_json, - synthflow: synthflow_config_json, - tandemvideo: tandemvideo__metadata_ts, - telegram: telegram_config_json, - telli: telli_config_json, - basic: basic_config_json, - "booking-pages-tag": booking_pages_tag_config_json, - "event-type-app-card": event_type_app_card_config_json, - "event-type-location-video-static": event_type_location_video_static_config_json, - "general-app-settings": general_app_settings_config_json, - "link-as-an-app": link_as_an_app_config_json, - twipla: twipla_config_json, - umami: umami_config_json, - vimcal: vimcal_config_json, - vital: vital__metadata_ts, - weather_in_your_calendar: weather_in_your_calendar_config_json, - webex: webex_config_json, - whatsapp: whatsapp_config_json, - whereby: whereby_config_json, - wipemycalother: wipemycalother__metadata_ts, - wordpress: wordpress_config_json, - zapier: zapier_config_json, - "zoho-bigin": zoho_bigin_config_json, - zohocalendar: zohocalendar_config_json, - zohocrm: zohocrm_config_json, - zoomvideo: zoomvideo__metadata_ts, -}; +"alby": alby_config_json, +"amie": amie_config_json, +"applecalendar": applecalendar__metadata_ts, +"attio": attio_config_json, +"autocheckin": autocheckin_config_json, +"baa-for-hipaa": baa_for_hipaa_config_json, +"basecamp3": basecamp3_config_json, +"bolna": bolna_config_json, +"btcpayserver": btcpayserver_config_json, +"caldavcalendar": caldavcalendar__metadata_ts, +"campfire": campfire_config_json, +"caretta": caretta_config_json, +"chatbase": chatbase_config_json, +"clic": clic_config_json, +"closecom": closecom_config_json, +"cron": cron_config_json, +"dailyvideo": dailyvideo__metadata_ts, +"databuddy": databuddy_config_json, +"deel": deel_config_json, +"demodesk": demodesk_config_json, +"dialpad": dialpad_config_json, +"discord": discord_config_json, +"dub": dub_config_json, +"eightxeight": eightxeight_config_json, +"element-call": element_call_config_json, +"elevenlabs": elevenlabs_config_json, +"exchange2013calendar": exchange2013calendar__metadata_ts, +"exchange2016calendar": exchange2016calendar__metadata_ts, +"exchangecalendar": exchangecalendar_config_json, +"facetime": facetime_config_json, +"famulor": famulor_config_json, +"fathom": fathom_config_json, +"feishucalendar": feishucalendar__metadata_ts, +"fonio-ai": fonio_ai_config_json, +"framer": framer_config_json, +"ga4": ga4_config_json, +"giphy": giphy__metadata_ts, +"googlecalendar": googlecalendar__metadata_ts, +"googlevideo": googlevideo__metadata_ts, +"granola": granola_config_json, +"greetmate-ai": greetmate_ai_config_json, +"gtm": gtm_config_json, +"hitpay": hitpay_config_json, +"horizon-workrooms": horizon_workrooms_config_json, +"hubspot": hubspot__metadata_ts, +"huddle01video": huddle01video__metadata_ts, +"ics-feedcalendar": ics_feedcalendar_config_json, +"insihts": insihts_config_json, +"intercom": intercom_config_json, +"jelly": jelly_config_json, +"jitsivideo": jitsivideo__metadata_ts, +"larkcalendar": larkcalendar__metadata_ts, +"lindy": lindy_config_json, +"linear": linear_config_json, +"lyra": lyra_config_json, +"make": make_config_json, +"matomo": matomo_config_json, +"metapixel": metapixel_config_json, +"millis-ai": millis_ai_config_json, +"mirotalk": mirotalk_config_json, +"mock-payment-app": mock_payment_app_config_json, +"monobot": monobot_config_json, +"n8n": n8n_config_json, +"nextcloudtalk": nextcloudtalk_config_json, +"office365calendar": office365calendar__metadata_ts, +"office365video": office365video_config_json, +"paypal": paypal_config_json, +"paystack": paystack_config_json, +"ping": ping_config_json, +"pipedream": pipedream_config_json, +"pipedrive-crm": pipedrive_crm_config_json, +"plausible": plausible_config_json, +"posthog": posthog_config_json, +"qr_code": qr_code_config_json, +"raycast": raycast_config_json, +"retell-ai": retell_ai_config_json, +"riverside": riverside_config_json, +"roam": roam_config_json, +"routing-forms": routing_forms_config_json, +"salesforce": salesforce_config_json, +"salesroom": salesroom_config_json, +"sendgrid": sendgrid_config_json, +"shimmervideo": shimmervideo_config_json, +"signal": signal_config_json, +"sirius_video": sirius_video_config_json, +"skype": skype_config_json, +"stripepayment": stripepayment__metadata_ts, +"sylapsvideo": sylapsvideo_config_json, +"synthflow": synthflow_config_json, +"tandemvideo": tandemvideo__metadata_ts, +"telegram": telegram_config_json, +"telli": telli_config_json, +"basic": basic_config_json, +"booking-pages-tag": booking_pages_tag_config_json, +"event-type-app-card": event_type_app_card_config_json, +"event-type-location-video-static": event_type_location_video_static_config_json, +"general-app-settings": general_app_settings_config_json, +"link-as-an-app": link_as_an_app_config_json, +"twipla": twipla_config_json, +"umami": umami_config_json, +"vimcal": vimcal_config_json, +"vital": vital__metadata_ts, +"weather_in_your_calendar": weather_in_your_calendar_config_json, +"webex": webex_config_json, +"whatsapp": whatsapp_config_json, +"whereby": whereby_config_json, +"wipemycalother": wipemycalother__metadata_ts, +"wordpress": wordpress_config_json, +"zapier": zapier_config_json, +"zoho-bigin": zoho_bigin_config_json, +"zohocalendar": zohocalendar_config_json, +"zohocrm": zohocrm_config_json, +"zoomvideo": zoomvideo__metadata_ts, +}; \ No newline at end of file diff --git a/packages/app-store/apps.schemas.generated.ts b/packages/app-store/apps.schemas.generated.ts index f4017ac5881a7d..9a136f7fe7ea12 100644 --- a/packages/app-store/apps.schemas.generated.ts +++ b/packages/app-store/apps.schemas.generated.ts @@ -2,107 +2,109 @@ This file is autogenerated using the command `yarn app-store:build --watch`. Don't modify this file manually. **/ -import { appDataSchema as alby_zod_ts } from "./alby/zod"; -import { appDataSchema as basecamp3_zod_ts } from "./basecamp3/zod"; -import { appDataSchema as btcpayserver_zod_ts } from "./btcpayserver/zod"; -import { appDataSchema as closecom_zod_ts } from "./closecom/zod"; -import { appDataSchema as dailyvideo_zod_ts } from "./dailyvideo/zod"; -import { appDataSchema as databuddy_zod_ts } from "./databuddy/zod"; -import { appDataSchema as dub_zod_ts } from "./dub/zod"; -import { appDataSchema as fathom_zod_ts } from "./fathom/zod"; -import { appDataSchema as feishucalendar_zod_ts } from "./feishucalendar/zod"; -import { appDataSchema as ga4_zod_ts } from "./ga4/zod"; -import { appDataSchema as giphy_zod_ts } from "./giphy/zod"; -import { appDataSchema as googlecalendar_zod_ts } from "./googlecalendar/zod"; -import { appDataSchema as googlevideo_zod_ts } from "./googlevideo/zod"; -import { appDataSchema as gtm_zod_ts } from "./gtm/zod"; -import { appDataSchema as hitpay_zod_ts } from "./hitpay/zod"; -import { appDataSchema as hubspot_zod_ts } from "./hubspot/zod"; -import { appDataSchema as insihts_zod_ts } from "./insihts/zod"; -import { appDataSchema as intercom_zod_ts } from "./intercom/zod"; -import { appDataSchema as jelly_zod_ts } from "./jelly/zod"; -import { appDataSchema as jitsivideo_zod_ts } from "./jitsivideo/zod"; -import { appDataSchema as larkcalendar_zod_ts } from "./larkcalendar/zod"; -import { appDataSchema as lyra_zod_ts } from "./lyra/zod"; -import { appDataSchema as make_zod_ts } from "./make/zod"; -import { appDataSchema as matomo_zod_ts } from "./matomo/zod"; -import { appDataSchema as metapixel_zod_ts } from "./metapixel/zod"; -import { appDataSchema as mock_payment_app_zod_ts } from "./mock-payment-app/zod"; -import { appDataSchema as nextcloudtalk_zod_ts } from "./nextcloudtalk/zod"; -import { appDataSchema as office365calendar_zod_ts } from "./office365calendar/zod"; -import { appDataSchema as office365video_zod_ts } from "./office365video/zod"; -import { appDataSchema as paypal_zod_ts } from "./paypal/zod"; -import { appDataSchema as pipedrive_crm_zod_ts } from "./pipedrive-crm/zod"; -import { appDataSchema as plausible_zod_ts } from "./plausible/zod"; -import { appDataSchema as posthog_zod_ts } from "./posthog/zod"; -import { appDataSchema as qr_code_zod_ts } from "./qr_code/zod"; -import { appDataSchema as routing_forms_zod_ts } from "./routing-forms/zod"; -import { appDataSchema as salesforce_zod_ts } from "./salesforce/zod"; -import { appDataSchema as shimmervideo_zod_ts } from "./shimmervideo/zod"; -import { appDataSchema as stripepayment_zod_ts } from "./stripepayment/zod"; -import { appDataSchema as tandemvideo_zod_ts } from "./tandemvideo/zod"; -import { appDataSchema as booking_pages_tag_zod_ts } from "./templates/booking-pages-tag/zod"; -import { appDataSchema as event_type_app_card_zod_ts } from "./templates/event-type-app-card/zod"; -import { appDataSchema as twipla_zod_ts } from "./twipla/zod"; -import { appDataSchema as umami_zod_ts } from "./umami/zod"; -import { appDataSchema as vital_zod_ts } from "./vital/zod"; -import { appDataSchema as webex_zod_ts } from "./webex/zod"; -import { appDataSchema as wordpress_zod_ts } from "./wordpress/zod"; -import { appDataSchema as zapier_zod_ts } from "./zapier/zod"; -import { appDataSchema as zoho_bigin_zod_ts } from "./zoho-bigin/zod"; -import { appDataSchema as zohocalendar_zod_ts } from "./zohocalendar/zod"; -import { appDataSchema as zohocrm_zod_ts } from "./zohocrm/zod"; -import { appDataSchema as zoomvideo_zod_ts } from "./zoomvideo/zod"; +import { appDataSchema as alby_zod_ts } from "./alby/zod" +import { appDataSchema as basecamp3_zod_ts } from "./basecamp3/zod" +import { appDataSchema as btcpayserver_zod_ts } from "./btcpayserver/zod" +import { appDataSchema as closecom_zod_ts } from "./closecom/zod" +import { appDataSchema as dailyvideo_zod_ts } from "./dailyvideo/zod" +import { appDataSchema as databuddy_zod_ts } from "./databuddy/zod" +import { appDataSchema as dub_zod_ts } from "./dub/zod" +import { appDataSchema as fathom_zod_ts } from "./fathom/zod" +import { appDataSchema as feishucalendar_zod_ts } from "./feishucalendar/zod" +import { appDataSchema as ga4_zod_ts } from "./ga4/zod" +import { appDataSchema as giphy_zod_ts } from "./giphy/zod" +import { appDataSchema as googlecalendar_zod_ts } from "./googlecalendar/zod" +import { appDataSchema as googlevideo_zod_ts } from "./googlevideo/zod" +import { appDataSchema as gtm_zod_ts } from "./gtm/zod" +import { appDataSchema as hitpay_zod_ts } from "./hitpay/zod" +import { appDataSchema as hubspot_zod_ts } from "./hubspot/zod" +import { appDataSchema as insihts_zod_ts } from "./insihts/zod" +import { appDataSchema as intercom_zod_ts } from "./intercom/zod" +import { appDataSchema as jelly_zod_ts } from "./jelly/zod" +import { appDataSchema as jitsivideo_zod_ts } from "./jitsivideo/zod" +import { appDataSchema as larkcalendar_zod_ts } from "./larkcalendar/zod" +import { appDataSchema as lyra_zod_ts } from "./lyra/zod" +import { appDataSchema as make_zod_ts } from "./make/zod" +import { appDataSchema as matomo_zod_ts } from "./matomo/zod" +import { appDataSchema as metapixel_zod_ts } from "./metapixel/zod" +import { appDataSchema as mock_payment_app_zod_ts } from "./mock-payment-app/zod" +import { appDataSchema as nextcloudtalk_zod_ts } from "./nextcloudtalk/zod" +import { appDataSchema as office365calendar_zod_ts } from "./office365calendar/zod" +import { appDataSchema as office365video_zod_ts } from "./office365video/zod" +import { appDataSchema as paypal_zod_ts } from "./paypal/zod" +import { appDataSchema as paystack_zod_ts } from "./paystack/zod" +import { appDataSchema as pipedrive_crm_zod_ts } from "./pipedrive-crm/zod" +import { appDataSchema as plausible_zod_ts } from "./plausible/zod" +import { appDataSchema as posthog_zod_ts } from "./posthog/zod" +import { appDataSchema as qr_code_zod_ts } from "./qr_code/zod" +import { appDataSchema as routing_forms_zod_ts } from "./routing-forms/zod" +import { appDataSchema as salesforce_zod_ts } from "./salesforce/zod" +import { appDataSchema as shimmervideo_zod_ts } from "./shimmervideo/zod" +import { appDataSchema as stripepayment_zod_ts } from "./stripepayment/zod" +import { appDataSchema as tandemvideo_zod_ts } from "./tandemvideo/zod" +import { appDataSchema as booking_pages_tag_zod_ts } from "./templates/booking-pages-tag/zod" +import { appDataSchema as event_type_app_card_zod_ts } from "./templates/event-type-app-card/zod" +import { appDataSchema as twipla_zod_ts } from "./twipla/zod" +import { appDataSchema as umami_zod_ts } from "./umami/zod" +import { appDataSchema as vital_zod_ts } from "./vital/zod" +import { appDataSchema as webex_zod_ts } from "./webex/zod" +import { appDataSchema as wordpress_zod_ts } from "./wordpress/zod" +import { appDataSchema as zapier_zod_ts } from "./zapier/zod" +import { appDataSchema as zoho_bigin_zod_ts } from "./zoho-bigin/zod" +import { appDataSchema as zohocalendar_zod_ts } from "./zohocalendar/zod" +import { appDataSchema as zohocrm_zod_ts } from "./zohocrm/zod" +import { appDataSchema as zoomvideo_zod_ts } from "./zoomvideo/zod" export const appDataSchemas = { - alby: alby_zod_ts, - basecamp3: basecamp3_zod_ts, - btcpayserver: btcpayserver_zod_ts, - closecom: closecom_zod_ts, - dailyvideo: dailyvideo_zod_ts, - databuddy: databuddy_zod_ts, - dub: dub_zod_ts, - fathom: fathom_zod_ts, - feishucalendar: feishucalendar_zod_ts, - ga4: ga4_zod_ts, - giphy: giphy_zod_ts, - googlecalendar: googlecalendar_zod_ts, - googlevideo: googlevideo_zod_ts, - gtm: gtm_zod_ts, - hitpay: hitpay_zod_ts, - hubspot: hubspot_zod_ts, - insihts: insihts_zod_ts, - intercom: intercom_zod_ts, - jelly: jelly_zod_ts, - jitsivideo: jitsivideo_zod_ts, - larkcalendar: larkcalendar_zod_ts, - lyra: lyra_zod_ts, - make: make_zod_ts, - matomo: matomo_zod_ts, - metapixel: metapixel_zod_ts, - "mock-payment-app": mock_payment_app_zod_ts, - nextcloudtalk: nextcloudtalk_zod_ts, - office365calendar: office365calendar_zod_ts, - office365video: office365video_zod_ts, - paypal: paypal_zod_ts, - "pipedrive-crm": pipedrive_crm_zod_ts, - plausible: plausible_zod_ts, - posthog: posthog_zod_ts, - qr_code: qr_code_zod_ts, - "routing-forms": routing_forms_zod_ts, - salesforce: salesforce_zod_ts, - shimmervideo: shimmervideo_zod_ts, - stripe: stripepayment_zod_ts, - tandemvideo: tandemvideo_zod_ts, - "booking-pages-tag": booking_pages_tag_zod_ts, - "event-type-app-card": event_type_app_card_zod_ts, - twipla: twipla_zod_ts, - umami: umami_zod_ts, - vital: vital_zod_ts, - webex: webex_zod_ts, - wordpress: wordpress_zod_ts, - zapier: zapier_zod_ts, - "zoho-bigin": zoho_bigin_zod_ts, - zohocalendar: zohocalendar_zod_ts, - zohocrm: zohocrm_zod_ts, - zoomvideo: zoomvideo_zod_ts, -}; +"alby": alby_zod_ts, +"basecamp3": basecamp3_zod_ts, +"btcpayserver": btcpayserver_zod_ts, +"closecom": closecom_zod_ts, +"dailyvideo": dailyvideo_zod_ts, +"databuddy": databuddy_zod_ts, +"dub": dub_zod_ts, +"fathom": fathom_zod_ts, +"feishucalendar": feishucalendar_zod_ts, +"ga4": ga4_zod_ts, +"giphy": giphy_zod_ts, +"googlecalendar": googlecalendar_zod_ts, +"googlevideo": googlevideo_zod_ts, +"gtm": gtm_zod_ts, +"hitpay": hitpay_zod_ts, +"hubspot": hubspot_zod_ts, +"insihts": insihts_zod_ts, +"intercom": intercom_zod_ts, +"jelly": jelly_zod_ts, +"jitsivideo": jitsivideo_zod_ts, +"larkcalendar": larkcalendar_zod_ts, +"lyra": lyra_zod_ts, +"make": make_zod_ts, +"matomo": matomo_zod_ts, +"metapixel": metapixel_zod_ts, +"mock-payment-app": mock_payment_app_zod_ts, +"nextcloudtalk": nextcloudtalk_zod_ts, +"office365calendar": office365calendar_zod_ts, +"office365video": office365video_zod_ts, +"paypal": paypal_zod_ts, +"paystack": paystack_zod_ts, +"pipedrive-crm": pipedrive_crm_zod_ts, +"plausible": plausible_zod_ts, +"posthog": posthog_zod_ts, +"qr_code": qr_code_zod_ts, +"routing-forms": routing_forms_zod_ts, +"salesforce": salesforce_zod_ts, +"shimmervideo": shimmervideo_zod_ts, +"stripe": stripepayment_zod_ts, +"tandemvideo": tandemvideo_zod_ts, +"booking-pages-tag": booking_pages_tag_zod_ts, +"event-type-app-card": event_type_app_card_zod_ts, +"twipla": twipla_zod_ts, +"umami": umami_zod_ts, +"vital": vital_zod_ts, +"webex": webex_zod_ts, +"wordpress": wordpress_zod_ts, +"zapier": zapier_zod_ts, +"zoho-bigin": zoho_bigin_zod_ts, +"zohocalendar": zohocalendar_zod_ts, +"zohocrm": zohocrm_zod_ts, +"zoomvideo": zoomvideo_zod_ts, +}; \ No newline at end of file diff --git a/packages/app-store/apps.server.generated.ts b/packages/app-store/apps.server.generated.ts index 650bc722dbf6cf..55c69298bb197e 100644 --- a/packages/app-store/apps.server.generated.ts +++ b/packages/app-store/apps.server.generated.ts @@ -3,89 +3,90 @@ Don't modify this file manually. **/ export const apiHandlers = { - alby: import("./alby/api"), - applecalendar: import("./applecalendar/api"), - attio: import("./attio/api"), - basecamp3: import("./basecamp3/api"), - btcpayserver: import("./btcpayserver/api"), - caldavcalendar: import("./caldavcalendar/api"), - campfire: import("./campfire/api"), - closecom: import("./closecom/api"), - databuddy: import("./databuddy/api"), - demodesk: import("./demodesk/api"), - dialpad: import("./dialpad/api"), - discord: import("./discord/api"), - dub: import("./dub/api"), - eightxeight: import("./eightxeight/api"), - "element-call": import("./element-call/api"), - exchange2013calendar: import("./exchange2013calendar/api"), - exchange2016calendar: import("./exchange2016calendar/api"), - exchangecalendar: import("./exchangecalendar/api"), - facetime: import("./facetime/api"), - famulor: import("./famulor/api"), - fathom: import("./fathom/api"), - feishucalendar: import("./feishucalendar/api"), - ga4: import("./ga4/api"), - giphy: import("./giphy/api"), - googlecalendar: import("./googlecalendar/api"), - googlevideo: import("./googlevideo/api"), - gtm: import("./gtm/api"), - hitpay: import("./hitpay/api"), - "horizon-workrooms": import("./horizon-workrooms/api"), - hubspot: import("./hubspot/api"), - huddle01video: import("./huddle01video/api"), - "ics-feedcalendar": import("./ics-feedcalendar/api"), - insihts: import("./insihts/api"), - intercom: import("./intercom/api"), - jelly: import("./jelly/api"), - jitsivideo: import("./jitsivideo/api"), - larkcalendar: import("./larkcalendar/api"), - linear: import("./linear/api"), - lyra: import("./lyra/api"), - make: import("./make/api"), - matomo: import("./matomo/api"), - metapixel: import("./metapixel/api"), - mirotalk: import("./mirotalk/api"), - "mock-payment-app": import("./mock-payment-app/api"), - nextcloudtalk: import("./nextcloudtalk/api"), - office365calendar: import("./office365calendar/api"), - office365video: import("./office365video/api"), - paypal: import("./paypal/api"), - ping: import("./ping/api"), - "pipedrive-crm": import("./pipedrive-crm/api"), - plausible: import("./plausible/api"), - posthog: import("./posthog/api"), - qr_code: import("./qr_code/api"), - riverside: import("./riverside/api"), - roam: import("./roam/api"), - "routing-forms": import("./routing-forms/api"), - salesforce: import("./salesforce/api"), - salesroom: import("./salesroom/api"), - sendgrid: import("./sendgrid/api"), - shimmervideo: import("./shimmervideo/api"), - signal: import("./signal/api"), - sirius_video: import("./sirius_video/api"), - skype: import("./skype/api"), - stripepayment: import("./stripepayment/api"), - sylapsvideo: import("./sylapsvideo/api"), - tandemvideo: import("./tandemvideo/api"), - telegram: import("./telegram/api"), - basic: import("./templates/basic/api"), - "booking-pages-tag": import("./templates/booking-pages-tag/api"), - "event-type-app-card": import("./templates/event-type-app-card/api"), - "event-type-location-video-static": import("./templates/event-type-location-video-static/api"), - "general-app-settings": import("./templates/general-app-settings/api"), - twipla: import("./twipla/api"), - umami: import("./umami/api"), - vital: import("./vital/api"), - weather_in_your_calendar: import("./weather_in_your_calendar/api"), - webex: import("./webex/api"), - whatsapp: import("./whatsapp/api"), - whereby: import("./whereby/api"), - wipemycalother: import("./wipemycalother/api"), - zapier: import("./zapier/api"), - "zoho-bigin": import("./zoho-bigin/api"), - zohocalendar: import("./zohocalendar/api"), - zohocrm: import("./zohocrm/api"), - zoomvideo: import("./zoomvideo/api"), -}; +"alby": import("./alby/api"), +"applecalendar": import("./applecalendar/api"), +"attio": import("./attio/api"), +"basecamp3": import("./basecamp3/api"), +"btcpayserver": import("./btcpayserver/api"), +"caldavcalendar": import("./caldavcalendar/api"), +"campfire": import("./campfire/api"), +"closecom": import("./closecom/api"), +"databuddy": import("./databuddy/api"), +"demodesk": import("./demodesk/api"), +"dialpad": import("./dialpad/api"), +"discord": import("./discord/api"), +"dub": import("./dub/api"), +"eightxeight": import("./eightxeight/api"), +"element-call": import("./element-call/api"), +"exchange2013calendar": import("./exchange2013calendar/api"), +"exchange2016calendar": import("./exchange2016calendar/api"), +"exchangecalendar": import("./exchangecalendar/api"), +"facetime": import("./facetime/api"), +"famulor": import("./famulor/api"), +"fathom": import("./fathom/api"), +"feishucalendar": import("./feishucalendar/api"), +"ga4": import("./ga4/api"), +"giphy": import("./giphy/api"), +"googlecalendar": import("./googlecalendar/api"), +"googlevideo": import("./googlevideo/api"), +"gtm": import("./gtm/api"), +"hitpay": import("./hitpay/api"), +"horizon-workrooms": import("./horizon-workrooms/api"), +"hubspot": import("./hubspot/api"), +"huddle01video": import("./huddle01video/api"), +"ics-feedcalendar": import("./ics-feedcalendar/api"), +"insihts": import("./insihts/api"), +"intercom": import("./intercom/api"), +"jelly": import("./jelly/api"), +"jitsivideo": import("./jitsivideo/api"), +"larkcalendar": import("./larkcalendar/api"), +"linear": import("./linear/api"), +"lyra": import("./lyra/api"), +"make": import("./make/api"), +"matomo": import("./matomo/api"), +"metapixel": import("./metapixel/api"), +"mirotalk": import("./mirotalk/api"), +"mock-payment-app": import("./mock-payment-app/api"), +"nextcloudtalk": import("./nextcloudtalk/api"), +"office365calendar": import("./office365calendar/api"), +"office365video": import("./office365video/api"), +"paypal": import("./paypal/api"), +"paystack": import("./paystack/api"), +"ping": import("./ping/api"), +"pipedrive-crm": import("./pipedrive-crm/api"), +"plausible": import("./plausible/api"), +"posthog": import("./posthog/api"), +"qr_code": import("./qr_code/api"), +"riverside": import("./riverside/api"), +"roam": import("./roam/api"), +"routing-forms": import("./routing-forms/api"), +"salesforce": import("./salesforce/api"), +"salesroom": import("./salesroom/api"), +"sendgrid": import("./sendgrid/api"), +"shimmervideo": import("./shimmervideo/api"), +"signal": import("./signal/api"), +"sirius_video": import("./sirius_video/api"), +"skype": import("./skype/api"), +"stripepayment": import("./stripepayment/api"), +"sylapsvideo": import("./sylapsvideo/api"), +"tandemvideo": import("./tandemvideo/api"), +"telegram": import("./telegram/api"), +"basic": import("./templates/basic/api"), +"booking-pages-tag": import("./templates/booking-pages-tag/api"), +"event-type-app-card": import("./templates/event-type-app-card/api"), +"event-type-location-video-static": import("./templates/event-type-location-video-static/api"), +"general-app-settings": import("./templates/general-app-settings/api"), +"twipla": import("./twipla/api"), +"umami": import("./umami/api"), +"vital": import("./vital/api"), +"weather_in_your_calendar": import("./weather_in_your_calendar/api"), +"webex": import("./webex/api"), +"whatsapp": import("./whatsapp/api"), +"whereby": import("./whereby/api"), +"wipemycalother": import("./wipemycalother/api"), +"zapier": import("./zapier/api"), +"zoho-bigin": import("./zoho-bigin/api"), +"zohocalendar": import("./zohocalendar/api"), +"zohocrm": import("./zohocrm/api"), +"zoomvideo": import("./zoomvideo/api"), +}; \ No newline at end of file diff --git a/packages/app-store/bookerApps.metadata.generated.ts b/packages/app-store/bookerApps.metadata.generated.ts index 16d9e3f77e50d2..2abb260052f19f 100644 --- a/packages/app-store/bookerApps.metadata.generated.ts +++ b/packages/app-store/bookerApps.metadata.generated.ts @@ -2,95 +2,95 @@ This file is autogenerated using the command `yarn app-store:build --watch`. Don't modify this file manually. **/ -import campfire_config_json from "./campfire/config.json"; -import { metadata as dailyvideo__metadata_ts } from "./dailyvideo/_metadata"; -import databuddy_config_json from "./databuddy/config.json"; -import demodesk_config_json from "./demodesk/config.json"; -import dialpad_config_json from "./dialpad/config.json"; -import discord_config_json from "./discord/config.json"; -import eightxeight_config_json from "./eightxeight/config.json"; -import element_call_config_json from "./element-call/config.json"; -import facetime_config_json from "./facetime/config.json"; -import fathom_config_json from "./fathom/config.json"; -import ga4_config_json from "./ga4/config.json"; -import { metadata as googlevideo__metadata_ts } from "./googlevideo/_metadata"; -import gtm_config_json from "./gtm/config.json"; -import horizon_workrooms_config_json from "./horizon-workrooms/config.json"; -import { metadata as huddle01video__metadata_ts } from "./huddle01video/_metadata"; -import insihts_config_json from "./insihts/config.json"; -import jelly_config_json from "./jelly/config.json"; -import { metadata as jitsivideo__metadata_ts } from "./jitsivideo/_metadata"; -import lyra_config_json from "./lyra/config.json"; -import matomo_config_json from "./matomo/config.json"; -import metapixel_config_json from "./metapixel/config.json"; -import mirotalk_config_json from "./mirotalk/config.json"; -import nextcloudtalk_config_json from "./nextcloudtalk/config.json"; -import office365video_config_json from "./office365video/config.json"; -import ping_config_json from "./ping/config.json"; -import plausible_config_json from "./plausible/config.json"; -import posthog_config_json from "./posthog/config.json"; -import riverside_config_json from "./riverside/config.json"; -import roam_config_json from "./roam/config.json"; -import salesroom_config_json from "./salesroom/config.json"; -import shimmervideo_config_json from "./shimmervideo/config.json"; -import signal_config_json from "./signal/config.json"; -import sirius_video_config_json from "./sirius_video/config.json"; -import skype_config_json from "./skype/config.json"; -import sylapsvideo_config_json from "./sylapsvideo/config.json"; -import { metadata as tandemvideo__metadata_ts } from "./tandemvideo/_metadata"; -import telegram_config_json from "./telegram/config.json"; -import booking_pages_tag_config_json from "./templates/booking-pages-tag/config.json"; -import event_type_location_video_static_config_json from "./templates/event-type-location-video-static/config.json"; -import twipla_config_json from "./twipla/config.json"; -import umami_config_json from "./umami/config.json"; -import webex_config_json from "./webex/config.json"; -import whatsapp_config_json from "./whatsapp/config.json"; -import whereby_config_json from "./whereby/config.json"; -import { metadata as zoomvideo__metadata_ts } from "./zoomvideo/_metadata"; +import campfire_config_json from "./campfire/config.json" +import { metadata as dailyvideo__metadata_ts } from "./dailyvideo/_metadata" +import databuddy_config_json from "./databuddy/config.json" +import demodesk_config_json from "./demodesk/config.json" +import dialpad_config_json from "./dialpad/config.json" +import discord_config_json from "./discord/config.json" +import eightxeight_config_json from "./eightxeight/config.json" +import element_call_config_json from "./element-call/config.json" +import facetime_config_json from "./facetime/config.json" +import fathom_config_json from "./fathom/config.json" +import ga4_config_json from "./ga4/config.json" +import { metadata as googlevideo__metadata_ts } from "./googlevideo/_metadata" +import gtm_config_json from "./gtm/config.json" +import horizon_workrooms_config_json from "./horizon-workrooms/config.json" +import { metadata as huddle01video__metadata_ts } from "./huddle01video/_metadata" +import insihts_config_json from "./insihts/config.json" +import jelly_config_json from "./jelly/config.json" +import { metadata as jitsivideo__metadata_ts } from "./jitsivideo/_metadata" +import lyra_config_json from "./lyra/config.json" +import matomo_config_json from "./matomo/config.json" +import metapixel_config_json from "./metapixel/config.json" +import mirotalk_config_json from "./mirotalk/config.json" +import nextcloudtalk_config_json from "./nextcloudtalk/config.json" +import office365video_config_json from "./office365video/config.json" +import ping_config_json from "./ping/config.json" +import plausible_config_json from "./plausible/config.json" +import posthog_config_json from "./posthog/config.json" +import riverside_config_json from "./riverside/config.json" +import roam_config_json from "./roam/config.json" +import salesroom_config_json from "./salesroom/config.json" +import shimmervideo_config_json from "./shimmervideo/config.json" +import signal_config_json from "./signal/config.json" +import sirius_video_config_json from "./sirius_video/config.json" +import skype_config_json from "./skype/config.json" +import sylapsvideo_config_json from "./sylapsvideo/config.json" +import { metadata as tandemvideo__metadata_ts } from "./tandemvideo/_metadata" +import telegram_config_json from "./telegram/config.json" +import booking_pages_tag_config_json from "./templates/booking-pages-tag/config.json" +import event_type_location_video_static_config_json from "./templates/event-type-location-video-static/config.json" +import twipla_config_json from "./twipla/config.json" +import umami_config_json from "./umami/config.json" +import webex_config_json from "./webex/config.json" +import whatsapp_config_json from "./whatsapp/config.json" +import whereby_config_json from "./whereby/config.json" +import { metadata as zoomvideo__metadata_ts } from "./zoomvideo/_metadata" export const appStoreMetadata = { - campfire: campfire_config_json, - dailyvideo: dailyvideo__metadata_ts, - databuddy: databuddy_config_json, - demodesk: demodesk_config_json, - dialpad: dialpad_config_json, - discord: discord_config_json, - eightxeight: eightxeight_config_json, - "element-call": element_call_config_json, - facetime: facetime_config_json, - fathom: fathom_config_json, - ga4: ga4_config_json, - googlevideo: googlevideo__metadata_ts, - gtm: gtm_config_json, - "horizon-workrooms": horizon_workrooms_config_json, - huddle01video: huddle01video__metadata_ts, - insihts: insihts_config_json, - jelly: jelly_config_json, - jitsivideo: jitsivideo__metadata_ts, - lyra: lyra_config_json, - matomo: matomo_config_json, - metapixel: metapixel_config_json, - mirotalk: mirotalk_config_json, - nextcloudtalk: nextcloudtalk_config_json, - office365video: office365video_config_json, - ping: ping_config_json, - plausible: plausible_config_json, - posthog: posthog_config_json, - riverside: riverside_config_json, - roam: roam_config_json, - salesroom: salesroom_config_json, - shimmervideo: shimmervideo_config_json, - signal: signal_config_json, - sirius_video: sirius_video_config_json, - skype: skype_config_json, - sylapsvideo: sylapsvideo_config_json, - tandemvideo: tandemvideo__metadata_ts, - telegram: telegram_config_json, - "booking-pages-tag": booking_pages_tag_config_json, - "event-type-location-video-static": event_type_location_video_static_config_json, - twipla: twipla_config_json, - umami: umami_config_json, - webex: webex_config_json, - whatsapp: whatsapp_config_json, - whereby: whereby_config_json, - zoomvideo: zoomvideo__metadata_ts, -}; +"campfire": campfire_config_json, +"dailyvideo": dailyvideo__metadata_ts, +"databuddy": databuddy_config_json, +"demodesk": demodesk_config_json, +"dialpad": dialpad_config_json, +"discord": discord_config_json, +"eightxeight": eightxeight_config_json, +"element-call": element_call_config_json, +"facetime": facetime_config_json, +"fathom": fathom_config_json, +"ga4": ga4_config_json, +"googlevideo": googlevideo__metadata_ts, +"gtm": gtm_config_json, +"horizon-workrooms": horizon_workrooms_config_json, +"huddle01video": huddle01video__metadata_ts, +"insihts": insihts_config_json, +"jelly": jelly_config_json, +"jitsivideo": jitsivideo__metadata_ts, +"lyra": lyra_config_json, +"matomo": matomo_config_json, +"metapixel": metapixel_config_json, +"mirotalk": mirotalk_config_json, +"nextcloudtalk": nextcloudtalk_config_json, +"office365video": office365video_config_json, +"ping": ping_config_json, +"plausible": plausible_config_json, +"posthog": posthog_config_json, +"riverside": riverside_config_json, +"roam": roam_config_json, +"salesroom": salesroom_config_json, +"shimmervideo": shimmervideo_config_json, +"signal": signal_config_json, +"sirius_video": sirius_video_config_json, +"skype": skype_config_json, +"sylapsvideo": sylapsvideo_config_json, +"tandemvideo": tandemvideo__metadata_ts, +"telegram": telegram_config_json, +"booking-pages-tag": booking_pages_tag_config_json, +"event-type-location-video-static": event_type_location_video_static_config_json, +"twipla": twipla_config_json, +"umami": umami_config_json, +"webex": webex_config_json, +"whatsapp": whatsapp_config_json, +"whereby": whereby_config_json, +"zoomvideo": zoomvideo__metadata_ts, +}; \ No newline at end of file diff --git a/packages/app-store/calendar.services.generated.ts b/packages/app-store/calendar.services.generated.ts index 9795cfe01ea444..4f2bb7de323ea7 100644 --- a/packages/app-store/calendar.services.generated.ts +++ b/packages/app-store/calendar.services.generated.ts @@ -2,19 +2,16 @@ This file is autogenerated using the command `yarn app-store:build --watch`. Don't modify this file manually. **/ -export const CalendarServiceMap = - process.env.NEXT_PUBLIC_IS_E2E === "1" - ? {} - : { - applecalendar: import("./applecalendar/lib/CalendarService"), - caldavcalendar: import("./caldavcalendar/lib/CalendarService"), - exchange2013calendar: import("./exchange2013calendar/lib/CalendarService"), - exchange2016calendar: import("./exchange2016calendar/lib/CalendarService"), - exchangecalendar: import("./exchangecalendar/lib/CalendarService"), - feishucalendar: import("./feishucalendar/lib/CalendarService"), - googlecalendar: import("./googlecalendar/lib/CalendarService"), - "ics-feedcalendar": import("./ics-feedcalendar/lib/CalendarService"), - larkcalendar: import("./larkcalendar/lib/CalendarService"), - office365calendar: import("./office365calendar/lib/CalendarService"), - zohocalendar: import("./zohocalendar/lib/CalendarService"), - }; +export const CalendarServiceMap = process.env.NEXT_PUBLIC_IS_E2E === '1' ? {} : { +"applecalendar": import("./applecalendar/lib/CalendarService"), +"caldavcalendar": import("./caldavcalendar/lib/CalendarService"), +"exchange2013calendar": import("./exchange2013calendar/lib/CalendarService"), +"exchange2016calendar": import("./exchange2016calendar/lib/CalendarService"), +"exchangecalendar": import("./exchangecalendar/lib/CalendarService"), +"feishucalendar": import("./feishucalendar/lib/CalendarService"), +"googlecalendar": import("./googlecalendar/lib/CalendarService"), +"ics-feedcalendar": import("./ics-feedcalendar/lib/CalendarService"), +"larkcalendar": import("./larkcalendar/lib/CalendarService"), +"office365calendar": import("./office365calendar/lib/CalendarService"), +"zohocalendar": import("./zohocalendar/lib/CalendarService"), +}; \ No newline at end of file diff --git a/packages/app-store/crm.apps.generated.ts b/packages/app-store/crm.apps.generated.ts index 94392d5ee50fcd..31e47670b83af2 100644 --- a/packages/app-store/crm.apps.generated.ts +++ b/packages/app-store/crm.apps.generated.ts @@ -3,10 +3,10 @@ Don't modify this file manually. **/ export const CrmServiceMap = { - closecom: import("./closecom/lib/CrmService"), - hubspot: import("./hubspot/lib/CrmService"), - "pipedrive-crm": import("./pipedrive-crm/lib/CrmService"), - salesforce: import("./salesforce/lib/CrmService"), - "zoho-bigin": import("./zoho-bigin/lib/CrmService"), - zohocrm: import("./zohocrm/lib/CrmService"), -}; +"closecom": import("./closecom/lib/CrmService"), +"hubspot": import("./hubspot/lib/CrmService"), +"pipedrive-crm": import("./pipedrive-crm/lib/CrmService"), +"salesforce": import("./salesforce/lib/CrmService"), +"zoho-bigin": import("./zoho-bigin/lib/CrmService"), +"zohocrm": import("./zohocrm/lib/CrmService"), +}; \ No newline at end of file diff --git a/packages/app-store/payment.services.generated.ts b/packages/app-store/payment.services.generated.ts index 2e42d632580810..aeaf08bbe5703d 100644 --- a/packages/app-store/payment.services.generated.ts +++ b/packages/app-store/payment.services.generated.ts @@ -3,10 +3,11 @@ Don't modify this file manually. **/ export const PaymentServiceMap = { - alby: import("./alby/lib/PaymentService"), - btcpayserver: import("./btcpayserver/lib/PaymentService"), - hitpay: import("./hitpay/lib/PaymentService"), - "mock-payment-app": import("./mock-payment-app/lib/PaymentService"), - paypal: import("./paypal/lib/PaymentService"), - stripepayment: import("./stripepayment/lib/PaymentService"), -}; +"alby": import("./alby/lib/PaymentService"), +"btcpayserver": import("./btcpayserver/lib/PaymentService"), +"hitpay": import("./hitpay/lib/PaymentService"), +"mock-payment-app": import("./mock-payment-app/lib/PaymentService"), +"paypal": import("./paypal/lib/PaymentService"), +"paystack": import("./paystack/lib/PaymentService"), +"stripepayment": import("./stripepayment/lib/PaymentService"), +}; \ No newline at end of file diff --git a/packages/app-store/paystack/_metadata.ts b/packages/app-store/paystack/_metadata.ts new file mode 100644 index 00000000000000..d783e59bfb1467 --- /dev/null +++ b/packages/app-store/paystack/_metadata.ts @@ -0,0 +1,22 @@ +import type { AppMeta } from "@calcom/types/App"; + +import _package from "./package.json"; + +export const metadata = { + name: "Paystack", + description: _package.description, + installed: true, + type: "paystack_payment", + variant: "payment", + logo: "icon.svg", + publisher: "Cal.com", + url: "https://paystack.com", + categories: ["payment"], + slug: "paystack", + title: "Paystack", + email: "support@cal.com", + dirName: "paystack", + isOAuth: true, +} as AppMeta; + +export default metadata; diff --git a/packages/app-store/paystack/api/add.ts b/packages/app-store/paystack/api/add.ts new file mode 100644 index 00000000000000..6128bc986d20f8 --- /dev/null +++ b/packages/app-store/paystack/api/add.ts @@ -0,0 +1,53 @@ +import type { NextApiRequest, NextApiResponse } from "next"; + +import { throwIfNotHaveAdminAccessToTeam } from "@calcom/app-store/_utils/throwIfNotHaveAdminAccessToTeam"; +import { getServerErrorFromUnknown } from "@calcom/lib/server/getServerErrorFromUnknown"; +import prisma from "@calcom/prisma"; + +import config from "../config.json"; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (!req.session?.user?.id) { + return res.status(401).json({ message: "You must be logged in to do this" }); + } + + const { teamId } = req.query; + const teamIdNumber = teamId ? Number(teamId) : null; + + if (teamIdNumber !== null && Number.isNaN(teamIdNumber)) { + return res.status(400).json({ message: "Invalid teamId" }); + } + + await throwIfNotHaveAdminAccessToTeam({ teamId: teamIdNumber, userId: req.session.user.id }); + + const appType = config.type; + const ownerFilter = teamIdNumber ? { teamId: teamIdNumber } : { userId: req.session.user.id }; + + try { + const alreadyInstalled = await prisma.credential.findFirst({ + where: { + type: appType, + ...ownerFilter, + }, + }); + if (alreadyInstalled) { + throw new Error("Already installed"); + } + await prisma.credential.create({ + data: { + type: appType, + key: {}, + appId: "paystack", + ...ownerFilter, + }, + }); + } catch (error: unknown) { + if (error instanceof Error && error.message === "Already installed") { + return res.status(409).json({ message: error.message }); + } + const httpError = getServerErrorFromUnknown(error); + return res.status(httpError.statusCode).json({ message: httpError.message }); + } + + return res.status(200).json({ url: `/apps/paystack/setup${teamIdNumber ? `?teamId=${teamIdNumber}` : ""}` }); +} diff --git a/packages/app-store/paystack/api/index.ts b/packages/app-store/paystack/api/index.ts new file mode 100644 index 00000000000000..214855db1a31c9 --- /dev/null +++ b/packages/app-store/paystack/api/index.ts @@ -0,0 +1,3 @@ +export { default as add } from "./add"; +export { default as webhook } from "./webhook"; +export { default as verify } from "./verify"; diff --git a/packages/app-store/paystack/api/verify.ts b/packages/app-store/paystack/api/verify.ts new file mode 100644 index 00000000000000..96eae892ef57c3 --- /dev/null +++ b/packages/app-store/paystack/api/verify.ts @@ -0,0 +1,109 @@ +import type { NextApiRequest, NextApiResponse } from "next"; + +import { handlePaymentSuccess } from "@calcom/app-store/_utils/payments/handlePaymentSuccess"; +import { IS_PRODUCTION } from "@calcom/lib/constants"; +import { HttpError as HttpCode } from "@calcom/lib/http-error"; +import logger from "@calcom/lib/logger"; +import { safeStringify } from "@calcom/lib/safeStringify"; +import { getServerErrorFromUnknown } from "@calcom/lib/server/getServerErrorFromUnknown"; +import { distributedTracing } from "@calcom/lib/tracing/factory"; +import prisma from "@calcom/prisma"; + +import { appKeysSchema } from "../zod"; +import { PaystackClient } from "../lib/PaystackClient"; + +const log = logger.getSubLogger({ prefix: ["[paystackVerify]"] }); + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + if (req.method !== "GET") { + throw new HttpCode({ statusCode: 405, message: "Method Not Allowed" }); + } + + const reference = req.query.reference as string; + if (!reference) { + throw new HttpCode({ statusCode: 400, message: "Missing reference parameter" }); + } + + const payment = await prisma.payment.findFirst({ + where: { externalId: reference }, + select: { + id: true, + bookingId: true, + success: true, + booking: { + select: { + eventType: { + select: { metadata: true }, + }, + userId: true, + }, + }, + }, + }); + + if (!payment?.bookingId) { + throw new HttpCode({ statusCode: 404, message: "Payment not found" }); + } + + // Already processed + if (payment.success) { + res.status(200).json({ status: "success", message: "Payment already confirmed" }); + return; + } + + // Find credential + const metadata = payment.booking?.eventType?.metadata as Record | null; + const paystackAppData = (metadata?.apps as Record | undefined)?.paystack as + | { credentialId?: number } + | undefined; + + const credentialQuery = paystackAppData?.credentialId + ? { id: paystackAppData.credentialId } + : { userId: payment.booking?.userId, appId: "paystack" as const }; + + const credential = await prisma.credential.findFirst({ + where: credentialQuery, + select: { key: true }, + }); + + if (!credential) { + throw new HttpCode({ statusCode: 500, message: "Missing payment credentials" }); + } + + const parsedKeys = appKeysSchema.safeParse(credential.key); + if (!parsedKeys.success) { + throw new HttpCode({ statusCode: 500, message: "Malformed credentials" }); + } + + // Verify with Paystack + const client = new PaystackClient(parsedKeys.data.secret_key); + const verification = await client.verifyTransaction(reference); + + if (verification.status !== "success") { + res.status(200).json({ status: verification.status, message: "Payment not yet successful" }); + return; + } + + // Confirm booking + const traceContext = distributedTracing.createTrace("paystack_verify", { + meta: { reference, bookingId: payment.bookingId }, + }); + + await handlePaymentSuccess({ + paymentId: payment.id, + bookingId: payment.bookingId, + appSlug: "paystack", + traceContext, + }); + + res.status(200).json({ status: "success", message: "Payment confirmed" }); + } catch (_err) { + const err = getServerErrorFromUnknown(_err); + log.error(`Verify Error: ${err.message}`, safeStringify(err)); + res.status(err.statusCode).json({ + message: err.message, + stack: IS_PRODUCTION ? undefined : err.cause?.stack, + }); + } +} diff --git a/packages/app-store/paystack/api/webhook.ts b/packages/app-store/paystack/api/webhook.ts new file mode 100644 index 00000000000000..a17988580c0d30 --- /dev/null +++ b/packages/app-store/paystack/api/webhook.ts @@ -0,0 +1,164 @@ +import { buffer } from "micro"; +import type { NextApiRequest, NextApiResponse } from "next"; + +import { handlePaymentSuccess } from "@calcom/app-store/_utils/payments/handlePaymentSuccess"; +import { IS_PRODUCTION } from "@calcom/lib/constants"; +import { HttpError as HttpCode } from "@calcom/lib/http-error"; +import logger from "@calcom/lib/logger"; +import { safeStringify } from "@calcom/lib/safeStringify"; +import { getServerErrorFromUnknown } from "@calcom/lib/server/getServerErrorFromUnknown"; +import { distributedTracing } from "@calcom/lib/tracing/factory"; +import prisma from "@calcom/prisma"; + +import { appKeysSchema } from "../zod"; +import { PaystackClient } from "../lib/PaystackClient"; +import { verifyWebhookSignature } from "../lib/verifyWebhookSignature"; + +const log = logger.getSubLogger({ prefix: ["[paystackWebhook]"] }); + +export const config = { + api: { + bodyParser: false, + }, +}; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + if (req.method !== "POST") { + throw new HttpCode({ statusCode: 405, message: "Method Not Allowed" }); + } + + const requestBuffer = await buffer(req); + const bodyString = requestBuffer.toString(); + + // Parse body to get the reference (needed to find the credential for signature verification) + let parsedBody: { event: string; data: { reference: string } }; + try { + parsedBody = JSON.parse(bodyString); + } catch { + throw new HttpCode({ statusCode: 400, message: "Invalid JSON body" }); + } + + if (!parsedBody?.data?.reference) { + throw new HttpCode({ statusCode: 400, message: "Missing reference in payload" }); + } + + const reference = parsedBody.data.reference; + + // Look up payment by reference to find the credential + const payment = await prisma.payment.findFirst({ + where: { externalId: reference }, + select: { + id: true, + bookingId: true, + success: true, + booking: { + select: { + eventType: { + select: { + metadata: true, + }, + }, + userId: true, + }, + }, + }, + }); + + if (!payment?.bookingId) { + log.error("Payment not found for reference", { reference }); + throw new HttpCode({ statusCode: 204, message: "Payment not found" }); + } + + // Find the credential to verify the signature + const metadata = payment.booking?.eventType?.metadata as Record | null; + const paystackAppData = (metadata?.apps as Record | undefined)?.paystack as + | { credentialId?: number } + | undefined; + + const credentialQuery = paystackAppData?.credentialId + ? { id: paystackAppData.credentialId } + : { userId: payment.booking?.userId, appId: "paystack" as const }; + + const credential = await prisma.credential.findFirst({ + where: credentialQuery, + select: { key: true }, + }); + + if (!credential) { + log.error("Paystack credentials not found"); + throw new HttpCode({ statusCode: 500, message: "Missing payment credentials" }); + } + + const parsedKeys = appKeysSchema.safeParse(credential.key); + if (!parsedKeys.success) { + throw new HttpCode({ statusCode: 500, message: "Malformed credentials" }); + } + + // Verify webhook signature + const signature = req.headers["x-paystack-signature"] as string | undefined; + if (!signature || !verifyWebhookSignature(bodyString, signature, parsedKeys.data.secret_key)) { + log.error("Invalid Paystack webhook signature"); + throw new HttpCode({ statusCode: 401, message: "Invalid signature" }); + } + + // Only handle charge.success events + if (parsedBody.event !== "charge.success") { + res.status(200).json({ message: `Unhandled event type: ${parsedBody.event}` }); + return; + } + + // Atomic idempotency: only proceed if we can flip success from false to true + const updated = await prisma.payment.updateMany({ + where: { id: payment.id, success: false }, + data: { success: true }, + }); + + if (updated.count === 0) { + // Another request already processed this payment + res.status(200).json({ message: "Payment already processed" }); + return; + } + + // Everything after the lock must rollback on failure so retries can re-process + try { + // Re-verify with Paystack API (belt and suspenders) + const client = new PaystackClient(parsedKeys.data.secret_key); + const verification = await client.verifyTransaction(reference); + + if (verification.status !== "success") { + log.error("Paystack verification failed", { reference, status: verification.status }); + throw new HttpCode({ statusCode: 400, message: "Payment verification failed" }); + } + + // Confirm the booking + const traceContext = distributedTracing.createTrace("paystack_webhook", { + meta: { reference, bookingId: payment.bookingId }, + }); + + await handlePaymentSuccess({ + paymentId: payment.id, + bookingId: payment.bookingId, + appSlug: "paystack", + traceContext, + }); + } catch (processingError) { + // Rollback so webhook retries can re-process this payment + await prisma.payment.update({ + where: { id: payment.id }, + data: { success: false }, + }); + throw processingError; + } + } catch (_err) { + const err = getServerErrorFromUnknown(_err); + log.error(`Webhook Error: ${err.message}`, safeStringify(err)); + res.status(err.statusCode).send({ + message: err.message, + stack: IS_PRODUCTION ? undefined : err.cause?.stack, + }); + return; + } + + res.status(200).json({ received: true }); +} diff --git a/packages/app-store/paystack/components/EventTypeAppCardInterface.tsx b/packages/app-store/paystack/components/EventTypeAppCardInterface.tsx new file mode 100644 index 00000000000000..1aa66f6c2cd8c5 --- /dev/null +++ b/packages/app-store/paystack/components/EventTypeAppCardInterface.tsx @@ -0,0 +1,59 @@ +import { usePathname, useSearchParams } from "next/navigation"; +import { useState, useMemo } from "react"; + +import { useAppContextWithSchema } from "@calcom/app-store/EventTypeAppContext"; +import AppCard from "@calcom/app-store/_components/AppCard"; +import type { EventTypeAppCardComponent } from "@calcom/app-store/types"; +import { WEBAPP_URL } from "@calcom/lib/constants"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; + +import checkForMultiplePaymentApps from "../../_utils/payments/checkForMultiplePaymentApps"; +import type { appDataSchema } from "../zod"; +import EventTypeAppSettingsInterface from "./EventTypeAppSettingsInterface"; + +const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ + app, + eventType, + eventTypeFormMetadata, + onAppInstallSuccess, +}) { + const searchParams = useSearchParams(); + const pathname = usePathname(); + const asPath = useMemo( + () => `${pathname}${searchParams ? `?${searchParams.toString()}` : ""}`, + [pathname, searchParams] + ); + const { getAppData, setAppData, disabled } = useAppContextWithSchema(); + const [requirePayment, setRequirePayment] = useState(getAppData("enabled")); + const otherPaymentAppEnabled = checkForMultiplePaymentApps(eventTypeFormMetadata); + const { t } = useLocale(); + + const shouldDisableSwitch = !requirePayment && otherPaymentAppEnabled; + + return ( + { + setRequirePayment(enabled); + setAppData("enabled", enabled); + }} + description={<>{t("paystack_app_description")}} + disableSwitch={shouldDisableSwitch} + switchTooltip={shouldDisableSwitch ? t("other_payment_app_enabled") : undefined}> + <> + + + + ); +}; + +export default EventTypeAppCard; diff --git a/packages/app-store/paystack/components/EventTypeAppSettingsInterface.tsx b/packages/app-store/paystack/components/EventTypeAppSettingsInterface.tsx new file mode 100644 index 00000000000000..d7b85f504c03ce --- /dev/null +++ b/packages/app-store/paystack/components/EventTypeAppSettingsInterface.tsx @@ -0,0 +1,170 @@ +import * as RadioGroup from "@radix-ui/react-radio-group"; +import { useState, useEffect } from "react"; + +import type { EventTypeAppSettingsComponent } from "@calcom/app-store/types"; +import { + convertToSmallestCurrencyUnit, + convertFromSmallestToPresentableCurrencyUnit, +} from "@calcom/lib/currencyConversions"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { RefundPolicy } from "@calcom/lib/payment/types"; +import classNames from "@calcom/ui/classNames"; +import { Alert } from "@calcom/ui/components/alert"; +import { Select } from "@calcom/ui/components/form"; +import { TextField } from "@calcom/ui/components/form"; +import { RadioField } from "@calcom/ui/components/radio"; + +import { currencyOptions, currencySymbols, isAcceptedCurrencyCode } from "../lib/currencyOptions"; + +const EventTypeAppSettingsInterface: EventTypeAppSettingsComponent = ({ + getAppData, + setAppData, + disabled, + eventType, +}) => { + const price = getAppData("price"); + const currency = getAppData("currency") || currencyOptions[0].value; + const [selectedCurrency, setSelectedCurrency] = useState( + currencyOptions.find((c) => c.value === currency) || { + label: currencyOptions[0].label, + value: currencyOptions[0].value, + } + ); + const requirePayment = getAppData("enabled"); + + const { t } = useLocale(); + const recurringEventDefined = eventType.recurringEvent?.count !== undefined; + + const getCurrencySymbol = (curr: string) => + isAcceptedCurrencyCode(curr) ? currencySymbols[curr] : ""; + + useEffect(() => { + if (requirePayment) { + if (!getAppData("currency")) { + setAppData("currency", currencyOptions[0].value); + } + if (!getAppData("paymentOption")) { + setAppData("paymentOption", "ON_BOOKING"); + } + } + if (!getAppData("refundPolicy")) { + setAppData("refundPolicy", RefundPolicy.NEVER); + } + }, [requirePayment, getAppData, setAppData]); + + const dayTypeOptions = [ + { value: 0, label: t("business_days") }, + { value: 1, label: t("calendar_days") }, + ]; + + const getSelectedDayType = () => + dayTypeOptions.find((opt) => opt.value === (getAppData("refundCountCalendarDays") === true ? 1 : 0)); + + return ( + <> + {recurringEventDefined && ( + + )} + {!recurringEventDefined && requirePayment && ( + <> +
+ {getCurrencySymbol(selectedCurrency.value)}} + addOnSuffix={currency.toUpperCase()} + addOnClassname="h-[38px]" + step="0.01" + min="0.5" + type="number" + required + placeholder="Price" + disabled={disabled} + onChange={(e) => { + setAppData("price", convertToSmallestCurrencyUnit(Number(e.target.value), currency)); + }} + value={price > 0 ? convertFromSmallestToPresentableCurrencyUnit(price, currency) : undefined} + /> +
+
+ + setAppData("refundCountCalendarDays", option?.value === 1)} + value={getSelectedDayType()} + defaultValue={getSelectedDayType()} + /> +  {t("before")} +
+
+ + + + )} + + ); +}; + +export default EventTypeAppSettingsInterface; diff --git a/packages/app-store/paystack/components/PaystackPaymentComponent.tsx b/packages/app-store/paystack/components/PaystackPaymentComponent.tsx new file mode 100644 index 00000000000000..1a06f5a51ba486 --- /dev/null +++ b/packages/app-store/paystack/components/PaystackPaymentComponent.tsx @@ -0,0 +1,116 @@ +"use client"; + +import { useState } from "react"; + +import { convertFromSmallestToPresentableCurrencyUnit } from "@calcom/lib/currencyConversions"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import type { Payment } from "@calcom/prisma/client"; +import { Button } from "@calcom/ui/components/button"; + +interface PaystackPaymentData { + access_code: string; + authorization_url: string; + publicKey: string; + reference: string; +} + +interface PaystackPaymentComponentProps { + payment: Payment & { + data: PaystackPaymentData; + }; + clientId: string; + bookingUid: string; + bookingTitle: string; + amount: number; + currency: string; +} + +export default function PaystackPaymentComponent({ + payment, + bookingUid, + bookingTitle, + amount, + currency, +}: PaystackPaymentComponentProps) { + const [status, setStatus] = useState<"idle" | "loading" | "success" | "error">("idle"); + const [errorMessage, setErrorMessage] = useState(""); + const { t } = useLocale(); + + const paymentData = payment.data as unknown as PaystackPaymentData; + + const formattedAmount = new Intl.NumberFormat("en", { + style: "currency", + currency: currency.toUpperCase(), + }).format(convertFromSmallestToPresentableCurrencyUnit(amount, currency)); + + const handlePayment = async () => { + setStatus("loading"); + setErrorMessage(""); + + try { + const PaystackPop = (await import("@paystack/inline-js")).default; + const popup = new PaystackPop(); + + popup.resumeTransaction(paymentData.access_code, { + onSuccess: async () => { + setStatus("success"); + + // Backup verification — call our verify endpoint + try { + await fetch( + `/api/integrations/paystack/verify?reference=${paymentData.reference}` + ); + } catch { + // Webhook will handle it if this fails + } + + // Redirect to booking confirmation + setTimeout(() => { + window.location.href = `/booking/${bookingUid}`; + }, 2000); + }, + onCancel: () => { + setStatus("idle"); + }, + onError: () => { + setStatus("error"); + setErrorMessage(t("payment_failed_try_again")); + }, + }); + } catch { + setStatus("error"); + setErrorMessage(t("payment_failed_try_again")); + } + }; + + if (status === "success") { + return ( +
+
{t("payment_successful")}
+

{t("redirecting_to_booking_confirmation")}

+
+ ); + } + + return ( +
+

{bookingTitle}

+

{formattedAmount}

+ + {errorMessage &&

{errorMessage}

} + + + + {status === "idle" && ( +

{t("paystack_payment_prompt")}

+ )} +
+ ); +} diff --git a/packages/app-store/paystack/config.json b/packages/app-store/paystack/config.json new file mode 100644 index 00000000000000..a0428d445850c0 --- /dev/null +++ b/packages/app-store/paystack/config.json @@ -0,0 +1,20 @@ +{ + "/*": "Don't modify slug - If required, do it using cli edit command", + "name": "Paystack", + "slug": "paystack", + "type": "paystack_payment", + "logo": "icon.svg", + "url": "https://paystack.com", + "variant": "payment", + "categories": ["payment"], + "publisher": "Cal.com", + "email": "support@cal.com", + "description": "Accept payments via Paystack for your Cal.com bookings", + "extendsFeature": "EventType", + "isTemplate": false, + "__createdUsingCli": true, + "imageSrc": "icon.svg", + "__template": "event-type-app-card", + "dirName": "paystack", + "isOAuth": true +} diff --git a/packages/app-store/paystack/index.ts b/packages/app-store/paystack/index.ts new file mode 100644 index 00000000000000..e2e9d7b029c031 --- /dev/null +++ b/packages/app-store/paystack/index.ts @@ -0,0 +1,2 @@ +export * as api from "./api"; +export * as lib from "./lib"; diff --git a/packages/app-store/paystack/lib/PaymentService.ts b/packages/app-store/paystack/lib/PaymentService.ts new file mode 100644 index 00000000000000..99c1a7cfc76cba --- /dev/null +++ b/packages/app-store/paystack/lib/PaymentService.ts @@ -0,0 +1,207 @@ +import { v4 as uuidv4 } from "uuid"; + +import prisma from "@calcom/prisma"; +import type { Booking, Payment, Prisma, PaymentOption } from "@calcom/prisma/client"; +import type { CalendarEvent } from "@calcom/types/Calendar"; +import type { IAbstractPaymentService } from "@calcom/types/PaymentService"; + +import { appKeysSchema } from "../zod"; +import { PaystackClient } from "./PaystackClient"; + +class PaystackPaymentService implements IAbstractPaymentService { + private client: PaystackClient | null; + private credentials: { public_key: string; secret_key: string } | null; + + constructor(credentials: { key: Prisma.JsonValue }) { + const parsed = appKeysSchema.safeParse(credentials.key); + if (parsed.success) { + this.credentials = parsed.data; + this.client = new PaystackClient(parsed.data.secret_key); + } else { + this.credentials = null; + this.client = null; + } + } + + async create( + payment: Pick, + bookingId: Booking["id"], + _userId: Booking["userId"], + _username: string | null, + _bookerName: string | null, + _paymentOption: PaymentOption, + bookerEmail: string, + _bookerPhoneNumber?: string | null, + eventTitle?: string, + _bookingTitle?: string + ): Promise { + const booking = await prisma.booking.findUnique({ + select: { uid: true, title: true }, + where: { id: bookingId }, + }); + + if (!booking) { + throw new Error("Booking not found"); + } + + if (!this.client || !this.credentials) { + throw new Error("Paystack credentials not configured"); + } + + const uid = uuidv4(); + const reference = `cal_${bookingId}_${uid.slice(0, 8)}`; + + const paystackResponse = await this.client.initializeTransaction({ + email: bookerEmail, + amount: payment.amount, + currency: payment.currency.toUpperCase(), + reference, + callback_url: `${process.env.NEXT_PUBLIC_WEBAPP_URL}/api/integrations/paystack/verify`, + metadata: { + bookingId, + eventTitle: eventTitle || booking.title, + }, + }); + + const paymentData = await prisma.payment.create({ + data: { + uid, + app: { + connect: { + slug: "paystack", + }, + }, + booking: { + connect: { + id: bookingId, + }, + }, + amount: payment.amount, + externalId: reference, + currency: payment.currency, + data: { + access_code: paystackResponse.access_code, + authorization_url: paystackResponse.authorization_url, + publicKey: this.credentials.public_key, + reference, + } as unknown as Prisma.InputJsonValue, + fee: 0, + refunded: false, + success: false, + paymentOption: "ON_BOOKING", + }, + }); + + return paymentData; + } + + async collectCard( + _payment: Pick, + _bookingId: Booking["id"], + _paymentOption: PaymentOption, + _bookerEmail: string, + _bookerPhoneNumber?: string | null + ): Promise { + throw new Error("Paystack does not support card hold. Only ON_BOOKING payment is available."); + } + + async chargeCard( + _payment: Pick, + _bookingId?: Booking["id"] + ): Promise { + throw new Error("Paystack does not support card hold. Only ON_BOOKING payment is available."); + } + + async update( + paymentId: Payment["id"], + data: Partial + ): Promise { + return await prisma.payment.update({ + where: { id: paymentId }, + data, + }); + } + + async refund(paymentId: Payment["id"]): Promise { + const payment = await prisma.payment.findUnique({ + where: { id: paymentId }, + select: { + id: true, + success: true, + refunded: true, + externalId: true, + }, + }); + + if (!payment) { + return null; + } + + if (payment.refunded) { + return await prisma.payment.findUnique({ where: { id: paymentId } }); + } + + if (!payment.success) { + return await prisma.payment.findUnique({ where: { id: paymentId } }); + } + + if (!this.client) { + throw new Error("Paystack credentials not configured"); + } + + await this.client.createRefund({ + transaction: payment.externalId, + }); + + return await prisma.payment.update({ + where: { id: paymentId }, + data: { refunded: true }, + }); + } + + async getPaymentPaidStatus(): Promise { + return "paid"; + } + + async getPaymentDetails(): Promise { + throw new Error("Method not implemented."); + } + + async afterPayment( + _event: CalendarEvent, + _booking: { + user: { email: string | null; name: string | null; timeZone: string } | null; + id: number; + startTime: { toISOString: () => string }; + uid: string; + }, + _paymentData: Payment + ): Promise { + // No post-payment actions needed for Paystack + return Promise.resolve(); + } + + async deletePayment(paymentId: Payment["id"]): Promise { + try { + await prisma.payment.delete({ + where: { id: paymentId }, + }); + return true; + } catch { + return false; + } + } + + isSetupAlready(): boolean { + return !!(this.credentials?.public_key && this.credentials?.secret_key); + } +} + +/** + * Factory function that creates a Paystack Payment service instance. + * Exported instead of the class to prevent internal types from leaking + * into the emitted .d.ts file. + */ +export function BuildPaymentService(credentials: { key: Prisma.JsonValue }): IAbstractPaymentService { + return new PaystackPaymentService(credentials); +} diff --git a/packages/app-store/paystack/lib/PaystackClient.ts b/packages/app-store/paystack/lib/PaystackClient.ts new file mode 100644 index 00000000000000..14e97902d58b94 --- /dev/null +++ b/packages/app-store/paystack/lib/PaystackClient.ts @@ -0,0 +1,84 @@ +const PAYSTACK_BASE_URL = "https://api.paystack.co"; + +export class PaystackClient { + private secretKey: string; + + constructor(secretKey: string) { + this.secretKey = secretKey; + } + + async initializeTransaction(params: { + email: string; + amount: number; + currency: string; + reference: string; + callback_url: string; + metadata?: Record; + }): Promise<{ + authorization_url: string; + access_code: string; + reference: string; + }> { + const response = await fetch(`${PAYSTACK_BASE_URL}/transaction/initialize`, { + method: "POST", + headers: { + Authorization: `Bearer ${this.secretKey}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(params), + }); + + const json = await response.json(); + + if (!response.ok || !json.status) { + throw new Error(`Paystack API error: ${json.message || "Unknown error"}`); + } + + return json.data; + } + + async verifyTransaction(reference: string): Promise<{ + status: string; + amount: number; + currency: string; + reference: string; + paid_at: string | null; + }> { + const response = await fetch(`${PAYSTACK_BASE_URL}/transaction/verify/${reference}`, { + method: "GET", + headers: { + Authorization: `Bearer ${this.secretKey}`, + }, + }); + + const json = await response.json(); + + if (!response.ok || !json.status) { + throw new Error(`Paystack API error: ${json.message || "Unknown error"}`); + } + + return json.data; + } + + async createRefund(params: { + transaction: string; + amount?: number; + }): Promise<{ status: boolean; data: unknown }> { + const response = await fetch(`${PAYSTACK_BASE_URL}/refund`, { + method: "POST", + headers: { + Authorization: `Bearer ${this.secretKey}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(params), + }); + + const json = await response.json(); + + if (!response.ok || !json.status) { + throw new Error(`Paystack API error: ${json.message || "Unknown error"}`); + } + + return json; + } +} diff --git a/packages/app-store/paystack/lib/__tests__/PaystackClient.test.ts b/packages/app-store/paystack/lib/__tests__/PaystackClient.test.ts new file mode 100644 index 00000000000000..ace6a810eccde4 --- /dev/null +++ b/packages/app-store/paystack/lib/__tests__/PaystackClient.test.ts @@ -0,0 +1,161 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +import { PaystackClient } from "../PaystackClient"; + +describe("PaystackClient", () => { + let client: PaystackClient; + + beforeEach(() => { + client = new PaystackClient("sk_test_xxxxx"); + }); + + describe("initializeTransaction", () => { + it("sends correct params and returns parsed response", async () => { + const mockResponse = { + status: true, + message: "Authorization URL created", + data: { + authorization_url: "https://checkout.paystack.com/abc123", + access_code: "abc123", + reference: "cal_42_ref123", + }, + }; + + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve(mockResponse), + }) + ); + + const result = await client.initializeTransaction({ + email: "test@example.com", + amount: 500000, + currency: "NGN", + reference: "cal_42_ref123", + callback_url: "https://cal.com/payment/callback", + metadata: { bookingId: 42 }, + }); + + expect(fetch).toHaveBeenCalledWith("https://api.paystack.co/transaction/initialize", { + method: "POST", + headers: { + Authorization: "Bearer sk_test_xxxxx", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + email: "test@example.com", + amount: 500000, + currency: "NGN", + reference: "cal_42_ref123", + callback_url: "https://cal.com/payment/callback", + metadata: { bookingId: 42 }, + }), + }); + + expect(result).toEqual({ + authorization_url: "https://checkout.paystack.com/abc123", + access_code: "abc123", + reference: "cal_42_ref123", + }); + }); + + it("throws on API error response", async () => { + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue({ + ok: false, + status: 400, + json: () => Promise.resolve({ status: false, message: "Invalid amount" }), + }) + ); + + await expect( + client.initializeTransaction({ + email: "test@example.com", + amount: 0, + currency: "NGN", + reference: "cal_42_ref123", + callback_url: "https://cal.com/payment/callback", + }) + ).rejects.toThrow("Paystack API error: Invalid amount"); + }); + }); + + describe("verifyTransaction", () => { + it("returns parsed verification result", async () => { + const mockResponse = { + status: true, + data: { + status: "success", + amount: 500000, + currency: "NGN", + reference: "cal_42_ref123", + paid_at: "2026-04-04T12:00:00.000Z", + }, + }; + + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve(mockResponse), + }) + ); + + const result = await client.verifyTransaction("cal_42_ref123"); + + expect(fetch).toHaveBeenCalledWith( + "https://api.paystack.co/transaction/verify/cal_42_ref123", + { + method: "GET", + headers: { + Authorization: "Bearer sk_test_xxxxx", + }, + } + ); + + expect(result).toEqual({ + status: "success", + amount: 500000, + currency: "NGN", + reference: "cal_42_ref123", + paid_at: "2026-04-04T12:00:00.000Z", + }); + }); + }); + + describe("createRefund", () => { + it("sends refund request with transaction reference", async () => { + const mockResponse = { + status: true, + data: { + status: "pending", + transaction: { reference: "cal_42_ref123" }, + }, + }; + + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve(mockResponse), + }) + ); + + const result = await client.createRefund({ transaction: "cal_42_ref123" }); + + expect(fetch).toHaveBeenCalledWith("https://api.paystack.co/refund", { + method: "POST", + headers: { + Authorization: "Bearer sk_test_xxxxx", + "Content-Type": "application/json", + }, + body: JSON.stringify({ transaction: "cal_42_ref123" }), + }); + + expect(result.status).toBe(true); + }); + }); +}); diff --git a/packages/app-store/paystack/lib/__tests__/verifyWebhookSignature.test.ts b/packages/app-store/paystack/lib/__tests__/verifyWebhookSignature.test.ts new file mode 100644 index 00000000000000..1936684e7cf83e --- /dev/null +++ b/packages/app-store/paystack/lib/__tests__/verifyWebhookSignature.test.ts @@ -0,0 +1,28 @@ +import crypto from "crypto"; + +import { describe, it, expect } from "vitest"; + +import { verifyWebhookSignature } from "../verifyWebhookSignature"; + +describe("verifyWebhookSignature", () => { + const secretKey = "sk_test_secretkey123"; + const body = JSON.stringify({ event: "charge.success", data: { reference: "ref123" } }); + const validSignature = crypto.createHmac("sha512", secretKey).update(body).digest("hex"); + + it("returns true for valid signature", () => { + expect(verifyWebhookSignature(body, validSignature, secretKey)).toBe(true); + }); + + it("returns false for tampered body", () => { + const tamperedBody = JSON.stringify({ event: "charge.success", data: { reference: "TAMPERED" } }); + expect(verifyWebhookSignature(tamperedBody, validSignature, secretKey)).toBe(false); + }); + + it("returns false for wrong secret key", () => { + expect(verifyWebhookSignature(body, validSignature, "wrong_secret")).toBe(false); + }); + + it("returns false for empty signature", () => { + expect(verifyWebhookSignature(body, "", secretKey)).toBe(false); + }); +}); diff --git a/packages/app-store/paystack/lib/currencyOptions.ts b/packages/app-store/paystack/lib/currencyOptions.ts new file mode 100644 index 00000000000000..02825261a7f23e --- /dev/null +++ b/packages/app-store/paystack/lib/currencyOptions.ts @@ -0,0 +1,19 @@ +export const currencyOptions = [ + { label: "Nigerian naira (NGN)", value: "ngn" }, + { label: "Ghanaian cedi (GHS)", value: "ghs" }, + { label: "South African rand (ZAR)", value: "zar" }, + { label: "Kenyan shilling (KES)", value: "kes" }, + { label: "United States dollar (USD)", value: "usd" }, +]; + +export const currencySymbols: Record = { + ngn: "₦", + ghs: "GH₵", + zar: "R", + kes: "KSh", + usd: "$", +}; + +export const isAcceptedCurrencyCode = (code: string): code is keyof typeof currencySymbols => { + return code in currencySymbols; +}; diff --git a/packages/app-store/paystack/lib/verifyWebhookSignature.ts b/packages/app-store/paystack/lib/verifyWebhookSignature.ts new file mode 100644 index 00000000000000..8c3d24043f2e49 --- /dev/null +++ b/packages/app-store/paystack/lib/verifyWebhookSignature.ts @@ -0,0 +1,13 @@ +import crypto from "crypto"; + +export function verifyWebhookSignature(body: string, signature: string, secretKey: string): boolean { + if (!signature) return false; + + const hash = crypto.createHmac("sha512", secretKey).update(body).digest("hex"); + + try { + return crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(signature)); + } catch { + return false; + } +} diff --git a/packages/app-store/paystack/package.json b/packages/app-store/paystack/package.json new file mode 100644 index 00000000000000..244cc297392e8d --- /dev/null +++ b/packages/app-store/paystack/package.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/package.json", + "private": true, + "name": "@calcom/paystack", + "version": "0.0.0", + "main": "./index.ts", + "scripts": { + "test": "TZ=UTC vitest run" + }, + "dependencies": { + "@calcom/lib": "workspace:*", + "@paystack/inline-js": "^2.0.0", + "uuid": "^9.0.0" + }, + "devDependencies": { + "@calcom/types": "workspace:*", + "@types/uuid": "^9.0.0" + }, + "description": "Paystack payment integration for Cal.com" +} diff --git a/packages/app-store/paystack/pages/setup/_getServerSideProps.ts b/packages/app-store/paystack/pages/setup/_getServerSideProps.ts new file mode 100644 index 00000000000000..fb322eb278b3c0 --- /dev/null +++ b/packages/app-store/paystack/pages/setup/_getServerSideProps.ts @@ -0,0 +1,40 @@ +import type { GetServerSidePropsContext } from "next"; + +import { throwIfNotHaveAdminAccessToTeam } from "@calcom/app-store/_utils/throwIfNotHaveAdminAccessToTeam"; +import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; +import prisma from "@calcom/prisma"; + +export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { + const notFound = { notFound: true } as const; + + if (typeof ctx.params?.slug !== "string") return notFound; + + const { req, query } = ctx; + const session = await getServerSession({ req }); + + if (!session?.user?.id) { + return { redirect: { permanent: false, destination: "/auth/login" } } as const; + } + + const teamId = query.teamId ? Number(query.teamId) : null; + + await throwIfNotHaveAdminAccessToTeam({ teamId, userId: session.user.id }); + const installForObject = teamId ? { teamId } : { userId: session.user.id }; + + const credential = await prisma.credential.findFirst({ + where: { + type: "paystack_payment", + ...installForObject, + }, + select: { + id: true, + key: true, + }, + }); + + return { + props: { + credentialId: credential?.id ?? null, + }, + }; +}; diff --git a/packages/app-store/paystack/static/icon.svg b/packages/app-store/paystack/static/icon.svg new file mode 100644 index 00000000000000..b1556dad70f44c --- /dev/null +++ b/packages/app-store/paystack/static/icon.svg @@ -0,0 +1 @@ + diff --git a/packages/app-store/paystack/zod.ts b/packages/app-store/paystack/zod.ts new file mode 100644 index 00000000000000..aa4bca386fc15e --- /dev/null +++ b/packages/app-store/paystack/zod.ts @@ -0,0 +1,20 @@ +import { z } from "zod"; + +import { eventTypeAppCardZod } from "@calcom/app-store/eventTypeAppCardZod"; +import { RefundPolicy } from "@calcom/lib/payment/types"; + +export const appDataSchema = eventTypeAppCardZod.merge( + z.object({ + price: z.number(), + currency: z.string(), + paymentOption: z.literal("ON_BOOKING").optional(), + refundPolicy: z.nativeEnum(RefundPolicy).optional(), + refundDaysCount: z.number().optional(), + refundCountCalendarDays: z.boolean().optional(), + }) +); + +export const appKeysSchema = z.object({ + public_key: z.string().min(1), + secret_key: z.string().min(1), +}); diff --git a/packages/app-store/redirect-apps.generated.ts b/packages/app-store/redirect-apps.generated.ts index abe13cf533913f..a035a8d21a0e8d 100644 --- a/packages/app-store/redirect-apps.generated.ts +++ b/packages/app-store/redirect-apps.generated.ts @@ -2,34 +2,5 @@ This file is autogenerated using the command `yarn app-store:build --watch`. Don't modify this file manually. **/ -export const REDIRECT_APPS = [ - "amie", - "autocheckin", - "baa-for-hipaa", - "bolna", - "caretta", - "chatbase", - "clic", - "cron", - "deel", - "elevenlabs", - "famulor", - "fonio-ai", - "framer", - "granola", - "greetmate-ai", - "lindy", - "linear", - "millis-ai", - "monobot", - "n8n", - "pipedream", - "raycast", - "retell-ai", - "synthflow", - "telli", - "vimcal", - "wordpress", - "zapier", -] as const; -export type RedirectApp = (typeof REDIRECT_APPS)[number]; +export const REDIRECT_APPS = ["amie","autocheckin","baa-for-hipaa","bolna","caretta","chatbase","clic","cron","deel","elevenlabs","famulor","fonio-ai","framer","granola","greetmate-ai","lindy","linear","millis-ai","monobot","n8n","pipedream","raycast","retell-ai","synthflow","telli","vimcal","wordpress","zapier"] as const; +export type RedirectApp = typeof REDIRECT_APPS[number]; \ No newline at end of file diff --git a/packages/app-store/video.adapters.generated.ts b/packages/app-store/video.adapters.generated.ts index f1e0f05ffe7f78..e069a2ae8fdc9d 100644 --- a/packages/app-store/video.adapters.generated.ts +++ b/packages/app-store/video.adapters.generated.ts @@ -2,20 +2,17 @@ This file is autogenerated using the command `yarn app-store:build --watch`. Don't modify this file manually. **/ -export const VideoApiAdapterMap = - process.env.NEXT_PUBLIC_IS_E2E === "1" - ? {} - : { - dailyvideo: import("./dailyvideo/lib/VideoApiAdapter"), - huddle01video: import("./huddle01video/lib/VideoApiAdapter"), - jelly: import("./jelly/lib/VideoApiAdapter"), - jitsivideo: import("./jitsivideo/lib/VideoApiAdapter"), - lyra: import("./lyra/lib/VideoApiAdapter"), - nextcloudtalk: import("./nextcloudtalk/lib/VideoApiAdapter"), - office365video: import("./office365video/lib/VideoApiAdapter"), - shimmervideo: import("./shimmervideo/lib/VideoApiAdapter"), - sylapsvideo: import("./sylapsvideo/lib/VideoApiAdapter"), - tandemvideo: import("./tandemvideo/lib/VideoApiAdapter"), - webex: import("./webex/lib/VideoApiAdapter"), - zoomvideo: import("./zoomvideo/lib/VideoApiAdapter"), - }; +export const VideoApiAdapterMap = process.env.NEXT_PUBLIC_IS_E2E === '1' ? {} : { +"dailyvideo": import("./dailyvideo/lib/VideoApiAdapter"), +"huddle01video": import("./huddle01video/lib/VideoApiAdapter"), +"jelly": import("./jelly/lib/VideoApiAdapter"), +"jitsivideo": import("./jitsivideo/lib/VideoApiAdapter"), +"lyra": import("./lyra/lib/VideoApiAdapter"), +"nextcloudtalk": import("./nextcloudtalk/lib/VideoApiAdapter"), +"office365video": import("./office365video/lib/VideoApiAdapter"), +"shimmervideo": import("./shimmervideo/lib/VideoApiAdapter"), +"sylapsvideo": import("./sylapsvideo/lib/VideoApiAdapter"), +"tandemvideo": import("./tandemvideo/lib/VideoApiAdapter"), +"webex": import("./webex/lib/VideoApiAdapter"), +"zoomvideo": import("./zoomvideo/lib/VideoApiAdapter"), +}; \ No newline at end of file diff --git a/packages/i18n/locales/en/common.json b/packages/i18n/locales/en/common.json index 515683022c646c..d54e6fc3a35194 100644 --- a/packages/i18n/locales/en/common.json +++ b/packages/i18n/locales/en/common.json @@ -3750,6 +3750,17 @@ "resume_onboarding": "Resume onboarding", "resume_onboarding_description": "Continue with your organization onboarding process", "payment_successful": "Payment successful!", + "payment_failed_try_again": "Payment failed. Please try again.", + "redirecting_to_booking_confirmation": "Redirecting to your booking confirmation...", + "pay_with_paystack": "Pay with Paystack", + "paystack_payment_prompt": "You will be prompted to enter your payment details", + "paystack_app_description": "Accept payments via Paystack for your events", + "paystack_public_key": "Public Key", + "paystack_secret_key": "Secret Key", + "paystack_getting_started_description": "To use Paystack with Cal.com, you need a Paystack account. Get your API keys from your", + "paystack_dashboard": "Paystack Dashboard", + "paystack_webhook_setup": "Webhook Setup", + "paystack_webhook_setup_description": "Add this webhook URL in your Paystack dashboard under Settings > API Keys & Webhooks:", "handover_onboarding_page_title": "Handover onboarding", "handover_onboarding_page_description": "Handover onboarding to the user", "organization_already_exists_with_this_slug": "Organization already exists with this slug", diff --git a/scripts/seed-app-store.ts b/scripts/seed-app-store.ts index 45b1f36ed95b43..26d581647b59ef 100644 --- a/scripts/seed-app-store.ts +++ b/scripts/seed-app-store.ts @@ -235,6 +235,9 @@ export default async function main() { }); } + // Paystack doesn't require env vars at seed time — keys are entered via Setup UI + await createApp("paystack", "paystack", ["payment"], "paystack_payment"); + if (process.env.CLOSECOM_CLIENT_ID && process.env.CLOSECOM_CLIENT_SECRET) { await createApp("closecom", "closecom", ["crm"], "closecom_crm", { client_id: process.env.CLOSECOM_CLIENT_ID, diff --git a/yarn.lock b/yarn.lock index c3402539c3c5a0..8f66c01a210ac2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2912,6 +2912,18 @@ __metadata: languageName: unknown linkType: soft +"@calcom/paystack@workspace:packages/app-store/paystack": + version: 0.0.0-use.local + resolution: "@calcom/paystack@workspace:packages/app-store/paystack" + dependencies: + "@calcom/lib": "workspace:*" + "@calcom/types": "workspace:*" + "@paystack/inline-js": "npm:^2.0.0" + "@types/uuid": "npm:^9.0.0" + uuid: "npm:^9.0.0" + languageName: unknown + linkType: soft + "@calcom/ping@workspace:packages/app-store/ping": version: 0.0.0-use.local resolution: "@calcom/ping@workspace:packages/app-store/ping" @@ -10648,6 +10660,13 @@ __metadata: languageName: node linkType: hard +"@paystack/inline-js@npm:^2.0.0": + version: 2.22.8 + resolution: "@paystack/inline-js@npm:2.22.8" + checksum: 10/925a9e1d5d90fcde4d8316493afef2c988d5d8f503bc5c439669aa345348cf383fa083c3e2c4f83ecad45d0bd943977e050464ce85042f06a2117da02fa13cdd + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -17222,7 +17241,7 @@ __metadata: languageName: node linkType: hard -"@types/uuid@npm:^9.0.1": +"@types/uuid@npm:^9.0.0, @types/uuid@npm:^9.0.1": version: 9.0.8 resolution: "@types/uuid@npm:9.0.8" checksum: 10/b8c60b7ba8250356b5088302583d1704a4e1a13558d143c549c408bf8920535602ffc12394ede77f8a8083511b023704bc66d1345792714002bfa261b17c5275