import * as plugins from './bunq.plugins'; import { BunqAccount } from './bunq.classes.account'; export class BunqAttachment { private bunqAccount: BunqAccount; public id?: number; public created?: string; public updated?: string; public uuid?: string; constructor(bunqAccount: BunqAccount) { this.bunqAccount = bunqAccount; } /** * Upload a file attachment */ public async upload(options: { contentType: string; description?: string; body: Buffer | string; }): Promise { await this.bunqAccount.apiContext.ensureValidSession(); // First, create the attachment placeholder const attachmentResponse = await this.bunqAccount.getHttpClient().post( '/v1/attachment-public', { description: options.description } ); if (!attachmentResponse.Response || !attachmentResponse.Response[0]) { throw new Error('Failed to create attachment'); } const attachmentUuid = attachmentResponse.Response[0].Uuid.uuid; this.uuid = attachmentUuid; // Upload the actual content const uploadUrl = `/v1/attachment-public/${attachmentUuid}/content`; // For file uploads, we need to make a raw request const headers = { 'Content-Type': options.contentType, 'X-Bunq-Client-Authentication': this.bunqAccount.apiContext.getSession().getContext().sessionToken }; const requestOptions = { method: 'PUT' as const, headers: headers, requestBody: options.body }; await plugins.smartrequest.request( `${this.bunqAccount.apiContext.getBaseUrl()}${uploadUrl}`, requestOptions ); return attachmentUuid; } /** * Get attachment content */ public async getContent(attachmentUuid: string): Promise { await this.bunqAccount.apiContext.ensureValidSession(); const response = await plugins.smartrequest.request( `${this.bunqAccount.apiContext.getBaseUrl()}/v1/attachment-public/${attachmentUuid}/content`, { method: 'GET', headers: { 'X-Bunq-Client-Authentication': this.bunqAccount.apiContext.getSession().getContext().sessionToken } } ); return Buffer.from(response.body); } /** * Create attachment for a specific monetary account */ public async createForAccount( monetaryAccountId: number, attachmentPublicUuid: string ): Promise { await this.bunqAccount.apiContext.ensureValidSession(); const response = await this.bunqAccount.getHttpClient().post( `/v1/user/${this.bunqAccount.userId}/monetary-account/${monetaryAccountId}/attachment`, { attachment_public_uuid: attachmentPublicUuid } ); if (response.Response && response.Response[0] && response.Response[0].Id) { this.id = response.Response[0].Id.id; return this.id; } throw new Error('Failed to create account attachment'); } /** * List attachments for a monetary account */ public static async listForAccount( bunqAccount: BunqAccount, monetaryAccountId: number ): Promise { await bunqAccount.apiContext.ensureValidSession(); const response = await bunqAccount.getHttpClient().list( `/v1/user/${bunqAccount.userId}/monetary-account/${monetaryAccountId}/attachment` ); return response.Response || []; } /** * Create attachment for a payment */ public async createForPayment( monetaryAccountId: number, paymentId: number, attachmentId: number ): Promise { await this.bunqAccount.apiContext.ensureValidSession(); await this.bunqAccount.getHttpClient().post( `/v1/user/${this.bunqAccount.userId}/monetary-account/${monetaryAccountId}/payment/${paymentId}/attachment`, { id: attachmentId } ); } /** * Upload image as avatar */ public async uploadAvatar(imageBuffer: Buffer, contentType: string = 'image/png'): Promise { return this.upload({ contentType, description: 'Avatar image', body: imageBuffer }); } /** * Upload document */ public async uploadDocument( documentBuffer: Buffer, contentType: string, description: string ): Promise { return this.upload({ contentType, description, body: documentBuffer }); } /** * Helper to upload file from filesystem */ public async uploadFile(filePath: string, description?: string): Promise { const fileBuffer = await plugins.smartfile.fs.toBuffer(filePath); const contentType = this.getContentType(filePath); return this.upload({ contentType, description: description || plugins.path.basename(filePath), body: fileBuffer }); } /** * Get content type from file extension */ private getContentType(filePath: string): string { const ext = plugins.path.extname(filePath).toLowerCase(); const contentTypes: { [key: string]: string } = { '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png', '.gif': 'image/gif', '.pdf': 'application/pdf', '.txt': 'text/plain', '.csv': 'text/csv', '.xml': 'application/xml', '.json': 'application/json' }; return contentTypes[ext] || 'application/octet-stream'; } } /** * Tab attachment class for managing receipt attachments */ export class BunqTabAttachment { private bunqAccount: BunqAccount; private monetaryAccountId: number; private tabUuid: string; constructor(bunqAccount: BunqAccount, monetaryAccountId: number, tabUuid: string) { this.bunqAccount = bunqAccount; this.monetaryAccountId = monetaryAccountId; this.tabUuid = tabUuid; } /** * Upload attachment for a tab */ public async upload(attachmentPublicUuid: string): Promise { await this.bunqAccount.apiContext.ensureValidSession(); const response = await this.bunqAccount.getHttpClient().post( `/v1/user/${this.bunqAccount.userId}/monetary-account/${this.monetaryAccountId}/tab/${this.tabUuid}/attachment`, { attachment_public_uuid: attachmentPublicUuid } ); if (response.Response && response.Response[0] && response.Response[0].Id) { return response.Response[0].Id.id; } throw new Error('Failed to create tab attachment'); } /** * List attachments for a tab */ public async list(): Promise { await this.bunqAccount.apiContext.ensureValidSession(); const response = await this.bunqAccount.getHttpClient().list( `/v1/user/${this.bunqAccount.userId}/monetary-account/${this.monetaryAccountId}/tab/${this.tabUuid}/attachment` ); return response.Response || []; } /** * Get specific attachment */ public async get(attachmentId: number): Promise { await this.bunqAccount.apiContext.ensureValidSession(); const response = await this.bunqAccount.getHttpClient().get( `/v1/user/${this.bunqAccount.userId}/monetary-account/${this.monetaryAccountId}/tab/${this.tabUuid}/attachment/${attachmentId}` ); if (response.Response && response.Response[0]) { return response.Response[0].TabAttachment; } throw new Error('Tab attachment not found'); } }