chore: Add logging eventId from Stripe webhook (#26623)
* Add loggin * fix: use established logger instead of console.log for Stripe webhook eventId Co-Authored-By: unknown <> * fix: remove sensitive stripe-signature header from error logs Co-Authored-By: unknown <> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
unknown
Devin AI
parent
c5cb2edd65
commit
d5c2212c41
@@ -8,8 +8,13 @@ import {
|
||||
eventTypeMetaDataSchemaWithTypedApps,
|
||||
eventTypeAppMetadataOptionalSchema,
|
||||
} from "@calcom/app-store/zod-utils";
|
||||
import { sendAttendeeRequestEmailAndSMS, sendOrganizerRequestEmail } from "@calcom/emails/email-manager";
|
||||
import EventManager, { placeholderCreatedEvent } from "@calcom/features/bookings/lib/EventManager";
|
||||
import {
|
||||
sendAttendeeRequestEmailAndSMS,
|
||||
sendOrganizerRequestEmail,
|
||||
} from "@calcom/emails/email-manager";
|
||||
import EventManager, {
|
||||
placeholderCreatedEvent,
|
||||
} from "@calcom/features/bookings/lib/EventManager";
|
||||
import { doesBookingRequireConfirmation } from "@calcom/features/bookings/lib/doesBookingRequireConfirmation";
|
||||
import { getAllCredentialsIncludeServiceAccountKey } from "@calcom/features/bookings/lib/getAllCredentialsForUsersOnEvent/getAllCredentials";
|
||||
import { handleConfirmation } from "@calcom/features/bookings/lib/handleConfirmation";
|
||||
@@ -36,7 +41,10 @@ export const config = {
|
||||
},
|
||||
};
|
||||
|
||||
export async function handleStripePaymentSuccess(event: Stripe.Event, traceContext: TraceContext) {
|
||||
export async function handleStripePaymentSuccess(
|
||||
event: Stripe.Event,
|
||||
traceContext: TraceContext
|
||||
) {
|
||||
const paymentIntent = event.data.object as Stripe.PaymentIntent;
|
||||
const payment = await prisma.payment.findFirst({
|
||||
where: {
|
||||
@@ -49,10 +57,15 @@ export async function handleStripePaymentSuccess(event: Stripe.Event, traceConte
|
||||
});
|
||||
|
||||
if (!payment?.bookingId) {
|
||||
log.error("Stripe: Payment Not Found", safeStringify(paymentIntent), safeStringify(payment));
|
||||
log.error(
|
||||
"Stripe: Payment Not Found",
|
||||
safeStringify(paymentIntent),
|
||||
safeStringify(payment)
|
||||
);
|
||||
throw new HttpCode({ statusCode: 204, message: "Payment not found" });
|
||||
}
|
||||
if (!payment?.bookingId) throw new HttpCode({ statusCode: 204, message: "Payment not found" });
|
||||
if (!payment?.bookingId)
|
||||
throw new HttpCode({ statusCode: 204, message: "Payment not found" });
|
||||
|
||||
await handlePaymentSuccess({
|
||||
paymentId: payment.id,
|
||||
@@ -62,7 +75,10 @@ export async function handleStripePaymentSuccess(event: Stripe.Event, traceConte
|
||||
});
|
||||
}
|
||||
|
||||
const handleSetupSuccess = async (event: Stripe.Event, traceContext: TraceContext) => {
|
||||
const handleSetupSuccess = async (
|
||||
event: Stripe.Event,
|
||||
traceContext: TraceContext
|
||||
) => {
|
||||
const setupIntent = event.data.object as Stripe.SetupIntent;
|
||||
const payment = await prisma.payment.findFirst({
|
||||
where: {
|
||||
@@ -70,7 +86,8 @@ const handleSetupSuccess = async (event: Stripe.Event, traceContext: TraceContex
|
||||
},
|
||||
});
|
||||
|
||||
if (!payment?.data || !payment?.id) throw new HttpCode({ statusCode: 204, message: "Payment not found" });
|
||||
if (!payment?.data || !payment?.id)
|
||||
throw new HttpCode({ statusCode: 204, message: "Payment not found" });
|
||||
|
||||
const { booking, user, evt, eventType } = await getBooking(payment.bookingId);
|
||||
|
||||
@@ -91,7 +108,9 @@ const handleSetupSuccess = async (event: Stripe.Event, traceContext: TraceContex
|
||||
},
|
||||
});
|
||||
|
||||
const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventType?.metadata);
|
||||
const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(
|
||||
eventType?.metadata
|
||||
);
|
||||
const allCredentials = await getAllCredentialsIncludeServiceAccountKey(user, {
|
||||
...booking.eventType,
|
||||
metadata,
|
||||
@@ -101,11 +120,15 @@ const handleSetupSuccess = async (event: Stripe.Event, traceContext: TraceContex
|
||||
const platformOAuthClient = user.isPlatformManaged
|
||||
? await platformOAuthClientRepository.getByUserId(user.id)
|
||||
: null;
|
||||
const areCalendarEventsEnabled = platformOAuthClient?.areCalendarEventsEnabled ?? true;
|
||||
const areCalendarEventsEnabled =
|
||||
platformOAuthClient?.areCalendarEventsEnabled ?? true;
|
||||
const areEmailsEnabled = platformOAuthClient?.areEmailsEnabled ?? true;
|
||||
|
||||
if (!requiresConfirmation) {
|
||||
const eventManager = new EventManager({ ...user, credentials: allCredentials }, metadata?.apps);
|
||||
const eventManager = new EventManager(
|
||||
{ ...user, credentials: allCredentials },
|
||||
metadata?.apps
|
||||
);
|
||||
const scheduleResult = areCalendarEventsEnabled
|
||||
? await eventManager.create(evt)
|
||||
: placeholderCreatedEvent;
|
||||
@@ -142,18 +165,27 @@ const handleSetupSuccess = async (event: Stripe.Event, traceContext: TraceContex
|
||||
bookingId: booking.id,
|
||||
booking,
|
||||
paid: true,
|
||||
platformClientParams: platformOAuthClient ? getPlatformParams(platformOAuthClient) : undefined,
|
||||
platformClientParams: platformOAuthClient
|
||||
? getPlatformParams(platformOAuthClient)
|
||||
: undefined,
|
||||
traceContext: updatedTraceContext,
|
||||
actionSource: "WEBHOOK",
|
||||
actor,
|
||||
});
|
||||
} else if (areEmailsEnabled) {
|
||||
await sendOrganizerRequestEmail({ ...evt }, eventType.metadata);
|
||||
await sendAttendeeRequestEmailAndSMS({ ...evt }, evt.attendees[0], eventType.metadata);
|
||||
await sendAttendeeRequestEmailAndSMS(
|
||||
{ ...evt },
|
||||
evt.attendees[0],
|
||||
eventType.metadata
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type WebhookHandler = (event: Stripe.Event, traceContext: TraceContext) => Promise<void>;
|
||||
type WebhookHandler = (
|
||||
event: Stripe.Event,
|
||||
traceContext: TraceContext
|
||||
) => Promise<void>;
|
||||
|
||||
const webhookHandlers: Record<string, WebhookHandler | undefined> = {
|
||||
"payment_intent.succeeded": handleStripePaymentSuccess,
|
||||
@@ -165,23 +197,54 @@ const webhookHandlers: Record<string, WebhookHandler | undefined> = {
|
||||
* We need to create a PaymentManager in `@calcom/lib`
|
||||
* to prevent circular dependencies on App Store migration
|
||||
*/
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
try {
|
||||
if (req.method !== "POST") {
|
||||
throw new HttpCode({ statusCode: 405, message: "Method Not Allowed" });
|
||||
}
|
||||
const sig = req.headers["stripe-signature"];
|
||||
if (!sig) {
|
||||
throw new HttpCode({ statusCode: 400, message: "Missing stripe-signature" });
|
||||
throw new HttpCode({
|
||||
statusCode: 400,
|
||||
message: "Missing stripe-signature",
|
||||
});
|
||||
}
|
||||
|
||||
if (!process.env.STRIPE_WEBHOOK_SECRET) {
|
||||
throw new HttpCode({ statusCode: 500, message: "Missing process.env.STRIPE_WEBHOOK_SECRET" });
|
||||
throw new HttpCode({
|
||||
statusCode: 500,
|
||||
message: "Missing process.env.STRIPE_WEBHOOK_SECRET",
|
||||
});
|
||||
}
|
||||
const requestBuffer = await buffer(req);
|
||||
const payload = requestBuffer.toString();
|
||||
|
||||
const event = stripe.webhooks.constructEvent(payload, sig, process.env.STRIPE_WEBHOOK_SECRET);
|
||||
let stripeEventId: string | undefined;
|
||||
try {
|
||||
const parsedPayload = JSON.parse(payload);
|
||||
stripeEventId = parsedPayload?.id;
|
||||
log.debug("Stripe webhook eventId", { stripeEventId });
|
||||
} catch {
|
||||
// Ignore parse errors, this is just for debugging
|
||||
}
|
||||
|
||||
let event: Stripe.Event;
|
||||
try {
|
||||
event = stripe.webhooks.constructEvent(
|
||||
payload,
|
||||
sig,
|
||||
process.env.STRIPE_WEBHOOK_SECRET
|
||||
);
|
||||
} catch (err) {
|
||||
log.error("Stripe webhook signature verification failed", {
|
||||
stripeEventId,
|
||||
error: err instanceof Error ? err.message : "Unknown error",
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
|
||||
const traceContext = distributedTracing.createTrace(`stripe_webhook`, {
|
||||
meta: {
|
||||
@@ -193,7 +256,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
// bypassing this validation for e2e tests
|
||||
// in order to successfully confirm the payment
|
||||
if (!event.account && !process.env.NEXT_PUBLIC_IS_E2E) {
|
||||
throw new HttpCode({ statusCode: 202, message: "Incoming connected account" });
|
||||
throw new HttpCode({
|
||||
statusCode: 202,
|
||||
message: "Incoming connected account",
|
||||
});
|
||||
}
|
||||
|
||||
const handler = webhookHandlers[event.type];
|
||||
|
||||
Reference in New Issue
Block a user