Skip to content

Commit

Permalink
Complete hitpay app setup
Browse files Browse the repository at this point in the history
Adding hitpay functions to hitpay app
  • Loading branch information
MuhammadAimanSulaiman committed Oct 1, 2024
1 parent 55e1e0f commit d49034b
Show file tree
Hide file tree
Showing 39 changed files with 1,266 additions and 5,671 deletions.
1 change: 1 addition & 0 deletions apps/web/pages/api/integrations/hitpay/webhook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default, config } from "@calcom/app-store/hitpay/api/webhook";
4 changes: 2 additions & 2 deletions apps/web/public/icons/sprite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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 @@ -5,6 +5,7 @@ export const AppSetupPageMap = {
make: import("../../make/pages/setup/_getServerSideProps"),
zapier: import("../../zapier/pages/setup/_getServerSideProps"),
stripe: import("../../stripepayment/pages/setup/_getServerSideProps"),
hitpay: import("../../hitpay/pages/setup/_getServerSideProps"),
};

export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
Expand Down
1 change: 1 addition & 0 deletions packages/app-store/_pages/setup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const AppSetupMap = {
sendgrid: dynamic(() => import("../../sendgrid/pages/setup")),
stripe: dynamic(() => import("../../stripepayment/pages/setup")),
paypal: dynamic(() => import("../../paypal/pages/setup")),
hitpay: dynamic(() => import("../../hitpay/pages/setup")),
};

export const AppSetupPage = (props: { slug: string }) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/app-store/alby/pages/setup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ function AlbySetupPage(props: IAlbySetupProps) {
<div className="bg-default flex h-screen">
{showContent ? (
<div className="flex w-full items-center justify-center p-4">
<div className="bg-default border-subtle m-auto flex max-w-[43em] flex-col items-center justify-center gap-4 overflow-auto rounded border p-4 md:p-10">
<div className="bg-default border-subtle m-auto flex w-full max-w-[43em] flex-col items-center justify-center gap-4 overflow-auto rounded border p-4 md:p-10">
{!props.lightningAddress ? (
<>
<p className="text-default">
Expand Down
2 changes: 2 additions & 0 deletions packages/app-store/apps.browser.generated.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const EventTypeAddonMap = {
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")),
matomo: dynamic(() => import("./matomo/components/EventTypeAppCardInterface")),
metapixel: dynamic(() => import("./metapixel/components/EventTypeAppCardInterface")),
Expand Down Expand Up @@ -56,6 +57,7 @@ export const EventTypeSettingsMap = {
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")),
Expand Down
2 changes: 2 additions & 0 deletions packages/app-store/apps.keys-schemas.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ 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 intercom_zod_ts } from "./intercom/zod";
import { appKeysSchema as jelly_zod_ts } from "./jelly/zod";
Expand Down Expand Up @@ -61,6 +62,7 @@ export const appKeysSchemas = {
googlecalendar: googlecalendar_zod_ts,
googlevideo: googlevideo_zod_ts,
gtm: gtm_zod_ts,
hitpay: hitpay_zod_ts,
hubspot: hubspot_zod_ts,
intercom: intercom_zod_ts,
jelly: jelly_zod_ts,
Expand Down
2 changes: 2 additions & 0 deletions packages/app-store/apps.metadata.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ 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 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";
Expand Down Expand Up @@ -126,6 +127,7 @@ export const appStoreMetadata = {
googlecalendar: googlecalendar__metadata_ts,
googlevideo: googlevideo__metadata_ts,
gtm: gtm_config_json,
hitpay: hitpay_config_json,
"horizon-workrooms": horizon_workrooms_config_json,
hubspot: hubspot__metadata_ts,
huddle01video: huddle01video__metadata_ts,
Expand Down
2 changes: 2 additions & 0 deletions packages/app-store/apps.schemas.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ 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 intercom_zod_ts } from "./intercom/zod";
import { appDataSchema as jelly_zod_ts } from "./jelly/zod";
Expand Down Expand Up @@ -61,6 +62,7 @@ export const appDataSchemas = {
googlecalendar: googlecalendar_zod_ts,
googlevideo: googlevideo_zod_ts,
gtm: gtm_zod_ts,
hitpay: hitpay_zod_ts,
hubspot: hubspot_zod_ts,
intercom: intercom_zod_ts,
jelly: jelly_zod_ts,
Expand Down
1 change: 1 addition & 0 deletions packages/app-store/apps.server.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const apiHandlers = {
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"),
Expand Down
8 changes: 8 additions & 0 deletions packages/app-store/hitpay/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
items:
- 1.jpeg
- 2.jpeg
- 3.jpeg
---

{DESCRIPTION}
42 changes: 42 additions & 0 deletions packages/app-store/hitpay/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
The Cal.com Commercial License (EE) license (the “EE License”)
Copyright (c) 2020-present Cal.com, Inc

With regard to the Cal.com Software:

This software and associated documentation files (the "Software") may only be
used in production, if you (and any entity that you represent) have agreed to,
and are in compliance with, the Cal.com Subscription Terms available
at https://cal.com/terms (the “EE Terms”), or other agreements governing
the use of the Software, as mutually agreed by you and Cal.com, Inc ("Cal.com"),
and otherwise have a valid Cal.com Commercial License subscription ("EE Subscription")
for the correct number of hosts as defined in the EE Terms ("Hosts"). Subject to the foregoing sentence,
you are free to modify this Software and publish patches to the Software. You agree
that Cal.com and/or its licensors (as applicable) retain all right, title and interest in
and to all such modifications and/or patches, and all such modifications and/or
patches may only be used, copied, modified, displayed, distributed, or otherwise
exploited with a valid EE Subscription for the correct number of hosts.
Notwithstanding the foregoing, you may copy and modify the Software for development
and testing purposes, without requiring a subscription. You agree that Cal.com and/or
its licensors (as applicable) retain all right, title and interest in and to all such
modifications. You are not granted any other rights beyond what is expressly stated herein.
Subject to the foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,
and/or sell the Software.

This EE License applies only to the part of this Software that is not distributed under
the AGPLv3 license. Any part of this Software distributed under the MIT license or which
is served client-side as an image, font, cascading stylesheet (CSS), file which produces
or is compiled, arranged, augmented, or combined into client-side JavaScript, in whole or
in part, is copyrighted under the AGPLv3 license. The full text of this EE License shall
be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

For all third party components incorporated into the Cal.com Software, those
components are licensed under the original license provided by the owner of the
applicable component.
55 changes: 55 additions & 0 deletions packages/app-store/hitpay/api/add.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// import { createDefaultInstallation } from "@calcom/app-store/_utils/installation";
// import type { AppDeclarativeHandler } from "@calcom/types/AppHandler";
// import appConfig from "../config.json";
// const handler: AppDeclarativeHandler = {
// appType: appConfig.type,
// variant: appConfig.variant,
// slug: appConfig.slug,
// supportsMultipleInstalls: false,
// handlerType: "add",
// createCredential: ({ appType, user, slug, teamId }) =>
// createDefaultInstallation({ appType, user: user, slug, key: {}, teamId }),
// };
// export default handler;
import type { NextApiRequest, NextApiResponse } from "next";

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 appType = config.type;
try {
const alreadyInstalled = await prisma.credential.findFirst({
where: {
type: appType,
userId: req.session.user.id,
},
});
if (alreadyInstalled) {
throw new Error("Already installed");
}
const installation = await prisma.credential.create({
data: {
type: appType,
key: {},
userId: req.session.user.id,
appId: "hitpay",
},
});

if (!installation) {
throw new Error("Unable to create user credential for Alby");
}
} catch (error: unknown) {
if (error instanceof Error) {
return res.status(500).json({ message: error.message });
}
return res.status(500);
}

return res.status(200).json({ url: "/apps/hitpay/setup" });
}
39 changes: 39 additions & 0 deletions packages/app-store/hitpay/api/callback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// import type { Prisma } from "@prisma/client";
import type { NextApiRequest, NextApiResponse } from "next";
import { stringify } from "querystring";

import getInstalledAppPath from "../../_utils/getInstalledAppPath";
import { decodeOAuthState } from "../../_utils/oauth/decodeOAuthState";

function getReturnToValueFromQueryState(req: NextApiRequest) {
let returnTo = "";
try {
returnTo = JSON.parse(`${req.query.state}`).returnTo;
} catch (error) {
console.info("No 'returnTo' in req.query.state");
}
return returnTo;
}

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { code, error, error_description } = req.query;
console.log("hitpay callback query =>", req.query);
const state = decodeOAuthState(req);

if (error) {
// User cancels flow
if (error === "access_denied") {
state?.onErrorReturnTo ? res.redirect(state.onErrorReturnTo) : res.redirect("/apps/installed/payment");
}
const query = stringify({ error, error_description });
res.redirect(`/apps/installed?${query}`);
return;
}

if (!req.session?.user?.id) {
return res.status(401).json({ message: "You must be logged in to do this" });
}

const returnTo = getReturnToValueFromQueryState(req);
res.redirect(returnTo || getInstalledAppPath({ variant: "payment", slug: "hitpay" }));
}
1 change: 1 addition & 0 deletions packages/app-store/hitpay/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as add } from "./add";
61 changes: 61 additions & 0 deletions packages/app-store/hitpay/api/webhook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { NextApiRequest, NextApiResponse } from "next";
// import getRawBody from "raw-body";
import { z } from "zod";

import { IS_PRODUCTION } from "@calcom/lib/constants";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { HttpError as HttpCode } from "@calcom/lib/http-error";

// import prisma from "@calcom/prisma";

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 bodyRaw = await getRawBody(req);
const headers = req.headers;
const bodyAsString = bodyRaw.toString();
console.log("hitpay wehbook bodyRaw =>", bodyRaw);
console.log("hitpay webhook bodyAsString =>", bodyAsString);
} catch (_err) {
const err = getErrorFromUnknown(_err);
console.error(`Webhook Error: ${err.message}`);
return res.status(err.statusCode || 500).send({
message: err.message,
stack: IS_PRODUCTION ? undefined : err.stack,
});
}
}

const payerDataSchema = z
.object({
appId: z.string().optional(),
referenceId: z.string().optional(),
})
.optional();

const metadataSchema = z
.object({
payer_data: payerDataSchema,
})
.optional();

const eventSchema = z.object({
metadata: metadataSchema,
});

const webhookHeadersSchema = z
.object({
"svix-id": z.string(),
"svix-timestamp": z.string(),
"svix-signature": z.string(),
})
.passthrough();
Empty file.
50 changes: 50 additions & 0 deletions packages/app-store/hitpay/components/EventTypeAppCardInterface.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { usePathname } from "next/navigation";
import { useState } 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 useIsAppEnabled from "../../_utils/useIsAppEnabled";
import type { appDataSchema } from "../zod";
import EventTypeAppSettingsInterface from "./EventTypeAppSettingsInterface";

const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
app,
eventType,
eventTypeFormMetadata,
}) {
const { t } = useLocale();
const pathname = usePathname();
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
const { enabled, updateEnabled } = useIsAppEnabled(app);
const otherPaymentAppEnabled = checkForMultiplePaymentApps(eventTypeFormMetadata);
const [requirePayment, setRequirePayment] = useState(getAppData("enabled"));
const shouldDisableSwitch = !requirePayment && otherPaymentAppEnabled;

return (
<AppCard
returnTo={`${WEBAPP_URL}${pathname}?tabName=apps`}
app={app}
switchChecked={enabled}
switchOnClick={(e) => {
updateEnabled(e);
}}
teamId={eventType.team?.id || undefined}
disableSwitch={shouldDisableSwitch}
switchTooltip={shouldDisableSwitch ? t("other_payment_app_enabled") : undefined}>
<EventTypeAppSettingsInterface
eventType={eventType}
slug={app.slug}
disabled={disabled}
getAppData={getAppData}
setAppData={setAppData}
/>
</AppCard>
);
};

export default EventTypeAppCard;
Loading

0 comments on commit d49034b

Please sign in to comment.