259 lines
6.9 KiB
TypeScript
259 lines
6.9 KiB
TypeScript
import * as plugins from './bunq.plugins.js';
|
|
import { BunqAccount } from './bunq.classes.account.js';
|
|
import type { IBunqCard, IBunqAmount } from './bunq.interfaces.js';
|
|
|
|
export class BunqCard {
|
|
private bunqAccount: BunqAccount;
|
|
|
|
// Card properties
|
|
public id: number;
|
|
public created: string;
|
|
public updated: string;
|
|
public publicUuid: string;
|
|
public type: 'MAESTRO' | 'MASTERCARD';
|
|
public subType: string;
|
|
public secondLine: string;
|
|
public status: string;
|
|
public orderStatus?: string;
|
|
public expiryDate?: string;
|
|
public nameOnCard: string;
|
|
public primaryAccountNumberFourDigit?: string;
|
|
public limit?: IBunqAmount;
|
|
public monetaryAccountIdFallback?: number;
|
|
public country?: string;
|
|
|
|
constructor(bunqAccount: BunqAccount, cardData?: any) {
|
|
this.bunqAccount = bunqAccount;
|
|
|
|
if (cardData) {
|
|
this.updateFromApiResponse(cardData);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update card properties from API response
|
|
*/
|
|
private updateFromApiResponse(cardData: any): void {
|
|
this.id = cardData.id;
|
|
this.created = cardData.created;
|
|
this.updated = cardData.updated;
|
|
this.publicUuid = cardData.public_uuid;
|
|
this.type = cardData.type;
|
|
this.subType = cardData.sub_type;
|
|
this.secondLine = cardData.second_line;
|
|
this.status = cardData.status;
|
|
this.orderStatus = cardData.order_status;
|
|
this.expiryDate = cardData.expiry_date;
|
|
this.nameOnCard = cardData.name_on_card;
|
|
this.primaryAccountNumberFourDigit = cardData.primary_account_number_four_digit;
|
|
this.limit = cardData.limit;
|
|
this.monetaryAccountIdFallback = cardData.monetary_account_id_fallback;
|
|
this.country = cardData.country;
|
|
}
|
|
|
|
/**
|
|
* List all cards for the user
|
|
*/
|
|
public static async list(bunqAccount: BunqAccount): Promise<BunqCard[]> {
|
|
await bunqAccount.apiContext.ensureValidSession();
|
|
|
|
const response = await bunqAccount.getHttpClient().list(
|
|
`/v1/user/${bunqAccount.userId}/card`
|
|
);
|
|
|
|
const cards: BunqCard[] = [];
|
|
|
|
if (response.Response) {
|
|
for (const item of response.Response) {
|
|
if (item.CardDebit || item.CardCredit) {
|
|
const cardData = item.CardDebit || item.CardCredit;
|
|
cards.push(new BunqCard(bunqAccount, cardData));
|
|
}
|
|
}
|
|
}
|
|
|
|
return cards;
|
|
}
|
|
|
|
/**
|
|
* Get a specific card
|
|
*/
|
|
public static async get(bunqAccount: BunqAccount, cardId: number): Promise<BunqCard> {
|
|
await bunqAccount.apiContext.ensureValidSession();
|
|
|
|
const response = await bunqAccount.getHttpClient().get(
|
|
`/v1/user/${bunqAccount.userId}/card/${cardId}`
|
|
);
|
|
|
|
if (response.Response && response.Response[0]) {
|
|
const cardData = response.Response[0].CardDebit || response.Response[0].CardCredit;
|
|
return new BunqCard(bunqAccount, cardData);
|
|
}
|
|
|
|
throw new Error('Card not found');
|
|
}
|
|
|
|
/**
|
|
* Update card settings
|
|
*/
|
|
public async update(updates: any): Promise<void> {
|
|
// Check if this is a dangerous operation
|
|
if ((updates.status === 'CANCELLED' || updates.status === 'BLOCKED') &&
|
|
!this.bunqAccount.options.dangerousOperations) {
|
|
throw new Error('Dangerous operations are not enabled. Initialize the BunqAccount with dangerousOperations: true to allow cancelling or blocking cards.');
|
|
}
|
|
|
|
await this.bunqAccount.apiContext.ensureValidSession();
|
|
|
|
const cardType = this.type === 'MASTERCARD' ? 'CardCredit' : 'CardDebit';
|
|
|
|
await this.bunqAccount.getHttpClient().put(
|
|
`/v1/user/${this.bunqAccount.userId}/card/${this.id}`,
|
|
{
|
|
[cardType]: updates
|
|
}
|
|
);
|
|
|
|
// Refresh card data
|
|
const updatedCard = await BunqCard.get(this.bunqAccount, this.id);
|
|
this.updateFromApiResponse(updatedCard);
|
|
}
|
|
|
|
/**
|
|
* Activate the card
|
|
*/
|
|
public async activate(activationCode: string, cardStatus: string = 'ACTIVE'): Promise<void> {
|
|
await this.update({
|
|
activation_code: activationCode,
|
|
status: cardStatus
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Block the card
|
|
*/
|
|
public async block(reason: string = 'LOST'): Promise<void> {
|
|
await this.update({
|
|
status: 'BLOCKED',
|
|
cancellation_reason: reason
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Cancel the card
|
|
*/
|
|
public async cancel(reason: string = 'USER_REQUEST'): Promise<void> {
|
|
await this.update({
|
|
status: 'CANCELLED',
|
|
cancellation_reason: reason
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update spending limit
|
|
*/
|
|
public async updateLimit(value: string, currency: string = 'EUR'): Promise<void> {
|
|
await this.update({
|
|
monetary_account_id: this.monetaryAccountIdFallback,
|
|
limit: {
|
|
value,
|
|
currency
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update PIN code
|
|
*/
|
|
public async updatePin(pinCode: string): Promise<void> {
|
|
await this.bunqAccount.apiContext.ensureValidSession();
|
|
|
|
await this.bunqAccount.getHttpClient().put(
|
|
`/v1/user/${this.bunqAccount.userId}/card/${this.id}/pin-change`,
|
|
{
|
|
pin_code: pinCode
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get card limits
|
|
*/
|
|
public async getLimits(): Promise<any> {
|
|
await this.bunqAccount.apiContext.ensureValidSession();
|
|
|
|
const response = await this.bunqAccount.getHttpClient().list(
|
|
`/v1/user/${this.bunqAccount.userId}/limit`
|
|
);
|
|
|
|
return response.Response || [];
|
|
}
|
|
|
|
/**
|
|
* Update mag stripe permissions
|
|
*/
|
|
public async updateMagStripePermission(expiryTime?: string): Promise<void> {
|
|
await this.update({
|
|
mag_stripe_permission: {
|
|
expiry_time: expiryTime
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update country permissions
|
|
*/
|
|
public async updateCountryPermissions(permissions: Array<{country: string, expiryTime?: string}>): Promise<void> {
|
|
await this.update({
|
|
country_permission: permissions
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Link card to monetary account
|
|
*/
|
|
public async linkToAccount(monetaryAccountId: number): Promise<void> {
|
|
await this.update({
|
|
monetary_account_id: monetaryAccountId
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Order a new card
|
|
*/
|
|
public static async order(
|
|
bunqAccount: BunqAccount,
|
|
options: {
|
|
secondLine: string;
|
|
nameOnCard: string;
|
|
type?: 'MAESTRO' | 'MASTERCARD';
|
|
productType?: string;
|
|
monetaryAccountId?: number;
|
|
}
|
|
): Promise<BunqCard> {
|
|
await bunqAccount.apiContext.ensureValidSession();
|
|
|
|
const cardData = {
|
|
second_line: options.secondLine,
|
|
name_on_card: options.nameOnCard,
|
|
type: options.type || 'MASTERCARD',
|
|
product_type: options.productType || 'MASTERCARD_DEBIT',
|
|
monetary_account_id: options.monetaryAccountId
|
|
};
|
|
|
|
const cardType = options.type === 'MASTERCARD' ? 'CardCredit' : 'CardDebit';
|
|
|
|
const response = await bunqAccount.getHttpClient().post(
|
|
`/v1/user/${bunqAccount.userId}/card`,
|
|
{
|
|
[cardType]: cardData
|
|
}
|
|
);
|
|
|
|
if (response.Response && response.Response[0] && response.Response[0].Id) {
|
|
return BunqCard.get(bunqAccount, response.Response[0].Id.id);
|
|
}
|
|
|
|
throw new Error('Failed to order card');
|
|
}
|
|
} |