419 lines
11 KiB
TypeScript
419 lines
11 KiB
TypeScript
import * as plugins from './bunq.plugins.js';
|
|
import { BunqAccount } from './bunq.classes.account.js';
|
|
import { BunqMonetaryAccount } from './bunq.classes.monetaryaccount.js';
|
|
import type {
|
|
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<number> {
|
|
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<IBunqRequestInquiry> {
|
|
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<void> {
|
|
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<void> {
|
|
await this.update({ status: 'REVOKED' });
|
|
}
|
|
|
|
/**
|
|
* List request inquiries for a monetary account
|
|
*/
|
|
public static async list(
|
|
bunqAccount: BunqAccount,
|
|
monetaryAccountId: number,
|
|
options?: IBunqPaginationOptions
|
|
): Promise<IBunqRequestInquiry[]> {
|
|
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<BunqRequestInquiry> {
|
|
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<number> {
|
|
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<void> {
|
|
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<any[]> {
|
|
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 || [];
|
|
}
|
|
} |