import { a as createDefaultInstallation } from "./installation-b0PLfNT5.js";
import { f as appConfig } from "./utils-TrPY7mDR.js";
import crypto from "crypto";
import getRawBody from "raw-body";
import z__default, { z } from "zod";
import { p as prisma, an as IS_PRODUCTION, l as logger, a as safeStringify } from "./orgDomains-eTg-cjth.js";
import { g as getErrorFromUnknown, a as ErrorCode } from "./errors-SirEabuV.js";
import { H as HttpError } from "./http-error-RJXCPZV2.js";
import { h as handlePaymentSuccess } from "./handlePaymentSuccess-nXpqXGUS.js";
import { v4 } from "uuid";
const handler$1 = {
  appType: appConfig.type,
  variant: appConfig.variant,
  slug: appConfig.slug,
  supportsMultipleInstalls: false,
  handlerType: "add",
  redirect: {
    url: "/apps/btcpayserver/setup"
  },
  createCredential: ({ appType, user, slug, teamId }) => createDefaultInstallation({ appType, user, slug, key: {}, teamId })
};
const handler$2 = handler$1;
class PrismaBookingPaymentRepository {
  constructor(prismaClient = prisma) {
    this.prismaClient = prismaClient;
  }
  async findByExternalIdIncludeBookingUserCredentials(externalId, credentialType) {
    return await this.prismaClient.payment.findFirst({
      where: {
        externalId
      },
      select: {
        id: true,
        amount: true,
        success: true,
        bookingId: true,
        booking: {
          select: {
            user: {
              select: {
                credentials: {
                  where: { type: credentialType },
                  select: { key: true }
                }
              }
            }
          }
        }
      }
    });
  }
  async createPaymentRecord(data) {
    const createdPayment = await this.prismaClient.payment.create({
      data
    });
    return createdPayment;
  }
}
const btcpayCredentialKeysSchema = z__default.object({
  serverUrl: z__default.string().url(),
  storeId: z__default.string(),
  apiKey: z__default.string(),
  webhookSecret: z__default.string()
});
const config = { api: { bodyParser: false } };
function verifyBTCPaySignature(rawBody, expectedSignature, webhookSecret) {
  const hmac = crypto.createHmac("sha256", webhookSecret);
  hmac.update(rawBody);
  const computedSignature = hmac.digest("hex");
  const hexRegex = /^[0-9a-fA-F]+$/;
  if (!hexRegex.test(computedSignature) || !hexRegex.test(expectedSignature)) {
    throw new HttpError({ statusCode: 400, message: "signature mismatch" });
  }
  return computedSignature;
}
const btcpayWebhookSchema = z.object({
  deliveryId: z.string(),
  webhookId: z.string(),
  originalDeliveryId: z.string().optional(),
  isRedelivery: z.boolean(),
  type: z.string(),
  timestamp: z.number(),
  storeId: z.string(),
  invoiceId: z.string(),
  metadata: z.object({}).optional(),
  manuallyMarked: z.boolean().optional(),
  overPaid: z.boolean()
});
const SUPPORTED_INVOICE_EVENTS = ["InvoiceSettled", "InvoiceProcessing"];
async function handler(req, res) {
  try {
    if (req.method !== "POST")
      throw new HttpError({ statusCode: 405, message: "Method Not Allowed" });
    const rawBody = await getRawBody(req);
    const bodyAsString = rawBody.toString();
    const signature = req.headers["btcpay-sig"] || req.headers["BTCPay-Sig"];
    if (!signature || typeof signature !== "string" || !signature.startsWith("sha256="))
      throw new HttpError({ statusCode: 401, message: "Missing or invalid signature format" });
    const webhookData = btcpayWebhookSchema.safeParse(JSON.parse(bodyAsString));
    if (!webhookData.success)
      return res.status(400).json({ message: "Invalid webhook payload" });
    const data = webhookData.data;
    if (!SUPPORTED_INVOICE_EVENTS.includes(data.type))
      return res.status(200).send({ message: "Webhook received but ignored" });
    const bookingPaymentRepository = new PrismaBookingPaymentRepository();
    const payment = await bookingPaymentRepository.findByExternalIdIncludeBookingUserCredentials(
      data.invoiceId,
      appConfig.type
    );
    if (!payment)
      throw new HttpError({ statusCode: 404, message: "Cal.com: payment not found" });
    if (payment.success)
      return res.status(200).send({ message: "Payment already registered" });
    const key = payment.booking?.user?.credentials?.[0].key;
    if (!key)
      throw new HttpError({ statusCode: 404, message: "Cal.com: credentials not found" });
    const parsedKey = btcpayCredentialKeysSchema.safeParse(key);
    if (!parsedKey.success)
      throw new HttpError({ statusCode: 400, message: "Cal.com: Invalid BTCPay credentials" });
    const { webhookSecret, storeId } = parsedKey.data;
    if (storeId !== data.storeId)
      throw new HttpError({ statusCode: 400, message: "Cal.com: Store ID mismatch" });
    const expectedSignature = signature.split("=")[1];
    const computedSignature = verifyBTCPaySignature(rawBody, expectedSignature, webhookSecret);
    if (computedSignature.length !== expectedSignature.length) {
      throw new HttpError({ statusCode: 400, message: "signature mismatch" });
    }
    const isValid = crypto.timingSafeEqual(
      Buffer.from(computedSignature, "hex"),
      Buffer.from(expectedSignature, "hex")
    );
    if (!isValid)
      throw new HttpError({ statusCode: 400, message: "signature mismatch" });
    await handlePaymentSuccess(payment.id, payment.bookingId);
    return res.status(200).json({ success: true });
  } catch (_err) {
    const err = getErrorFromUnknown(_err);
    const statusCode = err instanceof HttpError ? err.statusCode : 500;
    return res.status(statusCode).send({
      message: err.message,
      stack: IS_PRODUCTION ? void 0 : err.stack
    });
  }
}
const index$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  __proto__: null,
  add: handler$2,
  config,
  webhook: handler
}, Symbol.toStringTag, { value: "Module" }));
const zeroDecimalCurrencies = ["SATS", "BTC", "JPY"];
const convertFromSmallestToPresentableCurrencyUnit = (amount, currency) => {
  if (zeroDecimalCurrencies.includes(currency.toUpperCase())) {
    return amount;
  }
  return amount / 100;
};
const log = logger.getSubLogger({ prefix: ["payment-service:btcpayserver"] });
class PaymentService {
  constructor(credentials, bookingPaymentRepository = new PrismaBookingPaymentRepository()) {
    const keyParsing = btcpayCredentialKeysSchema.safeParse(credentials.key);
    if (keyParsing.success) {
      this.credentials = keyParsing.data;
    } else {
      this.credentials = null;
    }
    this.bookingPaymentRepository = bookingPaymentRepository;
  }
  async BTCPayApiCall(endpoint, options = {}) {
    if (!this.credentials)
      throw new Error("BTCPay server credentials not found");
    const serverUrl = this.credentials.serverUrl.endsWith("/") ? this.credentials.serverUrl.slice(0, -1) : this.credentials.serverUrl;
    const url = `${serverUrl}${endpoint}`;
    const headers = {
      Authorization: `token ${this.credentials.apiKey}`,
      "Content-Type": "application/json",
      Accept: "application/json",
      ...options.headers
    };
    try {
      const response = await fetch(url, { ...options, headers });
      if (!response.ok) {
        const errorText = await response.text();
        throw new Error(`BTCPay server API error (${response.status}): ${errorText}`);
      }
      return await response.json();
    } catch (error) {
      throw error;
    }
  }
  async create(payment, bookingId, userId, username, bookerName, paymentOption, bookerEmail, bookerPhoneNumber, eventTitle, bookingTitle) {
    try {
      if (!this.credentials?.storeId) {
        throw new Error("BTCPay server: Store ID not found");
      }
      const uid = v4();
      const invoiceRequest = {
        metadata: {
          orderId: `cal-booking-${bookingId}`,
          itemDesc: bookingTitle || `Booking #${bookingId}`,
          appId: "cal.com",
          referenceId: uid,
          customerName: bookerName,
          customerEmail: bookerEmail,
          bookingDescription: bookingTitle || `Booking with ${bookerName}`
        },
        checkout: {
          buyerEmail: bookerEmail
        },
        receipt: {
          enabled: true
        },
        amount: convertFromSmallestToPresentableCurrencyUnit(payment.amount, payment.currency),
        currency: payment.currency === "BTC" ? "SATS" : payment.currency,
        additionalSearchTerms: [`cal-booking-${bookingId}`, bookerName, bookerEmail]
      };
      const invoiceResponse = await this.BTCPayApiCall(
        `/api/v1/stores/${this.credentials.storeId}/invoices`,
        { method: "POST", body: JSON.stringify(invoiceRequest) }
      );
      const paymentData = await this.bookingPaymentRepository.createPaymentRecord({
        uid,
        app: { connect: { slug: appConfig.slug } },
        booking: { connect: { id: bookingId } },
        amount: payment.amount,
        externalId: invoiceResponse.id,
        currency: payment.currency,
        fee: 0,
        success: false,
        refunded: false,
        data: Object.assign(
          {},
          {
            invoice: {
              ...invoiceResponse,
              isPaid: false,
              attendee: { name: bookerName, email: bookerEmail }
            }
          }
        )
      });
      if (!paymentData)
        throw new Error("Failed to store Payment data");
      return paymentData;
    } catch (error) {
      log.error("BTCPay server: Payment could not be created", bookingId, safeStringify(error));
      throw new Error(ErrorCode.PaymentCreationFailure);
    }
  }
  async update() {
    throw new Error("Method not implemented.");
  }
  async refund() {
    throw new Error("BTCPay Server does not support automatic refunds for Bitcoin payments");
  }
  async collectCard(_payment, _bookingId, _bookerEmail, _paymentOption) {
    throw new Error("Method not implemented");
  }
  chargeCard(_payment, _bookingId) {
    throw new Error("Method not implemented.");
  }
  async getPaymentPaidStatus() {
    throw new Error("Method not implemented.");
  }
  async getPaymentDetails() {
    throw new Error("Method not implemented.");
  }
  async afterPayment(_event, _booking, _paymentData) {
    return Promise.resolve();
  }
  deletePayment(_paymentId) {
    return Promise.resolve(false);
  }
  isSetupAlready() {
    return !!this.credentials;
  }
}
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  __proto__: null,
  PaymentService,
  btcpayCredentialKeysSchema
}, Symbol.toStringTag, { value: "Module" }));
export {
  index$1 as api,
  index as lib
};
