"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const Stripe = require("stripe");
const uuid = require("uuid");
const z = require("zod");
const errorCodes = require("./errorCodes-ifTrQJQk.cjs");
const CalEventParser = require("./CalEventParser-RR7nWuhu.cjs");
const index = require("./i18n-HHdcQbuJ.cjs");
const zodUtils = require("./zod-utils-WM6UHWGs.cjs");
const querystring = require("querystring");
require("@stripe/stripe-js/pure.js");
const customer = require("./customer-4xVYl5D0.cjs");
const index$1 = require("./email-manager-NqFEofh9.cjs");
function createPaymentLink(opts) {
  const { paymentUid, name, email, date, absolute = true } = opts;
  let link = "";
  if (absolute)
    link = index.WEBSITE_URL;
  const query = querystring.stringify({ date, name, email });
  return `${link}/payment/${paymentUid}?${query}`;
}
process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY || "";
const log = index.logger.getSubLogger({ prefix: ["payment-service:stripe"] });
const stripeCredentialKeysSchema = z.object({
  stripe_user_id: z.string(),
  default_currency: z.string(),
  stripe_publishable_key: z.string()
});
class PaymentService {
  constructor(credentials) {
    const keyParsing = stripeCredentialKeysSchema.safeParse(credentials.key);
    if (keyParsing.success) {
      this.credentials = keyParsing.data;
    } else {
      this.credentials = null;
    }
    this.stripe = new Stripe(process.env.STRIPE_PRIVATE_KEY || "", {
      apiVersion: "2020-08-27"
    });
  }
  async getPayment(where) {
    const payment = await index.prisma.payment.findFirst({ where });
    if (!payment)
      throw new Error("Payment not found");
    if (!payment.externalId)
      throw new Error("Payment externalId not found");
    return { ...payment, externalId: payment.externalId };
  }
  /* This method is for creating charges at the time of booking */
  async create(payment, bookingId, userId, username, bookerName, paymentOption, bookerEmail, bookerPhoneNumber, eventTitle, bookingTitle) {
    try {
      if (zodUtils.paymentOptionEnum$1.parse(paymentOption) !== "ON_BOOKING") {
        throw new Error("Payment option is not compatible with create method");
      }
      if (!this.credentials) {
        throw new Error("Stripe credentials not found");
      }
      const customer$1 = await customer.retrieveOrCreateStripeCustomerByEmail(
        this.credentials.stripe_user_id,
        bookerEmail,
        bookerPhoneNumber
      );
      const params = {
        amount: payment.amount,
        currency: payment.currency,
        customer: customer$1.id,
        automatic_payment_methods: {
          enabled: true
        },
        metadata: {
          identifier: "cal.com",
          bookingId,
          calAccountId: userId,
          calUsername: username,
          bookerName,
          bookerEmail,
          bookerPhoneNumber: bookerPhoneNumber ?? null,
          eventTitle: eventTitle || "",
          bookingTitle: bookingTitle || ""
        }
      };
      const paymentIntent = await this.stripe.paymentIntents.create(params, {
        stripeAccount: this.credentials.stripe_user_id
      });
      const paymentData = await index.prisma.payment.create({
        data: {
          uid: uuid.v4(),
          app: {
            connect: {
              slug: "stripe"
            }
          },
          booking: {
            connect: {
              id: bookingId
            }
          },
          amount: payment.amount,
          currency: payment.currency,
          externalId: paymentIntent.id,
          data: Object.assign({}, paymentIntent, {
            stripe_publishable_key: this.credentials.stripe_publishable_key,
            stripeAccount: this.credentials.stripe_user_id
          }),
          fee: 0,
          refunded: false,
          success: false,
          paymentOption: paymentOption || "ON_BOOKING"
        }
      });
      if (!paymentData) {
        throw new Error();
      }
      return paymentData;
    } catch (error) {
      log.error("Stripe: Payment could not be created", bookingId, index.safeStringify(error));
      throw new Error("payment_not_created_error");
    }
  }
  async collectCard(payment, bookingId, paymentOption, bookerEmail, bookerPhoneNumber) {
    try {
      if (!this.credentials) {
        throw new Error("Stripe credentials not found");
      }
      if (zodUtils.paymentOptionEnum$1.parse(paymentOption) !== "HOLD") {
        throw new Error("Payment option is not compatible with create method");
      }
      const customer$1 = await customer.retrieveOrCreateStripeCustomerByEmail(
        this.credentials.stripe_user_id,
        bookerEmail,
        bookerPhoneNumber
      );
      const params = {
        customer: customer$1.id,
        payment_method_types: ["card"],
        metadata: {
          bookingId,
          bookerPhoneNumber: bookerPhoneNumber ?? null
        }
      };
      const setupIntent = await this.stripe.setupIntents.create(params, {
        stripeAccount: this.credentials.stripe_user_id
      });
      const paymentData = await index.prisma.payment.create({
        data: {
          uid: uuid.v4(),
          app: {
            connect: {
              slug: "stripe"
            }
          },
          booking: {
            connect: {
              id: bookingId
            }
          },
          amount: payment.amount,
          currency: payment.currency,
          externalId: setupIntent.id,
          data: Object.assign(
            {},
            {
              setupIntent,
              stripe_publishable_key: this.credentials.stripe_publishable_key,
              stripeAccount: this.credentials.stripe_user_id
            }
          ),
          fee: 0,
          refunded: false,
          success: false,
          paymentOption: paymentOption || "ON_BOOKING"
        }
      });
      return paymentData;
    } catch (error) {
      log.error(
        "Stripe: Payment method could not be collected for bookingId",
        bookingId,
        index.safeStringify(error)
      );
      throw new Error("Stripe: Payment method could not be collected");
    }
  }
  async chargeCard(payment, _bookingId) {
    try {
      if (!this.credentials) {
        throw new Error("Stripe credentials not found");
      }
      const stripeAppKeys = await index.prisma.app.findFirst({
        select: {
          keys: true
        },
        where: {
          slug: "stripe"
        }
      });
      const paymentObject = payment.data;
      const setupIntent = paymentObject.setupIntent;
      const customer2 = await this.stripe.customers.retrieve(setupIntent.customer, {
        stripeAccount: this.credentials.stripe_user_id
      });
      const paymentMethod = await this.stripe.paymentMethods.retrieve(setupIntent.payment_method, {
        stripeAccount: this.credentials.stripe_user_id
      });
      if (!customer2) {
        throw new Error(`Stripe customer does not exist for setupIntent ${setupIntent.id}`);
      }
      if (!paymentMethod) {
        throw new Error(`Stripe paymentMethod does not exist for setupIntent ${setupIntent.id}`);
      }
      const params = {
        amount: payment.amount,
        currency: payment.currency,
        customer: setupIntent.customer,
        payment_method: setupIntent.payment_method,
        off_session: true,
        confirm: true
      };
      const paymentIntent = await this.stripe.paymentIntents.create(params, {
        stripeAccount: this.credentials.stripe_user_id
      });
      const paymentData = await index.prisma.payment.update({
        where: {
          id: payment.id
        },
        data: {
          success: true,
          data: {
            ...paymentObject,
            paymentIntent
          }
        }
      });
      if (!paymentData) {
        throw new Error();
      }
      return paymentData;
    } catch (error) {
      log.error("Stripe: Could not charge card for payment", _bookingId, index.safeStringify(error));
      const errorMappings = {
        "your card was declined": "your_card_was_declined",
        "your card does not support this type of purchase": "your_card_does_not_support_this_type_of_purchase",
        "amount must convert to at least": "amount_must_convert_to_at_least"
      };
      let userMessage = "could_not_charge_card";
      if (error instanceof Error) {
        const errorMessage = error.message.toLowerCase();
        for (const [key, message] of Object.entries(errorMappings)) {
          if (errorMessage.includes(key)) {
            userMessage = message;
            break;
          }
        }
      }
      throw new CalEventParser.ErrorWithCode(errorCodes.ErrorCode.ChargeCardFailure, userMessage);
    }
  }
  async update() {
    throw new Error("Method not implemented.");
  }
  async refund(paymentId) {
    try {
      const payment = await this.getPayment({
        id: paymentId,
        success: true,
        refunded: false
      });
      const refund = await this.stripe.refunds.create(
        {
          payment_intent: payment.externalId
        },
        { stripeAccount: payment.data["stripeAccount"] }
      );
      if (!refund || refund.status === "failed") {
        throw new Error("Refund failed");
      }
      const updatedPayment = await index.prisma.payment.update({
        where: {
          id: payment.id
        },
        data: {
          refunded: true
        }
      });
      return updatedPayment;
    } catch (e) {
      const err = CalEventParser.getErrorFromUnknown(e);
      throw err;
    }
  }
  async afterPayment(event, booking, paymentData, eventTypeMetadata) {
    const attendeesToEmail = event.attendeeSeatId ? event.attendees.filter((attendee) => attendee.bookingSeat?.referenceUid === event.attendeeSeatId) : event.attendees;
    await index$1.sendAwaitingPaymentEmailAndSMS(
      {
        ...event,
        attendees: attendeesToEmail,
        paymentInfo: {
          link: createPaymentLink({
            paymentUid: paymentData.uid,
            name: booking.user?.name,
            email: booking.user?.email,
            date: booking.startTime.toISOString()
          }),
          paymentOption: paymentData.paymentOption || "ON_BOOKING",
          amount: paymentData.amount,
          currency: paymentData.currency
        }
      },
      eventTypeMetadata
    );
  }
  async deletePayment(paymentId) {
    try {
      const payment = await this.getPayment({
        id: paymentId
      });
      const stripeAccount = payment.data.stripeAccount;
      if (!stripeAccount) {
        throw new Error("Stripe account not found");
      }
      const sessions = await this.stripe.checkout.sessions.list(
        {
          payment_intent: payment.externalId
        },
        { stripeAccount }
      );
      for (const session of sessions.data) {
        await this.stripe.checkout.sessions.expire(session.id, { stripeAccount });
      }
      await this.stripe.paymentIntents.cancel(payment.externalId, { stripeAccount });
      return true;
    } catch (e) {
      log.error("Stripe: Unable to delete Payment in stripe of paymentId", paymentId, index.safeStringify(e));
      return false;
    }
  }
  getPaymentPaidStatus() {
    throw new Error("Method not implemented.");
  }
  getPaymentDetails() {
    throw new Error("Method not implemented.");
  }
  isSetupAlready() {
    return !!this.credentials;
  }
}
exports.PaymentService = PaymentService;
exports.stripeCredentialKeysSchema = stripeCredentialKeysSchema;
