import { LightningAddress } from "@getalby/lightning-tools"; import type { Booking, Payment, PaymentOption, Prisma } from "@prisma/client"; import { v4 as uuidv4 } from "uuid"; import type z from "zod"; import { ErrorCode } from "@calcom/lib/errorCodes"; import logger from "@calcom/lib/logger"; import { safeStringify } from "@calcom/lib/safeStringify"; import prisma from "@calcom/prisma"; import type { CalendarEvent } from "@calcom/types/Calendar"; import type { IAbstractPaymentService } from "@calcom/types/PaymentService"; import { albyCredentialKeysSchema } from "./albyCredentialKeysSchema"; const log = logger.getSubLogger({ prefix: ["payment-service:alby"] }); export class PaymentService implements IAbstractPaymentService { private credentials: z.infer | null; constructor(credentials: { key: Prisma.JsonValue }) { const keyParsing = albyCredentialKeysSchema.safeParse(credentials.key); if (keyParsing.success) { this.credentials = keyParsing.data; } else { this.credentials = null; } } async create( payment: Pick, bookingId: Booking["id"] ) { try { const booking = await prisma.booking.findFirst({ select: { uid: true, title: true, }, where: { id: bookingId, }, }); if (!booking || !this.credentials?.account_lightning_address) { throw new Error("Alby: Booking or Lightning address not found"); } const uid = uuidv4(); const lightningAddress = new LightningAddress(this.credentials.account_lightning_address); await lightningAddress.fetch(); const invoice = await lightningAddress.requestInvoice({ satoshi: payment.amount, payerdata: { appId: "cal.com", referenceId: uid, }, }); console.log("Created invoice", invoice, uid); const paymentData = await prisma.payment.create({ data: { uid, app: { connect: { slug: "alby", }, }, booking: { connect: { id: bookingId, }, }, amount: payment.amount, externalId: invoice.paymentRequest, currency: payment.currency, data: Object.assign( {}, { invoice: { ...invoice, isPaid: false } } ) as unknown as Prisma.InputJsonValue, fee: 0, refunded: false, success: false, }, }); if (!paymentData) { throw new Error(); } return paymentData; } catch (error) { log.error("Alby: Payment could not be created", bookingId, safeStringify(error)); throw new Error(ErrorCode.PaymentCreationFailure); } } async update(): Promise { throw new Error("Method not implemented."); } async refund(): Promise { throw new Error("Method not implemented."); } async collectCard( _payment: Pick, _bookingId: number, _bookerEmail: string, _paymentOption: PaymentOption ): Promise { throw new Error("Method not implemented"); } chargeCard( _payment: Pick, _bookingId: number ): Promise { throw new Error("Method not implemented."); } getPaymentPaidStatus(): Promise { throw new Error("Method not implemented."); } getPaymentDetails(): Promise { throw new Error("Method not implemented."); } afterPayment( _event: CalendarEvent, _booking: { user: { email: string | null; name: string | null; timeZone: string } | null; id: number; startTime: { toISOString: () => string }; uid: string; }, _paymentData: Payment ): Promise { return Promise.resolve(); } deletePayment(_paymentId: number): Promise { return Promise.resolve(false); } isSetupAlready(): boolean { return !!this.credentials; } }