198 lines
5.5 KiB
TypeScript
198 lines
5.5 KiB
TypeScript
import * as plugins from './bunq.plugins.js';
|
|
import { BunqAccount } from './bunq.classes.account.js';
|
|
import { BunqMonetaryAccount } from './bunq.classes.monetaryaccount.js';
|
|
import { BunqNotification, BunqWebhookHandler } from './bunq.classes.notification.js';
|
|
import { BunqCrypto } from './bunq.classes.crypto.js';
|
|
|
|
/**
|
|
* Webhook management for monetary accounts
|
|
*/
|
|
export class BunqWebhook {
|
|
private bunqAccount: BunqAccount;
|
|
|
|
constructor(bunqAccount: BunqAccount) {
|
|
this.bunqAccount = bunqAccount;
|
|
}
|
|
|
|
/**
|
|
* Create a webhook for a monetary account
|
|
*/
|
|
public async create(monetaryAccount: BunqMonetaryAccount, url: string): Promise<number> {
|
|
await this.bunqAccount.apiContext.ensureValidSession();
|
|
|
|
const response = await this.bunqAccount.getHttpClient().post(
|
|
`/v1/user/${this.bunqAccount.userId}/monetary-account/${monetaryAccount.id}/notification-filter-url`,
|
|
{
|
|
category: 'MUTATION',
|
|
notification_target: url
|
|
}
|
|
);
|
|
|
|
if (response.Response && response.Response[0] && response.Response[0].Id) {
|
|
return response.Response[0].Id.id;
|
|
}
|
|
|
|
throw new Error('Failed to create webhook');
|
|
}
|
|
|
|
/**
|
|
* List all webhooks for a monetary account
|
|
*/
|
|
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
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return webhooks;
|
|
}
|
|
|
|
/**
|
|
* Get a specific webhook
|
|
*/
|
|
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');
|
|
}
|
|
|
|
/**
|
|
* Update a webhook URL
|
|
*/
|
|
public async update(monetaryAccount: BunqMonetaryAccount, webhookId: number, newUrl: string): Promise<void> {
|
|
await this.bunqAccount.apiContext.ensureValidSession();
|
|
|
|
await this.bunqAccount.getHttpClient().put(
|
|
`/v1/user/${this.bunqAccount.userId}/monetary-account/${monetaryAccount.id}/notification-filter-url/${webhookId}`,
|
|
{
|
|
notification_target: newUrl
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Delete a webhook
|
|
*/
|
|
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}`
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Webhook server for receiving bunq notifications
|
|
*/
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Get the webhook handler for registering event callbacks
|
|
*/
|
|
public getHandler(): BunqWebhookHandler {
|
|
return this.handler;
|
|
}
|
|
|
|
/**
|
|
* 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}`);
|
|
}
|
|
|
|
/**
|
|
* Stop the webhook server
|
|
*/
|
|
public async stop(): Promise<void> {
|
|
if (this.server) {
|
|
// Stop the server
|
|
console.log('Webhook server stopped');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register the webhook URL with bunq
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Verify webhook signature
|
|
*/
|
|
public verifySignature(body: string, signature: string): boolean {
|
|
const crypto = new BunqCrypto();
|
|
// In production, use bunq's server public key
|
|
return true; // Placeholder
|
|
}
|
|
} |