diff --git a/changelog.md b/changelog.md index ac22caf..c594a07 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,17 @@ # Changelog +## 2025-07-25 - 4.1.0 - feat(transactions) +Enhanced transaction pagination support with full control over historical data retrieval + +- Added full `IBunqPaginationOptions` support to `getTransactions()` method +- Now supports `older_id` for paginating backwards through historical transactions +- Supports custom `count` parameter (defaults to 200) +- Maintains backward compatibility - passing a number is still treated as `newer_id` +- Only includes pagination parameters that are explicitly set (not false/undefined) +- Added `example.pagination.ts` demonstrating various pagination patterns + +This enhancement allows banking applications to properly fetch and paginate through historical transaction data using both `newer_id` and `older_id` parameters. + ## 2025-07-25 - 4.0.0 - BREAKING CHANGE(core) Complete stateless architecture - consumers now have full control over session persistence diff --git a/example.pagination.ts b/example.pagination.ts new file mode 100644 index 0000000..c2b6146 --- /dev/null +++ b/example.pagination.ts @@ -0,0 +1,128 @@ +import { BunqAccount, IBunqPaginationOptions } from './ts/index.js'; + +// Example demonstrating the enhanced pagination support in getTransactions + +async function demonstratePagination() { + const bunq = new BunqAccount({ + apiKey: 'your-api-key', + deviceName: 'Pagination Demo', + environment: 'PRODUCTION', + }); + + // Initialize and get session + const sessionData = await bunq.init(); + + // Get accounts + const { accounts } = await bunq.getAccounts(); + const account = accounts[0]; + + // Example 1: Get most recent transactions (default behavior) + const recentTransactions = await account.getTransactions(); + console.log(`Got ${recentTransactions.length} recent transactions`); + + // Example 2: Get transactions with custom count + const smallBatch = await account.getTransactions({ count: 10 }); + console.log(`Got ${smallBatch.length} transactions with custom count`); + + // Example 3: Get older transactions using older_id + if (recentTransactions.length > 0) { + const oldestTransaction = recentTransactions[recentTransactions.length - 1]; + const olderTransactions = await account.getTransactions({ + count: 50, + older_id: oldestTransaction.id + }); + console.log(`Got ${olderTransactions.length} older transactions`); + } + + // Example 4: Get newer transactions using newer_id + if (recentTransactions.length > 0) { + const newestTransaction = recentTransactions[0]; + const newerTransactions = await account.getTransactions({ + count: 20, + newer_id: newestTransaction.id + }); + console.log(`Got ${newerTransactions.length} newer transactions`); + } + + // Example 5: Backward compatibility - using number as newer_id + const backwardCompatible = await account.getTransactions(12345); + console.log(`Backward compatible call returned ${backwardCompatible.length} transactions`); + + // Example 6: Paginating through all historical transactions + async function getAllTransactions(account: any): Promise { + const allTransactions: any[] = []; + let lastTransactionId: number | false = false; + let hasMore = true; + + while (hasMore) { + const options: IBunqPaginationOptions = { + count: 200, + older_id: lastTransactionId + }; + + const batch = await account.getTransactions(options); + + if (batch.length === 0) { + hasMore = false; + } else { + allTransactions.push(...batch); + lastTransactionId = batch[batch.length - 1].id; + console.log(`Fetched ${batch.length} transactions, total: ${allTransactions.length}`); + } + } + + return allTransactions; + } + + // Example 7: Getting transactions between two dates + async function getTransactionsBetweenDates( + account: any, + startDate: Date, + endDate: Date + ): Promise { + const transactions: any[] = []; + let olderId: number | false = false; + let keepFetching = true; + + while (keepFetching) { + const batch = await account.getTransactions({ + count: 200, + older_id: olderId + }); + + if (batch.length === 0) { + keepFetching = false; + break; + } + + for (const transaction of batch) { + const transactionDate = new Date(transaction.created); + + if (transactionDate >= startDate && transactionDate <= endDate) { + transactions.push(transaction); + } else if (transactionDate < startDate) { + // We've gone past our date range + keepFetching = false; + break; + } + } + + olderId = batch[batch.length - 1].id; + } + + return transactions; + } + + // Usage + const lastMonth = new Date(); + lastMonth.setMonth(lastMonth.getMonth() - 1); + const transactionsLastMonth = await getTransactionsBetweenDates( + account, + lastMonth, + new Date() + ); + console.log(`Found ${transactionsLastMonth.length} transactions in the last month`); +} + +// Run the demo +demonstratePagination().catch(console.error); \ No newline at end of file diff --git a/package.json b/package.json index b2dba38..25e3a4d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@apiclient.xyz/bunq", - "version": "4.0.1", + "version": "4.1.0", "private": false, "description": "A full-featured TypeScript/JavaScript client for the bunq API", "type": "module", diff --git a/ts/bunq.classes.monetaryaccount.ts b/ts/bunq.classes.monetaryaccount.ts index b768ee5..c72b585 100644 --- a/ts/bunq.classes.monetaryaccount.ts +++ b/ts/bunq.classes.monetaryaccount.ts @@ -111,18 +111,41 @@ export class BunqMonetaryAccount { /** * gets all transactions on this account + * @param options - Pagination options or a number for backward compatibility (treated as newer_id) */ - public async getTransactions(startingIdArg: number | false = false): Promise { - const paginationOptions: IBunqPaginationOptions = { - count: 200, - newer_id: startingIdArg, + public async getTransactions(options?: IBunqPaginationOptions | number | false): Promise { + let paginationOptions: IBunqPaginationOptions = {}; + + // Backward compatibility: if a number or false is passed, treat it as newer_id + if (typeof options === 'number' || options === false) { + paginationOptions.newer_id = options; + } else if (options) { + paginationOptions = { ...options }; + } + + // Set default count if not specified + if (!paginationOptions.count) { + paginationOptions.count = 200; + } + + // Build clean pagination object - only include properties that are not false/undefined + const cleanPaginationOptions: IBunqPaginationOptions = { + count: paginationOptions.count, }; + + if (paginationOptions.newer_id !== undefined && paginationOptions.newer_id !== false) { + cleanPaginationOptions.newer_id = paginationOptions.newer_id; + } + + if (paginationOptions.older_id !== undefined && paginationOptions.older_id !== false) { + cleanPaginationOptions.older_id = paginationOptions.older_id; + } await this.bunqAccountRef.apiContext.ensureValidSession(); const response = await this.bunqAccountRef.getHttpClient().list( `/v1/user/${this.bunqAccountRef.userId}/monetary-account/${this.id}/payment`, - paginationOptions + cleanPaginationOptions ); const transactionsArray: BunqTransaction[] = [];