import * as plugins from './bunq.plugins.js'; import { BunqAccount } from './bunq.classes.account.js'; import { BunqMonetaryAccount } from './bunq.classes.monetaryaccount.js'; import { IBunqRequestInquiry, IBunqAmount, IBunqAlias, IBunqPaginationOptions } from './bunq.interfaces.js'; export class BunqRequestInquiry { private bunqAccount: BunqAccount; private monetaryAccount: BunqMonetaryAccount; // Request properties public id?: number; public created?: string; public updated?: string; public timeResponded?: string; public timeExpiry?: string; public monetaryAccountId?: number; public amountInquired?: IBunqAmount; public amountResponded?: IBunqAmount; public userAliasCreated?: IBunqAlias; public userAliasRevoked?: IBunqAlias; public counterpartyAlias?: IBunqAlias; public description?: string; public merchantReference?: string; public status?: string; public minimumAge?: number; public requireAddress?: string; public bunqmeShareUrl?: string; public redirectUrl?: string; constructor(bunqAccount: BunqAccount, monetaryAccount: BunqMonetaryAccount) { this.bunqAccount = bunqAccount; this.monetaryAccount = monetaryAccount; } /** * Create a new request inquiry */ public async create(options: { amountInquired: IBunqAmount; counterpartyAlias: IBunqAlias; description: string; allowBunqme?: boolean; merchantReference?: string; status?: 'PENDING' | 'REVOKED'; minimumAge?: number; requireAddress?: 'BILLING' | 'SHIPPING' | 'BILLING_SHIPPING'; wantTip?: boolean; allowAmountLower?: boolean; allowAmountHigher?: boolean; redirectUrl?: string; eventId?: number; }): Promise { await this.bunqAccount.apiContext.ensureValidSession(); const requestData = { amount_inquired: options.amountInquired, counterparty_alias: options.counterpartyAlias, description: options.description, allow_bunqme: options.allowBunqme, merchant_reference: options.merchantReference, status: options.status, minimum_age: options.minimumAge, require_address: options.requireAddress, want_tip: options.wantTip, allow_amount_lower: options.allowAmountLower, allow_amount_higher: options.allowAmountHigher, redirect_url: options.redirectUrl, event_id: options.eventId }; const response = await this.bunqAccount.getHttpClient().post( `/v1/user/${this.bunqAccount.userId}/monetary-account/${this.monetaryAccount.id}/request-inquiry`, requestData ); 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 request inquiry'); } /** * Get request inquiry details */ public async get(): Promise { if (!this.id) { throw new Error('Request inquiry ID not set'); } await this.bunqAccount.apiContext.ensureValidSession(); const response = await this.bunqAccount.getHttpClient().get( `/v1/user/${this.bunqAccount.userId}/monetary-account/${this.monetaryAccount.id}/request-inquiry/${this.id}` ); if (response.Response && response.Response[0] && response.Response[0].RequestInquiry) { const data = response.Response[0].RequestInquiry; this.updateFromApiResponse(data); return data; } throw new Error('Request inquiry not found'); } /** * Update request inquiry */ public async update(updates: { status?: 'REVOKED'; amountInquired?: IBunqAmount; description?: string; }): Promise { if (!this.id) { throw new Error('Request inquiry ID not set'); } await this.bunqAccount.apiContext.ensureValidSession(); await this.bunqAccount.getHttpClient().put( `/v1/user/${this.bunqAccount.userId}/monetary-account/${this.monetaryAccount.id}/request-inquiry/${this.id}`, updates ); // Refresh data await this.get(); } /** * Revoke the request inquiry */ public async revoke(): Promise { await this.update({ status: 'REVOKED' }); } /** * List request inquiries for a monetary account */ public static async list( bunqAccount: BunqAccount, monetaryAccountId: number, options?: IBunqPaginationOptions ): Promise { await bunqAccount.apiContext.ensureValidSession(); const response = await bunqAccount.getHttpClient().list( `/v1/user/${bunqAccount.userId}/monetary-account/${monetaryAccountId}/request-inquiry`, options ); const requests: IBunqRequestInquiry[] = []; if (response.Response) { for (const item of response.Response) { if (item.RequestInquiry) { requests.push(item.RequestInquiry); } } } return requests; } /** * Update properties from API response */ private updateFromApiResponse(data: any): void { this.id = data.id; this.created = data.created; this.updated = data.updated; this.timeResponded = data.time_responded; this.timeExpiry = data.time_expiry; this.monetaryAccountId = data.monetary_account_id; this.amountInquired = data.amount_inquired; this.amountResponded = data.amount_responded; this.userAliasCreated = data.user_alias_created; this.userAliasRevoked = data.user_alias_revoked; this.counterpartyAlias = data.counterparty_alias; this.description = data.description; this.merchantReference = data.merchant_reference; this.status = data.status; this.minimumAge = data.minimum_age; this.requireAddress = data.require_address; this.bunqmeShareUrl = data.bunqme_share_url; this.redirectUrl = data.redirect_url; } /** * Create a builder for request inquiries */ public static builder( bunqAccount: BunqAccount, monetaryAccount: BunqMonetaryAccount ): RequestInquiryBuilder { return new RequestInquiryBuilder(bunqAccount, monetaryAccount); } } /** * Builder class for creating request inquiries */ export class RequestInquiryBuilder { private bunqAccount: BunqAccount; private monetaryAccount: BunqMonetaryAccount; private options: any = {}; constructor(bunqAccount: BunqAccount, monetaryAccount: BunqMonetaryAccount) { this.bunqAccount = bunqAccount; this.monetaryAccount = monetaryAccount; } /** * Set the amount */ public amount(value: string, currency: string = 'EUR'): this { this.options.amountInquired = { value, currency }; return this; } /** * Set the counterparty by IBAN */ public fromIban(iban: string, name?: string): this { this.options.counterpartyAlias = { type: 'IBAN', value: iban, name }; return this; } /** * Set the counterparty by email */ public fromEmail(email: string, name?: string): this { this.options.counterpartyAlias = { type: 'EMAIL', value: email, name }; return this; } /** * Set the counterparty by phone number */ public fromPhoneNumber(phoneNumber: string, name?: string): this { this.options.counterpartyAlias = { type: 'PHONE_NUMBER', value: phoneNumber, name }; return this; } /** * Set the description */ public description(description: string): this { this.options.description = description; return this; } /** * Allow bunq.me */ public allowBunqme(allow: boolean = true): this { this.options.allowBunqme = allow; return this; } /** * Set merchant reference */ public merchantReference(reference: string): this { this.options.merchantReference = reference; return this; } /** * Set minimum age requirement */ public minimumAge(age: number): this { this.options.minimumAge = age; return this; } /** * Require address */ public requireAddress(type: 'BILLING' | 'SHIPPING' | 'BILLING_SHIPPING'): this { this.options.requireAddress = type; return this; } /** * Allow tips */ public allowTips(allow: boolean = true): this { this.options.wantTip = allow; return this; } /** * Allow lower amount */ public allowLowerAmount(allow: boolean = true): this { this.options.allowAmountLower = allow; return this; } /** * Allow higher amount */ public allowHigherAmount(allow: boolean = true): this { this.options.allowAmountHigher = allow; return this; } /** * Set redirect URL */ public redirectUrl(url: string): this { this.options.redirectUrl = url; return this; } /** * Create the request inquiry */ public async create(): Promise { if (!this.options.amountInquired) { throw new Error('Amount is required'); } if (!this.options.counterpartyAlias) { throw new Error('Counterparty is required'); } if (!this.options.description) { throw new Error('Description is required'); } const request = new BunqRequestInquiry(this.bunqAccount, this.monetaryAccount); await request.create(this.options); return request; } } /** * Request response class for responding to payment requests */ export class BunqRequestResponse { private bunqAccount: BunqAccount; private monetaryAccount: BunqMonetaryAccount; constructor(bunqAccount: BunqAccount, monetaryAccount: BunqMonetaryAccount) { this.bunqAccount = bunqAccount; this.monetaryAccount = monetaryAccount; } /** * Accept a request */ public async accept( requestResponseId: number, amountResponded?: IBunqAmount, description?: string ): Promise { await this.bunqAccount.apiContext.ensureValidSession(); const response = await this.bunqAccount.getHttpClient().put( `/v1/user/${this.bunqAccount.userId}/monetary-account/${this.monetaryAccount.id}/request-response/${requestResponseId}`, { amount_responded: amountResponded, status: 'ACCEPTED', description: description } ); if (response.Response && response.Response[0] && response.Response[0].Id) { return response.Response[0].Id.id; } throw new Error('Failed to accept request'); } /** * Reject a request */ public async reject(requestResponseId: number): Promise { await this.bunqAccount.apiContext.ensureValidSession(); await this.bunqAccount.getHttpClient().put( `/v1/user/${this.bunqAccount.userId}/monetary-account/${this.monetaryAccount.id}/request-response/${requestResponseId}`, { status: 'REJECTED' } ); } /** * List incoming payment requests */ public async listIncoming(options?: IBunqPaginationOptions): Promise { await this.bunqAccount.apiContext.ensureValidSession(); const response = await this.bunqAccount.getHttpClient().list( `/v1/user/${this.bunqAccount.userId}/monetary-account/${this.monetaryAccount.id}/request-response`, options ); return response.Response || []; } }