Skip to content
Merged
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
3 changes: 2 additions & 1 deletion apps/api/test/lib/bookings/_post.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { describe, expect, test, vi } from "vitest";

import dayjs from "@calcom/dayjs";
import sendPayload from "@calcom/features/webhooks/lib/sendPayload";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { buildBooking, buildEventType, buildWebhook } from "@calcom/lib/test/builder";
import prisma from "@calcom/prisma";

Expand Down Expand Up @@ -148,7 +149,7 @@ describe.skipIf(true)("POST /api/bookings", () => {
expect(res._getStatusCode()).toBe(500);
expect(JSON.parse(res._getData())).toEqual(
expect.objectContaining({
message: "No available users found.",
message: ErrorCode.NoAvailableUsersFound,
})
);
});
Expand Down
10 changes: 10 additions & 0 deletions apps/web/public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@
"a_refund_failed": "A refund failed",
"awaiting_payment_subject": "Awaiting Payment: {{title}} on {{date}}",
"meeting_awaiting_payment": "Your meeting is awaiting payment",
"payment_not_created_error": "Payment could not be created",
"couldnt_charge_card_error": "Could not charge card for Payment",
"no_available_users_found_error": "No available users found. Could you try another time slot?",
"request_body_end_time_internal_error": "Internal Error. Request body does not contain end time",
"create_calendar_event_error": "Unable to create Calendar event in Organizer's calendar",
"update_calendar_event_error": "Unable to update Calendar event.",
"delete_calendar_event_error": "Unable to delete Calendar event.",
"already_signed_up_for_this_booking_error": "You are already signed up for this booking.",
"hosts_unavailable_for_booking": "Some of the hosts are unavailable for booking.",
"help": "Help",
"price": "Price",
"paid": "Paid",
Expand Down Expand Up @@ -1363,6 +1372,7 @@
"event_name_info": "The event type name",
"event_date_info": "The event date",
"event_time_info": "The event start time",
"event_type_not_found": "EventType not Found",
"location_info": "The location of the event",
"additional_notes_info": "The additional notes of booking",
"attendee_name_info": "The person booking's name",
Expand Down
10 changes: 7 additions & 3 deletions packages/app-store/alby/lib/PaymentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ import type { Booking, Payment, PaymentOption, Prisma } from "@prisma/client";
import { v4 as uuidv4 } from "uuid";
import type z from "zod";

import { ErrorCode } from "@calcom/lib/errorCodes";
import logger from "@calcom/lib/logger";
import prisma from "@calcom/prisma";
import type { CalendarEvent } from "@calcom/types/Calendar";
import type { IAbstractPaymentService } from "@calcom/types/PaymentService";

import { albyCredentialKeysSchema } from "./albyCredentialKeysSchema";

const log = logger.getSubLogger({ prefix: ["payment-service:alby"] });

export class PaymentService implements IAbstractPaymentService {
private credentials: z.infer<typeof albyCredentialKeysSchema> | null;

Expand Down Expand Up @@ -36,7 +40,7 @@ export class PaymentService implements IAbstractPaymentService {
},
});
if (!booking || !this.credentials?.account_lightning_address) {
throw new Error();
throw new Error("Alby: Booking or Lightning address not found");
}

const uid = uuidv4();
Expand Down Expand Up @@ -80,8 +84,8 @@ export class PaymentService implements IAbstractPaymentService {
}
return paymentData;
} catch (error) {
console.error(error);
throw new Error("Payment could not be created");
log.error("Alby: Payment could not be created", bookingId);
throw new Error(ErrorCode.PaymentCreationFailure);
}
}
async update(): Promise<Payment> {
Expand Down
12 changes: 8 additions & 4 deletions packages/app-store/paypal/lib/PaymentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import z from "zod";

import Paypal from "@calcom/app-store/paypal/lib/Paypal";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { ErrorCode } from "@calcom/lib/errorCodes";
import logger from "@calcom/lib/logger";
import prisma from "@calcom/prisma";
import type { CalendarEvent } from "@calcom/types/Calendar";
import type { IAbstractPaymentService } from "@calcom/types/PaymentService";

import { paymentOptionEnum } from "../zod";

const log = logger.getSubLogger({ prefix: ["payment-service:paypal"] });

export const paypalCredentialKeysSchema = z.object({
client_id: z.string(),
secret_key: z.string(),
Expand Down Expand Up @@ -87,8 +91,8 @@ export class PaymentService implements IAbstractPaymentService {
}
return paymentData;
} catch (error) {
console.error(error);
throw new Error("Payment could not be created");
log.error("Paypal: Payment could not be created for bookingId", bookingId);
throw new Error(ErrorCode.PaymentCreationFailure);
}
}
async update(): Promise<Payment> {
Expand Down Expand Up @@ -166,8 +170,8 @@ export class PaymentService implements IAbstractPaymentService {
}
return paymentData;
} catch (error) {
console.error(error);
throw new Error("Payment could not be created");
log.error("Paypal: Payment method could not be collected for bookingId", bookingId);
throw new Error("Paypal: Payment method could not be collected");
}
}
chargeCard(
Expand Down
21 changes: 15 additions & 6 deletions packages/app-store/stripepayment/lib/PaymentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { v4 as uuidv4 } from "uuid";
import z from "zod";

import { sendAwaitingPaymentEmail } from "@calcom/emails";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import logger from "@calcom/lib/logger";
import prisma from "@calcom/prisma";
import type { CalendarEvent } from "@calcom/types/Calendar";
import type { IAbstractPaymentService } from "@calcom/types/PaymentService";
Expand All @@ -14,6 +16,8 @@ import { createPaymentLink } from "./client";
import { retrieveOrCreateStripeCustomerByEmail } from "./customer";
import type { StripePaymentData, StripeSetupIntentData } from "./server";

const log = logger.getSubLogger({ prefix: ["payment-service:stripe"] });

export const stripeCredentialKeysSchema = z.object({
stripe_user_id: z.string(),
default_currency: z.string(),
Expand Down Expand Up @@ -129,7 +133,8 @@ export class PaymentService implements IAbstractPaymentService {
return paymentData;
} catch (error) {
console.error(`Payment could not be created for bookingId ${bookingId}`, error);
throw new Error("Payment could not be created");
log.error("Stripe: Payment could not be created", bookingId, JSON.stringify(error));
throw new Error("payment_not_created_error");
}
}

Expand Down Expand Up @@ -199,8 +204,12 @@ export class PaymentService implements IAbstractPaymentService {

return paymentData;
} catch (error) {
console.error(`Payment method could not be collected for bookingId ${bookingId}`, error);
throw new Error("Payment could not be created");
log.error(
"Stripe: Payment method could not be collected for bookingId",
bookingId,
JSON.stringify(error)
);
throw new Error("Stripe: Payment method could not be collected");
}
}

Expand Down Expand Up @@ -277,8 +286,8 @@ export class PaymentService implements IAbstractPaymentService {

return paymentData;
} catch (error) {
console.error(`Could not charge card for payment ${payment.id}`, error);
throw new Error("Payment could not be created");
log.error("Stripe: Could not charge card for payment", _bookingId, JSON.stringify(error));
throw new Error(ErrorCode.ChargeCardFailure);
}
}

Expand Down Expand Up @@ -369,7 +378,7 @@ export class PaymentService implements IAbstractPaymentService {
await this.stripe.paymentIntents.cancel(payment.externalId, { stripeAccount });
return true;
} catch (e) {
console.error(e);
log.error("Stripe: Unable to delete Payment in stripe of paymentId", paymentId, JSON.stringify(e));
return false;
}
}
Expand Down
18 changes: 16 additions & 2 deletions packages/core/CalendarManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export const getBusyCalendarTimes = async (
selectedCalendars: SelectedCalendar[]
) => {
let results: EventBusyDate[][] = [];
const months = getMonths(dateFrom, dateTo);
// const months = getMonths(dateFrom, dateTo);
try {
// Subtract 11 hours from the start date to avoid problems in UTC- time zones.
const startDate = dayjs(dateFrom).subtract(11, "hours").format();
Expand Down Expand Up @@ -348,6 +348,19 @@ export const updateEvent = async (
})
: undefined;

if (!updatedResult) {
logger.error(
"updateEvent failed",
safeStringify({
success,
bookingRefUid,
credential: getPiiFreeCredential(credential),
originalEvent: getPiiFreeCalendarEvent(calEvent),
calError,
})
);
}

if (Array.isArray(updatedResult)) {
calWarnings = updatedResult.flatMap((res) => res.additionalInfo?.calWarnings ?? []);
} else {
Expand Down Expand Up @@ -388,10 +401,11 @@ export const deleteEvent = async ({
if (calendar) {
return calendar.deleteEvent(bookingRefUid, event, externalCalendarId);
} else {
log.warn(
log.error(
"Could not do deleteEvent - No calendar adapter found",
safeStringify({
credential: getPiiFreeCredential(credential),
event,
})
);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/getUserAvailability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export const getUserAvailability = async function getUsersWorkingHoursLifeTheUni

const user = initialData?.user || (await getUser(where));

if (!user) throw new HttpError({ statusCode: 404, message: "No user found" });
if (!user) throw new HttpError({ statusCode: 404, message: "No user found in getUserAvailability" });
log.debug(
"getUserAvailability for user",
safeStringify({ user: { id: user.id }, slot: { dateFrom, dateTo } })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import { useBookingSuccessRedirect } from "@calcom/lib/bookingSuccessRedirect";
import { MINUTES_TO_BOOK } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
import { HttpError } from "@calcom/lib/http-error";
import { trpc } from "@calcom/trpc";
import { Alert, Button, EmptyScreen, Form, showToast } from "@calcom/ui";
import { Calendar } from "@calcom/ui/components/icon";
Expand Down Expand Up @@ -153,6 +152,7 @@ export const BookEventFormChild = ({
const verifiedEmail = useBookerStore((state) => state.verifiedEmail);
const setVerifiedEmail = useBookerStore((state) => state.setVerifiedEmail);
const bookingSuccessRedirect = useBookingSuccessRedirect();
const [responseVercelIdHeader, setResponseVercelIdHeader] = useState<string | null>(null);

const router = useRouter();
const { t, i18n } = useLocale();
Expand Down Expand Up @@ -220,7 +220,12 @@ export const BookEventFormChild = ({
booking: responseData,
});
},
onError: () => {
onError: (err, _, ctx) => {
// TODO:
// const vercelId = ctx?.meta?.headers?.get("x-vercel-id");
// if (vercelId) {
// setResponseVercelIdHeader(vercelId);
// }
errorRef && errorRef.current?.scrollIntoView({ behavior: "smooth" });
},
});
Expand Down Expand Up @@ -390,7 +395,8 @@ export const BookEventFormChild = ({
bookingForm.formState.errors["globalError"],
createBookingMutation,
createRecurringBookingMutation,
t
t,
responseVercelIdHeader
)}
/>
</div>
Expand Down Expand Up @@ -438,16 +444,19 @@ const getError = (
bookingMutation: UseMutationResult<any, any, any, any>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
recurringBookingMutation: UseMutationResult<any, any, any, any>,
t: TFunction
t: TFunction,
responseVercelIdHeader: string | null
) => {
if (globalError) return globalError.message;

const error = bookingMutation.error || recurringBookingMutation.error;

return error instanceof HttpError || error instanceof Error ? (
<>{t("can_you_try_again")}</>
return error.message ? (
<>
{responseVercelIdHeader ?? ""} {t(error.message)}
</>
) : (
"Unknown error"
<>{t("can_you_try_again")}</>
);
};

Expand Down
22 changes: 14 additions & 8 deletions packages/features/bookings/lib/handleNewBooking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { isValidPhoneNumber } from "libphonenumber-js";
import { cloneDeep } from "lodash";
import type { NextApiRequest } from "next";
import short, { uuid } from "short-uuid";
import type { Logger } from "tslog";
import { v5 as uuidv5 } from "uuid";
import z from "zod";

Expand Down Expand Up @@ -52,6 +53,7 @@ import { cancelScheduledJobs, scheduleTrigger } from "@calcom/features/webhooks/
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
import { getVideoCallUrlFromCalEvent } from "@calcom/lib/CalEventParser";
import { getDefaultEvent, getUsernameList } from "@calcom/lib/defaultEvents";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
Expand Down Expand Up @@ -362,7 +364,8 @@ async function ensureAvailableUsers(
eventType: Awaited<ReturnType<typeof getEventTypesFromDB>> & {
users: IsFixedAwareUser[];
},
input: { dateFrom: string; dateTo: string; timeZone: string; originalRescheduledBooking?: BookingType }
input: { dateFrom: string; dateTo: string; timeZone: string; originalRescheduledBooking?: BookingType },
loggerWithEventDetails: Logger<unknown>
) {
const availableUsers: IsFixedAwareUser[] = [];
const duration = dayjs(input.dateTo).diff(input.dateFrom, "minute");
Expand Down Expand Up @@ -414,7 +417,8 @@ async function ensureAvailableUsers(
}
}
if (!availableUsers.length) {
throw new Error("No available users found.");
loggerWithEventDetails.error(`No available users found.`);
throw new Error(ErrorCode.NoAvailableUsersFound);
}
return availableUsers;
}
Expand Down Expand Up @@ -537,7 +541,7 @@ async function getBookingData({
return true;
};
if (!reqBodyWithEnd(reqBody)) {
throw new Error("Internal Error.");
throw new Error(ErrorCode.RequestBodyWithouEnd);
}
// reqBody.end is no longer an optional property.
if ("customInputs" in reqBody) {
Expand Down Expand Up @@ -672,10 +676,11 @@ async function handler(

const fullName = getFullName(bookerName);

// Why are we only using "en" locale
const tGuests = await getTranslation("en", "common");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we only using "en" locale?


const dynamicUserList = Array.isArray(reqBody.user) ? reqBody.user : getUsernameList(reqBody.user);
if (!eventType) throw new HttpError({ statusCode: 404, message: "eventType.notFound" });
if (!eventType) throw new HttpError({ statusCode: 404, message: "event_type_not_found" });

const isTeamEventType =
!!eventType.schedulingType && ["COLLECTIVE", "ROUND_ROBIN"].includes(eventType.schedulingType);
Expand Down Expand Up @@ -916,7 +921,8 @@ async function handler(
dateTo: dayjs(reqBody.end).tz(reqBody.timeZone).format(),
timeZone: reqBody.timeZone,
originalRescheduledBooking,
}
},
loggerWithEventDetails
);

const luckyUsers: typeof users = [];
Expand Down Expand Up @@ -946,7 +952,7 @@ async function handler(
if (
availableUsers.filter((user) => user.isFixed).length !== users.filter((user) => user.isFixed).length
) {
throw new Error("Some of the hosts are unavailable for booking.");
throw new Error(ErrorCode.HostsUnavailableForBooking);
}
// Pushing fixed user before the luckyUser guarantees the (first) fixed user as the organizer.
users = [...availableUsers.filter((user) => user.isFixed), ...luckyUsers];
Expand Down Expand Up @@ -1264,7 +1270,7 @@ async function handler(
booking.attendees.find((attendee) => attendee.email === invitee[0].email) &&
dayjs.utc(booking.startTime).format() === evt.startTime
) {
throw new HttpError({ statusCode: 409, message: "Already signed up for this booking." });
throw new HttpError({ statusCode: 409, message: ErrorCode.AlreadySignedUpForBooking });
}

// There are two paths here, reschedule a booking with seats and booking seats without reschedule
Expand Down Expand Up @@ -2675,7 +2681,7 @@ const findBookingQuery = async (bookingId: number) => {

// This should never happen but it's just typescript safe
if (!foundBooking) {
throw new Error("Internal Error.");
throw new Error("Internal Error. Couldn't find booking");
}

// Don't leak any sensitive data
Expand Down
Loading