"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const throwIfNotHaveAdminAccessToTeam = require("./throwIfNotHaveAdminAccessToTeam-YLldpFLn.cjs");
const index$2 = require("./orgDomains-W_rFTMBa.cjs");
const utils = require("./utils-gmlLPpGm.cjs");
const getRawBody = require("raw-body");
const z = require("zod");
const uuid = require("uuid");
const errors = require("./errors-fb9bpDY1.cjs");
const httpError = require("./http-error-mkY8Ke7V.cjs");
const handlePaymentSuccess = require("./handlePaymentSuccess-ZH5S8gzh.cjs");
async function handler$2(req, res) {
  if (!req.session?.user?.id) {
    return res.status(401).json({ message: "You must be logged in to do this" });
  }
  const { teamId } = req.query;
  await throwIfNotHaveAdminAccessToTeam.throwIfNotHaveAdminAccessToTeam({ teamId: Number(teamId) ?? null, userId: req.session.user.id });
  const installForObject = teamId ? { teamId: Number(teamId) } : { userId: req.session.user.id };
  const appType = utils.config$1.type;
  try {
    const alreadyInstalled = await index$2.prisma.credential.findFirst({
      where: {
        type: appType,
        ...installForObject
      }
    });
    if (alreadyInstalled) {
      throw new Error("Already installed");
    }
    const installation = await index$2.prisma.credential.create({
      data: {
        type: appType,
        key: {},
        userId: req.session.user.id,
        appId: "paypal"
      }
    });
    if (!installation) {
      throw new Error("Unable to create user credential for Paypal");
    }
  } catch (error) {
    if (error instanceof Error) {
      return res.status(500).json({ message: error.message });
    }
    return res.status(500);
  }
  return res.status(200).json({ url: "/apps/paypal/setup" });
}
class Paypal {
  constructor(opts) {
    this.accessToken = null;
    this.expiresAt = null;
    this.fetcher = async (endpoint, init) => {
      await this.getAccessToken();
      return fetch(`${this.url}${endpoint}`, {
        method: "get",
        ...init,
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${this.accessToken}`,
          ...init?.headers
        }
      });
    };
    this.url = index$2.IS_PRODUCTION ? "https://api-m.paypal.com" : "https://api-m.sandbox.paypal.com";
    this.clientId = opts.clientId;
    this.secretKey = opts.secretKey;
  }
  async getAccessToken() {
    if (this.accessToken && this.expiresAt && this.expiresAt > Date.now()) {
      return;
    }
    const headers = {
      "Content-Type": "application/x-www-form-urlencoded",
      Authorization: `Basic ${Buffer.from(`${this.clientId}:${this.secretKey}`).toString("base64")}`
    };
    const body = new URLSearchParams();
    body.append("grant_type", "client_credentials");
    try {
      const response = await fetch(`${this.url}/v1/oauth2/token`, {
        method: "POST",
        headers,
        body
      });
      if (response.ok) {
        const { access_token, expires_in } = await response.json();
        this.accessToken = access_token;
        this.expiresAt = Date.now() + expires_in;
      } else if (response?.status) {
        console.error(`Request failed with status ${response.status}`);
      }
    } catch (error) {
      console.error(error);
    }
  }
  // Orders
  async createOrder({
    referenceId,
    amount,
    currency,
    returnUrl,
    cancelUrl,
    intent = "CAPTURE"
  }) {
    const createOrderRequestBody = {
      intent,
      purchase_units: [
        {
          reference_id: referenceId,
          amount: {
            currency_code: currency,
            value: (amount / 100).toString()
          }
        }
      ],
      payment_source: {
        paypal: {
          experience_context: {
            user_action: "PAY_NOW",
            return_url: returnUrl,
            cancel_url: cancelUrl
          }
        }
      }
    };
    try {
      const response = await this.fetcher("/v2/checkout/orders", {
        method: "POST",
        headers: {
          "PayPal-Request-Id": uuid.v4()
        },
        body: JSON.stringify(createOrderRequestBody)
      });
      if (response.ok) {
        const createOrderResponse = await response.json();
        return createOrderResponse;
      } else {
        console.error(`Request failed with status ${response.status}`);
      }
    } catch (error) {
      console.error(error);
    }
    return {};
  }
  async captureOrder(orderId) {
    try {
      const captureResult = await this.fetcher(`/v2/checkout/orders/${orderId}/capture`, {
        method: "POST"
      });
      if (captureResult.ok) {
        const result = await captureResult.json();
        if (result?.status === "COMPLETED") {
          const payment = await index$2.prisma.payment.findFirst({
            where: {
              externalId: orderId
            },
            select: {
              id: true,
              bookingId: true,
              data: true
            }
          });
          if (!payment) {
            throw new Error("Payment not found");
          }
          await index$2.prisma.payment.update({
            where: {
              id: payment?.id
            },
            data: {
              success: true,
              data: Object.assign(
                {},
                { ...payment?.data, capture: result.id }
              )
            }
          });
          await index$2.prisma.booking.update({
            where: {
              id: payment.bookingId
            },
            data: {
              status: "ACCEPTED"
            }
          });
          return true;
        }
      }
    } catch (error) {
      console.error(error);
      throw error;
    }
    return false;
  }
  async createWebhook() {
    const body = {
      url: `${index$2.WEBAPP_URL}/api/integrations/paypal/webhook`,
      event_types: [
        {
          name: "CHECKOUT.ORDER.APPROVED"
        },
        {
          name: "CHECKOUT.ORDER.COMPLETED"
        }
      ]
    };
    try {
      const response = await this.fetcher(`/v1/notifications/webhooks`, {
        method: "POST",
        body: JSON.stringify(body)
      });
      if (!response.ok) {
        const message = `${response.statusText}: ${JSON.stringify(await response.json())}`;
        throw new Error(message);
      }
      const result = await response.json();
      return result.id;
    } catch (e) {
      index$2.logger.error("Error creating webhook", e);
    }
    return false;
  }
  async listWebhooks() {
    try {
      const response = await this.fetcher(`/v1/notifications/webhooks`);
      if (response.ok) {
        const { webhooks } = await response.json();
        return webhooks.filter((webhook) => {
          return webhook.url.includes("api/integrations/paypal/webhook");
        }).map((webhook) => webhook.id);
      }
    } catch (error) {
      console.error(error);
      return [];
    }
    return [];
  }
  async deleteWebhook(webhookId) {
    try {
      const response = await this.fetcher(`/v1/notifications/webhooks/${webhookId}`, {
        method: "DELETE"
      });
      if (response.ok) {
        return true;
      }
    } catch (error) {
      console.error(error);
    }
    return false;
  }
  async test() {
    try {
      await this.getAccessToken();
    } catch (error) {
      console.error(error);
      return false;
    }
    return true;
  }
  async verifyWebhook(options) {
    const parseRequest = webhookEventVerifyRequestSchema.safeParse(options);
    if (!parseRequest.success) {
      console.error(parseRequest.error);
      throw new Error("Request is malformed");
    }
    const stringy = JSON.stringify({
      auth_algo: options.body.auth_algo,
      cert_url: options.body.cert_url,
      transmission_id: options.body.transmission_id,
      transmission_sig: options.body.transmission_sig,
      transmission_time: options.body.transmission_time,
      webhook_id: options.body.webhook_id
    });
    const bodyToString = `${stringy.slice(0, -1)},"webhook_event":${options.body.webhook_event}}`;
    try {
      const response = await this.fetcher(`/v1/notifications/verify-webhook-signature`, {
        method: "POST",
        body: bodyToString
      });
      if (!response.ok) {
        throw response;
      }
      const data = await response.json();
      if (data.verification_status !== "SUCCESS") {
        throw data;
      }
    } catch (err) {
      console.error(err);
      throw err;
    }
  }
}
const Paypal$1 = Paypal;
const webhookEventVerifyRequestSchema = z.object({
  body: z.object({
    auth_algo: z.string(),
    cert_url: z.string(),
    transmission_id: z.string(),
    transmission_sig: z.string(),
    transmission_time: z.string(),
    webhook_event: z.string(),
    webhook_id: z.string()
  }).required()
});
const log = index$2.logger.getSubLogger({ prefix: ["payment-service:paypal"] });
const paypalCredentialKeysSchema = z.object({
  client_id: z.string(),
  secret_key: z.string(),
  webhook_id: z.string()
});
class PaymentService {
  constructor(credentials) {
    const keyParsing = paypalCredentialKeysSchema.safeParse(credentials.key);
    if (keyParsing.success) {
      this.credentials = keyParsing.data;
    } else {
      this.credentials = null;
    }
  }
  async create(payment, bookingId) {
    try {
      const booking = await index$2.prisma.booking.findUnique({
        select: {
          uid: true,
          title: true
        },
        where: {
          id: bookingId
        }
      });
      if (!booking || !this.credentials) {
        throw new Error();
      }
      const uid = uuid.v4();
      const paypalClient = new Paypal$1({
        clientId: this.credentials.client_id,
        secretKey: this.credentials.secret_key
      });
      const orderResult = await paypalClient.createOrder({
        referenceId: uid,
        amount: payment.amount,
        currency: payment.currency,
        returnUrl: `${index$2.WEBAPP_URL}/api/integrations/paypal/capture?bookingUid=${booking.uid}`,
        cancelUrl: `${index$2.WEBAPP_URL}/payment/${uid}`
      });
      const paymentData = await index$2.prisma.payment.create({
        data: {
          uid,
          app: {
            connect: {
              slug: "paypal"
            }
          },
          booking: {
            connect: {
              id: bookingId
            }
          },
          amount: payment.amount,
          externalId: orderResult.id,
          currency: payment.currency,
          data: Object.assign({}, { order: orderResult }),
          fee: 0,
          refunded: false,
          success: false
        }
      });
      if (!paymentData) {
        throw new Error();
      }
      return paymentData;
    } catch (error) {
      log.error("Paypal: Payment could not be created for bookingId", bookingId, index$2.safeStringify(error));
      throw new Error(errors.ErrorCode.PaymentCreationFailure);
    }
  }
  async update() {
    throw new Error("Method not implemented.");
  }
  async refund() {
    throw new Error("Method not implemented.");
  }
  async collectCard(payment, bookingId, paymentOption, _bookerEmail) {
    if (index$2.paymentOptionEnum.parse(paymentOption) !== "HOLD") {
      throw new Error("Payment option is not compatible with create method");
    }
    try {
      const booking = await index$2.prisma.booking.findUnique({
        select: {
          uid: true,
          title: true
        },
        where: {
          id: bookingId
        }
      });
      if (!booking || !this.credentials) {
        throw new Error();
      }
      const uid = uuid.v4();
      const paypalClient = new Paypal$1({
        clientId: this.credentials.client_id,
        secretKey: this.credentials.secret_key
      });
      const preference = await paypalClient.createOrder({
        referenceId: uid,
        amount: payment.amount,
        currency: payment.currency,
        returnUrl: `${index$2.WEBAPP_URL}/booking/${booking.uid}`,
        cancelUrl: `${index$2.WEBAPP_URL}/payment/${uid}`,
        intent: "AUTHORIZE"
      });
      const paymentData = await index$2.prisma.payment.create({
        data: {
          uid,
          app: {
            connect: {
              slug: "paypal"
            }
          },
          booking: {
            connect: {
              id: bookingId
            }
          },
          amount: payment.amount,
          currency: payment.currency,
          data: Object.assign({}, preference),
          fee: 0,
          refunded: false,
          success: false,
          paymentOption: paymentOption || "ON_BOOKING",
          externalId: preference?.id
        }
      });
      if (!paymentData) {
        throw new Error();
      }
      return paymentData;
    } catch (error) {
      log.error(
        "Paypal: Payment method could not be collected for bookingId",
        bookingId,
        index$2.safeStringify(error)
      );
      throw new Error("Paypal: Payment method could not be collected");
    }
  }
  chargeCard() {
    throw new Error("Method not implemented.");
  }
  getPaymentPaidStatus() {
    throw new Error("Method not implemented.");
  }
  getPaymentDetails() {
    throw new Error("Method not implemented.");
  }
  afterPayment() {
    return Promise.resolve();
  }
  deletePayment() {
    return Promise.resolve(false);
  }
  isSetupAlready() {
    return !!this.credentials;
  }
}
const index$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  __proto__: null,
  PaymentService,
  paypalCredentialKeysSchema
}, Symbol.toStringTag, { value: "Module" }));
const config = {
  api: {
    bodyParser: false
  }
};
async function handlePaypalPaymentSuccess(payload, rawPayload, webhookHeaders) {
  const payment = await index$2.prisma.payment.findFirst({
    where: {
      externalId: payload?.resource?.id
    },
    select: {
      id: true,
      bookingId: true
    }
  });
  if (!payment?.bookingId)
    throw new httpError.HttpError({ statusCode: 204, message: "Payment not found" });
  const booking = await index$2.prisma.booking.findUnique({
    where: {
      id: payment.bookingId
    },
    select: {
      id: true
    }
  });
  if (!booking)
    throw new httpError.HttpError({ statusCode: 204, message: "No booking found" });
  const foundCredentials = await findPaymentCredentials(booking.id);
  if (!foundCredentials)
    throw new httpError.HttpError({ statusCode: 204, message: "No credentials found" });
  const { webhookId, ...credentials } = foundCredentials;
  const paypalClient = new Paypal$1(credentials);
  await paypalClient.getAccessToken();
  await paypalClient.verifyWebhook({
    body: {
      auth_algo: webhookHeaders["paypal-auth-algo"],
      cert_url: webhookHeaders["paypal-cert-url"],
      transmission_id: webhookHeaders["paypal-transmission-id"],
      transmission_sig: webhookHeaders["paypal-transmission-sig"],
      transmission_time: webhookHeaders["paypal-transmission-time"],
      webhook_id: webhookId,
      webhook_event: rawPayload
    }
  });
  return await handlePaymentSuccess.handlePaymentSuccess(payment.id, payment.bookingId);
}
async function handler$1(req, res) {
  try {
    if (req.method !== "POST") {
      throw new httpError.HttpError({ statusCode: 405, message: "Method Not Allowed" });
    }
    const bodyRaw = await getRawBody(req);
    const headers = req.headers;
    const bodyAsString = bodyRaw.toString();
    const parseHeaders = webhookHeadersSchema.safeParse(headers);
    if (!parseHeaders.success) {
      console.error(parseHeaders.error);
      throw new httpError.HttpError({ statusCode: 400, message: "Bad Request" });
    }
    const parse = eventSchema.safeParse(JSON.parse(bodyAsString));
    if (!parse.success) {
      console.error(parse.error);
      throw new httpError.HttpError({ statusCode: 400, message: "Bad Request" });
    }
    const { data: parsedPayload } = parse;
    if (parsedPayload.event_type === "CHECKOUT.ORDER.APPROVED") {
      return await handlePaypalPaymentSuccess(parsedPayload, bodyAsString, parseHeaders.data);
    }
  } catch (_err) {
    const err = errors.getErrorFromUnknown(_err);
    console.error(`Webhook Error: ${err.message}`);
    res.status(200).send({
      message: err.message,
      stack: index$2.IS_PRODUCTION ? void 0 : err.stack
    });
    return;
  }
  res.status(200).end();
}
const resourceSchema = z.z.object({
  create_time: z.z.string(),
  id: z.z.string(),
  payment_source: z.z.object({
    paypal: z.z.object({}).optional()
  }),
  intent: z.z.string(),
  payer: z.z.object({
    email_address: z.z.string(),
    payer_id: z.z.string(),
    address: z.z.object({
      country_code: z.z.string()
    })
  }),
  status: z.z.string().optional()
}).passthrough();
const eventSchema = z.z.object({
  id: z.z.string(),
  create_time: z.z.string(),
  resource_type: z.z.string(),
  event_type: z.z.string(),
  summary: z.z.string(),
  resource: resourceSchema,
  status: z.z.string().optional(),
  event_version: z.z.string(),
  resource_version: z.z.string()
}).passthrough();
const webhookHeadersSchema = z.z.object({
  "paypal-auth-algo": z.z.string(),
  "paypal-cert-url": z.z.string(),
  "paypal-transmission-id": z.z.string(),
  "paypal-transmission-sig": z.z.string(),
  "paypal-transmission-time": z.z.string()
}).passthrough();
const findPaymentCredentials = async (bookingId) => {
  try {
    const userFromBooking = await index$2.prisma.booking.findFirst({
      where: {
        id: bookingId
      },
      select: {
        id: true,
        userId: true
      }
    });
    if (!userFromBooking)
      throw new Error("No user found");
    const credentials = await index$2.prisma.credential.findFirst({
      where: {
        appId: "paypal",
        userId: userFromBooking?.userId
      },
      select: {
        key: true
      }
    });
    if (!credentials) {
      throw new Error("No credentials found");
    }
    const parsedCredentials = paypalCredentialKeysSchema.safeParse(credentials?.key);
    if (!parsedCredentials.success) {
      throw new Error("Credentials malformed");
    }
    return {
      clientId: parsedCredentials.data.client_id,
      secretKey: parsedCredentials.data.secret_key,
      webhookId: parsedCredentials.data.webhook_id
    };
  } catch (err) {
    console.error(err);
    return {
      clientId: "",
      secretKey: "",
      webhookId: ""
    };
  }
};
async function handler(req, res) {
  try {
    if (req.method !== "GET") {
      throw new Error("Invalid method");
    }
    const parseRequest = captureRequestSchema.safeParse(req.query);
    if (!parseRequest.success) {
      throw new Error("Request is malformed");
    }
    const { bookingUid, token } = parseRequest.data;
    const booking = await index$2.prisma.booking.findUnique({
      where: {
        uid: bookingUid
      },
      select: {
        id: true,
        userId: true
      }
    });
    if (!booking) {
      throw new Error("Booking not found");
    }
    const credentials = await findPaymentCredentials(booking?.id);
    if (!credentials) {
      throw new Error("Credentials not found");
    }
    const paypalClient = new Paypal$1(credentials);
    const capture = await paypalClient.captureOrder(token);
    if (!capture) {
      res.redirect(`/booking/${bookingUid}?paypalPaymentStatus=failed`);
    }
    if (index$2.IS_PRODUCTION) {
      res.redirect(`/booking/${bookingUid}?paypalPaymentStatus=success`);
    } else {
      const updateBooking = index$2.prisma.booking.update({
        where: {
          uid: bookingUid
        },
        data: {
          paid: true
        }
      });
      const updatePayment = index$2.prisma.payment.update({
        where: {
          id: booking?.id
        },
        data: {
          success: true
        }
      });
      await Promise.all([updateBooking, updatePayment]);
      res.redirect(`/booking/${bookingUid}?paypalPaymentStatus=success`);
    }
    return;
  } catch (_err) {
    res.redirect(`/booking/${req.query.bookingUid}?paypalPaymentStatus=failed`);
  }
}
const captureRequestSchema = z.object({
  bookingUid: z.string(),
  token: z.string()
});
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  __proto__: null,
  add: handler$2,
  capture: handler,
  config,
  webhook: handler$1
}, Symbol.toStringTag, { value: "Module" }));
exports.api = index;
exports.lib = index$1;
