Files
bunq/ts/bunq.classes.webhook.ts

198 lines
5.5 KiB
TypeScript
Raw Normal View History

2025-07-18 10:43:39 +00:00
import * as plugins from './bunq.plugins.js';
import { BunqAccount } from './bunq.classes.account.js';
2025-07-18 12:10:29 +00:00
import { BunqMonetaryAccount } from './bunq.classes.monetaryaccount.js';
2025-07-18 10:43:39 +00:00
import { BunqNotification, BunqWebhookHandler } from './bunq.classes.notification.js';
import { BunqCrypto } from './bunq.classes.crypto.js';
2025-07-18 10:31:12 +00:00
/**
2025-07-18 12:10:29 +00:00
* Webhook management for monetary accounts
2025-07-18 10:31:12 +00:00
*/
2025-07-18 12:10:29 +00:00
export class BunqWebhook {
2025-07-18 10:31:12 +00:00
private bunqAccount: BunqAccount;
2025-07-18 12:10:29 +00:00
constructor(bunqAccount: BunqAccount) {
2025-07-18 10:31:12 +00:00
this.bunqAccount = bunqAccount;
}
/**
2025-07-18 12:10:29 +00:00
* Create a webhook for a monetary account
2025-07-18 10:31:12 +00:00
*/
2025-07-18 12:10:29 +00:00
public async create(monetaryAccount: BunqMonetaryAccount, url: string): Promise<number> {
await this.bunqAccount.apiContext.ensureValidSession();
2025-07-18 10:31:12 +00:00
2025-07-18 12:10:29 +00:00
const response = await this.bunqAccount.getHttpClient().post(
`/v1/user/${this.bunqAccount.userId}/monetary-account/${monetaryAccount.id}/notification-filter-url`,
{
category: 'MUTATION',
notification_target: url
2025-07-18 10:31:12 +00:00
}
2025-07-18 12:10:29 +00:00
);
2025-07-18 10:31:12 +00:00
2025-07-18 12:10:29 +00:00
if (response.Response && response.Response[0] && response.Response[0].Id) {
return response.Response[0].Id.id;
}
throw new Error('Failed to create webhook');
2025-07-18 10:31:12 +00:00
}
/**
2025-07-18 12:10:29 +00:00
* List all webhooks for a monetary account
2025-07-18 10:31:12 +00:00
*/
2025-07-18 12:10:29 +00:00
public async list(monetaryAccount: BunqMonetaryAccount): Promise<Array<{
id: number;
url: string;
category: string;
}>> {
await this.bunqAccount.apiContext.ensureValidSession();
const response = await this.bunqAccount.getHttpClient().list(
`/v1/user/${this.bunqAccount.userId}/monetary-account/${monetaryAccount.id}/notification-filter-url`
);
const webhooks: Array<{
id: number;
url: string;
category: string;
}> = [];
if (response.Response) {
for (const item of response.Response) {
if (item.NotificationFilterUrl) {
webhooks.push({
id: item.NotificationFilterUrl.id,
url: item.NotificationFilterUrl.notification_target,
category: item.NotificationFilterUrl.category
});
}
}
2025-07-18 10:31:12 +00:00
}
2025-07-18 12:10:29 +00:00
return webhooks;
2025-07-18 10:31:12 +00:00
}
/**
2025-07-18 12:10:29 +00:00
* Get a specific webhook
2025-07-18 10:31:12 +00:00
*/
2025-07-18 12:10:29 +00:00
public async get(monetaryAccount: BunqMonetaryAccount, webhookId: number): Promise<{
id: number;
url: string;
category: string;
}> {
await this.bunqAccount.apiContext.ensureValidSession();
const response = await this.bunqAccount.getHttpClient().get(
`/v1/user/${this.bunqAccount.userId}/monetary-account/${monetaryAccount.id}/notification-filter-url/${webhookId}`
);
if (response.Response && response.Response[0] && response.Response[0].NotificationFilterUrl) {
const webhook = response.Response[0].NotificationFilterUrl;
return {
id: webhook.id,
url: webhook.notification_target,
category: webhook.category
};
}
throw new Error('Webhook not found');
2025-07-18 10:31:12 +00:00
}
/**
2025-07-18 12:10:29 +00:00
* Update a webhook URL
2025-07-18 10:31:12 +00:00
*/
2025-07-18 12:10:29 +00:00
public async update(monetaryAccount: BunqMonetaryAccount, webhookId: number, newUrl: string): Promise<void> {
await this.bunqAccount.apiContext.ensureValidSession();
2025-07-18 10:31:12 +00:00
2025-07-18 12:10:29 +00:00
await this.bunqAccount.getHttpClient().put(
`/v1/user/${this.bunqAccount.userId}/monetary-account/${monetaryAccount.id}/notification-filter-url/${webhookId}`,
{
notification_target: newUrl
2025-07-18 12:10:29 +00:00
}
);
2025-07-18 10:31:12 +00:00
}
/**
2025-07-18 12:10:29 +00:00
* Delete a webhook
2025-07-18 10:31:12 +00:00
*/
2025-07-18 12:10:29 +00:00
public async delete(monetaryAccount: BunqMonetaryAccount, webhookId: number): Promise<void> {
await this.bunqAccount.apiContext.ensureValidSession();
await this.bunqAccount.getHttpClient().delete(
`/v1/user/${this.bunqAccount.userId}/monetary-account/${monetaryAccount.id}/notification-filter-url/${webhookId}`
);
2025-07-18 10:31:12 +00:00
}
}
/**
2025-07-18 12:10:29 +00:00
* Webhook server for receiving bunq notifications
2025-07-18 10:31:12 +00:00
*/
2025-07-18 12:10:29 +00:00
export class BunqWebhookServer {
private bunqAccount: BunqAccount;
private notification: BunqNotification;
private handler: BunqWebhookHandler;
private server?: any; // HTTP server instance
private port: number;
private path: string;
private publicUrl: string;
2025-07-18 10:31:12 +00:00
2025-07-18 12:10:29 +00:00
constructor(
bunqAccount: BunqAccount,
options: {
port?: number;
path?: string;
publicUrl: string;
}
) {
this.bunqAccount = bunqAccount;
this.notification = new BunqNotification(bunqAccount);
this.handler = new BunqWebhookHandler();
this.port = options.port || 3000;
this.path = options.path || '/webhook';
this.publicUrl = options.publicUrl;
2025-07-18 10:31:12 +00:00
}
/**
2025-07-18 12:10:29 +00:00
* Get the webhook handler for registering event callbacks
2025-07-18 10:31:12 +00:00
*/
2025-07-18 12:10:29 +00:00
public getHandler(): BunqWebhookHandler {
return this.handler;
}
2025-07-18 10:31:12 +00:00
2025-07-18 12:10:29 +00:00
/**
* Start the webhook server
*/
public async start(): Promise<void> {
// Implementation would use an HTTP server library
// For now, this is a placeholder
console.log(`Webhook server would start on port ${this.port}`);
}
2025-07-18 10:31:12 +00:00
2025-07-18 12:10:29 +00:00
/**
* Stop the webhook server
*/
public async stop(): Promise<void> {
if (this.server) {
// Stop the server
console.log('Webhook server stopped');
2025-07-18 10:31:12 +00:00
}
}
/**
2025-07-18 12:10:29 +00:00
* Register the webhook URL with bunq
2025-07-18 10:31:12 +00:00
*/
2025-07-18 12:10:29 +00:00
public async register(): Promise<void> {
const webhookUrl = `${this.publicUrl}${this.path}`;
// Register for all payment-related events
await this.notification.setupPaymentWebhook(webhookUrl);
// Register for all account-related events
await this.notification.setupAccountWebhook(webhookUrl);
2025-07-18 10:31:12 +00:00
}
/**
2025-07-18 12:10:29 +00:00
* Verify webhook signature
2025-07-18 10:31:12 +00:00
*/
2025-07-18 12:10:29 +00:00
public verifySignature(body: string, signature: string): boolean {
const crypto = new BunqCrypto();
// In production, use bunq's server public key
return true; // Placeholder
2025-07-18 10:31:12 +00:00
}
}