* feat: refund policies for payment apps * tests for payment refund --------- Co-authored-by: Peer Richelsen <peeroke@gmail.com>
87 lines
2.6 KiB
TypeScript
87 lines
2.6 KiB
TypeScript
import type { Payment, Prisma } from "@prisma/client";
|
|
|
|
import dayjs from "@calcom/dayjs";
|
|
import prisma from "@calcom/prisma";
|
|
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
|
|
|
import { getPaymentAppData } from "../getPaymentAppData";
|
|
import { handlePaymentRefund } from "./handlePaymentRefund";
|
|
import { RefundPolicy } from "./types";
|
|
|
|
export const processPaymentRefund = async ({
|
|
booking,
|
|
teamId,
|
|
}: {
|
|
booking: {
|
|
startTime: Date;
|
|
endTime: Date;
|
|
payment: Payment[];
|
|
eventType: {
|
|
owner?: {
|
|
id: number;
|
|
} | null;
|
|
metadata?: Prisma.JsonValue;
|
|
} | null;
|
|
};
|
|
teamId?: number | null;
|
|
}) => {
|
|
const { startTime, eventType, payment } = booking;
|
|
if (!teamId && !eventType?.owner) return;
|
|
|
|
const successPayment = payment.find((p) => p.success);
|
|
if (!successPayment) return;
|
|
|
|
const eventTypeMetadata = EventTypeMetaDataSchema.parse(eventType?.metadata);
|
|
const appData = getPaymentAppData({
|
|
currency: successPayment.currency,
|
|
metadata: eventTypeMetadata,
|
|
price: successPayment.amount,
|
|
});
|
|
|
|
if (!appData?.refundPolicy || appData.refundPolicy === RefundPolicy.NEVER) return;
|
|
|
|
const credentialWhereClause: Prisma.CredentialFindManyArgs["where"] = {
|
|
appId: successPayment.appId,
|
|
};
|
|
if (eventType?.owner) {
|
|
credentialWhereClause.userId = eventType.owner.id;
|
|
} else if (teamId) {
|
|
credentialWhereClause.teamId = teamId;
|
|
}
|
|
|
|
const paymentAppCredentials = await prisma.credential.findMany({
|
|
where: credentialWhereClause,
|
|
select: {
|
|
key: true,
|
|
appId: true,
|
|
app: {
|
|
select: {
|
|
categories: true,
|
|
dirName: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const paymentAppCredential = paymentAppCredentials.find((credential) => {
|
|
return credential.appId === successPayment.appId;
|
|
});
|
|
|
|
if (!paymentAppCredential) return;
|
|
|
|
const { refundPolicy, refundCountCalendarDays, refundDaysCount } = appData;
|
|
|
|
//refundDaysCount would always be present in case DAYS is selected, but adding it in AND jut for type safety
|
|
if (refundPolicy === RefundPolicy.DAYS && refundDaysCount) {
|
|
const refundDeadline =
|
|
refundCountCalendarDays === true
|
|
? dayjs(startTime).subtract(refundDaysCount, "days")
|
|
: // businessDaysSubtract exists on extended dayjs instance, but ts is messing up
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
//@ts-ignore
|
|
dayjs(startTime).businessDaysSubtract(refundDaysCount);
|
|
if (dayjs().isAfter(refundDeadline)) return;
|
|
}
|
|
await handlePaymentRefund(successPayment.id, paymentAppCredential);
|
|
};
|