feat: add paid webhook (#8721)

* feat: add BOOKING_PAID on enums

* feat: trigger the BOOKING_PAID webhook

* refactor: create payment variable

* feat: add REJECTED and REQUESTED on enums

* feat: create migration to booking werbhooks

* Update packages/features/webhooks/lib/constants.ts

Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>

* Update packages/prisma/schema.prisma

Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>

* Update packages/prisma/migrations/20230513235419_add_booking_webhooks/migration.sql

Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>

---------

Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
This commit is contained in:
Wesley 2023-06-13 11:57:59 -03:00 committed by GitHub
parent 4d30264950
commit 895b3ca7c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 6 deletions

View File

@ -7,6 +7,7 @@ export const WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP = {
WebhookTriggerEvents.BOOKING_CANCELLED,
WebhookTriggerEvents.BOOKING_CREATED,
WebhookTriggerEvents.BOOKING_RESCHEDULED,
WebhookTriggerEvents.BOOKING_PAID,
WebhookTriggerEvents.MEETING_ENDED,
WebhookTriggerEvents.BOOKING_REQUESTED,
WebhookTriggerEvents.BOOKING_REJECTED,

View File

@ -0,0 +1,9 @@
-- AlterEnum
-- This migration adds more than one value to an enum.
-- With PostgreSQL versions 11 and earlier, this is not possible
-- in a single migration. This can be worked around by creating
-- multiple migrations, each migration adding only one value to
-- the enum.
ALTER TYPE "WebhookTriggerEvents" ADD VALUE 'BOOKING_PAID';

View File

@ -498,6 +498,7 @@ enum PaymentOption {
enum WebhookTriggerEvents {
BOOKING_CREATED
BOOKING_PAID
BOOKING_RESCHEDULED
BOOKING_REQUESTED
BOOKING_CANCELLED

View File

@ -3,7 +3,9 @@ import { z } from "zod";
import appStore from "@calcom/app-store";
import dayjs from "@calcom/dayjs";
import { sendNoShowFeeChargedEmail } from "@calcom/emails";
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
import { getTranslation } from "@calcom/lib/server/i18n";
import sendPayload from "@calcom/lib/server/webhooks/sendPayload";
import type { CalendarEvent } from "@calcom/types/Calendar";
import { TRPCError } from "@trpc/server";
@ -33,11 +35,13 @@ export const paymentsRouter = router({
},
});
const payment = booking.payment[0];
if (!booking) {
throw new Error("Booking not found");
}
if (booking.payment[0].success) {
if (payment.success) {
throw new TRPCError({
code: "BAD_REQUEST",
message: `The no show fee for ${booking.id} has already been charged.`,
@ -77,16 +81,16 @@ export const paymentsRouter = router({
},
attendees: attendeesList,
paymentInfo: {
amount: booking.payment[0].amount,
currency: booking.payment[0].currency,
paymentOption: booking.payment[0].paymentOption,
amount: payment.amount,
currency: payment.currency,
paymentOption: payment.paymentOption,
},
};
const paymentCredential = await prisma.credential.findFirst({
where: {
userId: ctx.user.id,
appId: booking.payment[0].appId,
appId: payment.appId,
},
include: {
app: true,
@ -107,12 +111,32 @@ export const paymentsRouter = router({
const paymentInstance = new PaymentService(paymentCredential);
try {
const paymentData = await paymentInstance.chargeCard(booking.payment[0]);
const paymentData = await paymentInstance.chargeCard(payment);
if (!paymentData) {
throw new TRPCError({ code: "NOT_FOUND", message: `Could not generate payment data` });
}
const subscriberOptions = {
userId: ctx.user.id || 0,
eventTypeId: booking.eventTypeId || 0,
triggerEvent: WebhookTriggerEvents.BOOKING_PAID,
};
const subscribers = await getWebhooks(subscriberOptions);
await Promise.all(
subscribers.map(async (subscriber) => {
sendPayload(subscriber.secret, WebhookTriggerEvents.BOOKING_PAID, {
...evt,
bookingId: booking.id,
paymentId: payment.id,
paymentData,
eventTypeId: subscriberOptions.eventTypeId,
});
})
);
await sendNoShowFeeChargedEmail(attendeesListPromises[0], evt);
return paymentData;