Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions apps/web/app/(use-page-wrapper)/payment/[uid]/PaymentPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ const BtcpayPaymentComponent = dynamic(
}
);

const PaystackPaymentComponent = dynamic(
() => import("@calcom/app-store/paystack/components/PaystackPaymentComponent"),
{
ssr: false,
}
);

const PaymentPage: FC<PaymentPageProps> = (props) => {
const { t, i18n } = useLocale();
const [is24h, setIs24h] = useState(isBrowserLocale24h());
Expand Down Expand Up @@ -174,6 +181,18 @@ const PaymentPage: FC<PaymentPageProps> = (props) => {
{props.payment.appId === "btcpayserver" && !props.payment.success && (
<BtcpayPaymentComponent payment={props.payment} paymentPageProps={props} />
)}
{props.payment.appId === "paystack" && !props.payment.success && (
<PaystackPaymentComponent
payment={props.payment}
clientId={
(props.payment.data as unknown as { publicKey: string }).publicKey ?? ""
}
bookingUid={props.booking.uid}
bookingTitle={eventName}
amount={props.payment.amount}
currency={props.payment.currency}
/>
)}
{props.payment.refunded && (
<div className="text-default mt-4 text-center dark:text-gray-300">{t("refunded")}</div>
)}
Expand Down
1 change: 1 addition & 0 deletions apps/web/components/apps/AppSetupPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => {
Expand Down
132 changes: 132 additions & 0 deletions apps/web/components/apps/paystack/Setup.tsx
Original file line number Diff line number Diff line change
@@ -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 <div className="absolute z-50 flex h-screen w-full items-center bg-gray-200" />;
}

return (
<div className="bg-default flex h-screen">
{showContent ? (
<div className="bg-default border-subtle m-auto max-w-[43em] overflow-auto rounded border pb-10 md:p-10">
<div className="ml-2 ltr:mr-2 rtl:ml-2 md:ml-5">
<div className="invisible md:visible">
<img className="h-11" src="/api/app-store/paystack/icon.svg" alt="Paystack" />
<p className="text-default mt-5 text-lg">Paystack</p>
</div>

<form
autoComplete="off"
className="mt-5"
onSubmit={(e) => {
e.preventDefault();
saveKeysMutation.mutate({
credentialId,
key: {
public_key: newPublicKey,
secret_key: newSecretKey,
},
});
}}>
<TextField
label={t("paystack_public_key")}
type="text"
name="public_key"
id="public_key"
value={newPublicKey}
onChange={(e) => setNewPublicKey(e.target.value)}
role="presentation"
className="mb-6"
placeholder="pk_test_xxxxxxxxx"
/>

<TextField
label={t("paystack_secret_key")}
type="password"
name="secret_key"
id="secret_key"
value={newSecretKey}
autoComplete="new-password"
role="presentation"
onChange={(e) => setNewSecretKey(e.target.value)}
placeholder="sk_test_xxxxxxxxx"
/>

<div className="mt-5 flex flex-row justify-end">
<Button
type="submit"
color="primary"
loading={saveKeysMutation.isPending}
disabled={!newPublicKey || !newSecretKey}>
{t("save")}
</Button>
</div>
</form>

<div className="mt-5">
<p className="text-default font-bold">{t("getting_started")}</p>
<p className="text-default mt-2">
{t("paystack_getting_started_description")}{" "}
<a
className="text-blue-600 underline"
target="_blank"
href="https://dashboard.paystack.com/#/settings/developers"
rel="noreferrer">
{t("paystack_dashboard")}
</a>
.
</p>

<p className="text-default mt-4 font-bold">{t("paystack_webhook_setup")}</p>
<p className="text-default mt-2">
{t("paystack_webhook_setup_description")}
</p>
<code className="bg-subtle mt-2 block rounded p-2 text-sm">
{typeof window !== "undefined" ? window.location.origin : "https://your-cal.com"}
/api/integrations/paystack/webhook
</code>
</div>
</div>
</div>
) : (
<AppNotInstalledMessage appName="paystack" />
)}

<Toaster position="bottom-right" />
</div>
);
}
1 change: 1 addition & 0 deletions packages/app-store/_pages/setup/_getServerSideProps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
9 changes: 3 additions & 6 deletions packages/app-store/analytics.services.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
};
112 changes: 53 additions & 59 deletions packages/app-store/apps.browser.generated.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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")),
};
Loading
Loading