From 5fd172cae06cd3e8c124cb5f088a14f692f9b685 Mon Sep 17 00:00:00 2001 From: dragosp1011 Date: Fri, 25 Oct 2024 17:18:00 +0200 Subject: [PATCH 1/4] feat: save card transactions --- packages/shared/backend/src/model.ts | 4 +- packages/wallet/backend/src/card/service.ts | 8 +- packages/wallet/backend/src/gatehub/client.ts | 8 +- .../wallet/backend/src/transaction/service.ts | 82 ++++++++++++++++++- 4 files changed, 96 insertions(+), 6 deletions(-) diff --git a/packages/shared/backend/src/model.ts b/packages/shared/backend/src/model.ts index 3198326c0..f5c4b52b3 100644 --- a/packages/shared/backend/src/model.ts +++ b/packages/shared/backend/src/model.ts @@ -16,7 +16,9 @@ export abstract class BaseModel extends Model { public $beforeInsert(context: QueryContext): void { super.$beforeInsert(context) - this.createdAt = new Date() + if (!this.createdAt) { + this.createdAt = new Date() + } this.updatedAt = new Date() } diff --git a/packages/wallet/backend/src/card/service.ts b/packages/wallet/backend/src/card/service.ts index 88fee812e..fff11f43e 100644 --- a/packages/wallet/backend/src/card/service.ts +++ b/packages/wallet/backend/src/card/service.ts @@ -73,8 +73,14 @@ export class CardService { pageNumber?: number ): Promise { await this.ensureAccountExists(userId, cardId) + const { gateHubUserId } = await this.ensureGatehubUserUuid(userId) - return this.gateHubClient.getCardTransactions(cardId, pageSize, pageNumber) + return this.gateHubClient.getCardTransactions( + cardId, + gateHubUserId, + pageSize, + pageNumber + ) } async getCardLimits( diff --git a/packages/wallet/backend/src/gatehub/client.ts b/packages/wallet/backend/src/gatehub/client.ts index d00bf83ea..1383f5108 100644 --- a/packages/wallet/backend/src/gatehub/client.ts +++ b/packages/wallet/backend/src/gatehub/client.ts @@ -511,10 +511,11 @@ export class GateHubClient { async getCardTransactions( cardId: string, + userUuid: string, pageSize?: number, pageNumber?: number ): Promise { - let url = `${this.apiUrl}/v1/cards/${cardId}/transactions` + let url = `${this.apiUrl}/cards/v1/cards/${cardId}/transactions` const queryParams: string[] = [] @@ -529,7 +530,10 @@ export class GateHubClient { url += `?${queryParams.join('&')}` } - return this.request('GET', url) + return this.request('GET', url, undefined, { + cardAppId: this.env.GATEHUB_CARD_APP_ID, + managedUserUuid: userUuid + }) } async getCardLimits(cardId: string): Promise { diff --git a/packages/wallet/backend/src/transaction/service.ts b/packages/wallet/backend/src/transaction/service.ts index 63ea8a6a8..539669dbb 100644 --- a/packages/wallet/backend/src/transaction/service.ts +++ b/packages/wallet/backend/src/transaction/service.ts @@ -3,14 +3,18 @@ import { OrderByDirection, Page, PartialModelObject } from 'objection' import { AccountService } from '@/account/service' import { Logger } from 'winston' import { PaginationQueryParams } from '@/shared/types' -import { prefixSomeObjectKeys } from '@/utils/helpers' +import { prefixSomeObjectKeys, transformBalance } from '@/utils/helpers' import { Knex } from 'knex' import { IncomingPayment, OutgoingPayment } from '@/rafiki/backend/generated/graphql' import { WalletAddress } from '@/walletAddress/model' +import { Account } from '@/account/model' +import { CardService } from '@/card/service' +import NodeCache from 'node-cache' +const FETCHING_TRANSACTIONS_KEY = 'FETCHING_TRANSACTIONS' type ListAllTransactionsInput = { userId: string paginationParams: PaginationQueryParams @@ -34,10 +38,12 @@ export interface ITransactionService { } export class TransactionService implements ITransactionService { + cache: NodeCache = new NodeCache({ stdTTL: 30 }) constructor( private accountService: AccountService, private logger: Logger, - private knex: Knex + private knex: Knex, + private cardService: CardService ) {} async list( @@ -71,6 +77,10 @@ export class TransactionService implements ITransactionService { filterParams, orderByDate }: ListAllTransactionsInput): Promise> { + if (page === 0) { + await this.fetchCardTransactions(userId) + } + const filterParamsWithTableNames = prefixSomeObjectKeys( filterParams, ['walletAddressId', 'assetCode', 'type', 'status', 'accountId'], @@ -95,6 +105,74 @@ export class TransactionService implements ITransactionService { return transactions } + async fetchCardTransactions(userId: string) { + const key = `${FETCHING_TRANSACTIONS_KEY}-${userId}` + if (this.cache.has(key)) { + return + } + this.cache.set(key, true) + + const account = await Account.query().findOne({ userId, assetCode: 'EUR' }) + if (!account?.cardId) { + return + } + + const latestTransaction: Transaction | undefined = await Transaction.query() + .findOne({ accountId: account.id, isCard: true }) + .orderBy('createdAt', 'DESC') + + const walletAddress = await WalletAddress.query().findOne({ + accountId: account.id + // isCard: true + }) + + if (!walletAddress) { + return + } + + const page = 1 + const pageSize = 10 + let shouldFetchNext = true + while (shouldFetchNext) { + const transactionsResponse = await this.cardService.getCardTransactions( + userId, + account.cardId, + pageSize, + page + ) + + if (transactionsResponse.data.length === 0) { + return + } + + const newTransactions = transactionsResponse.data.filter( + (transaction) => + !latestTransaction || + latestTransaction.createdAt.toISOString() <= transaction.createdAt + ) + if (transactionsResponse.data.length > newTransactions.length) { + shouldFetchNext = false + } + + const transactionsToSave: Partial[] = newTransactions.map( + (transaction) => ({ + walletAddressId: walletAddress.id, + accountId: walletAddress.accountId, + paymentId: transaction.transactionId, + assetCode: transaction.billingCurrency, + value: transformBalance(Number(transaction.billingAmount), 2), + type: 'OUTGOING', + status: 'COMPLETED', + description: '', + isCard: true, + createdAt: new Date(transaction.createdAt) + }) + ) + + await Transaction.query().insert(transactionsToSave) + } + } + async processPendingIncomingPayments(): Promise { return this.knex.transaction(async (trx) => { // Giving a Rafiki a little more time to process the payments before we process them. From 03ac7ddc5fd5bb7fb58aac353ac067cf210a005d Mon Sep 17 00:00:00 2001 From: dragosp1011 Date: Fri, 25 Oct 2024 17:19:59 +0200 Subject: [PATCH 2/4] fix: query for is card --- packages/wallet/backend/src/transaction/service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/wallet/backend/src/transaction/service.ts b/packages/wallet/backend/src/transaction/service.ts index 539669dbb..30e24ab03 100644 --- a/packages/wallet/backend/src/transaction/service.ts +++ b/packages/wallet/backend/src/transaction/service.ts @@ -122,8 +122,8 @@ export class TransactionService implements ITransactionService { .orderBy('createdAt', 'DESC') const walletAddress = await WalletAddress.query().findOne({ - accountId: account.id - // isCard: true + accountId: account.id, + isCard: true }) if (!walletAddress) { From ea8db48497a85217708cd41bf2dd32629a5fd63f Mon Sep 17 00:00:00 2001 From: dragosp1011 Date: Fri, 25 Oct 2024 17:21:36 +0200 Subject: [PATCH 3/4] feat: increase page number --- packages/wallet/backend/src/transaction/service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/wallet/backend/src/transaction/service.ts b/packages/wallet/backend/src/transaction/service.ts index 30e24ab03..0b0f763c1 100644 --- a/packages/wallet/backend/src/transaction/service.ts +++ b/packages/wallet/backend/src/transaction/service.ts @@ -130,7 +130,7 @@ export class TransactionService implements ITransactionService { return } - const page = 1 + let page = 1 const pageSize = 10 let shouldFetchNext = true while (shouldFetchNext) { @@ -153,6 +153,7 @@ export class TransactionService implements ITransactionService { if (transactionsResponse.data.length > newTransactions.length) { shouldFetchNext = false } + page++; const transactionsToSave: Partial[] = newTransactions.map( (transaction) => ({ From 9accb772ccfbbe431f257eea51c635aa70c4da72 Mon Sep 17 00:00:00 2001 From: dragosp1011 Date: Fri, 25 Oct 2024 18:06:00 +0200 Subject: [PATCH 4/4] fix: format --- packages/wallet/backend/src/transaction/service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wallet/backend/src/transaction/service.ts b/packages/wallet/backend/src/transaction/service.ts index 0b0f763c1..67b95316f 100644 --- a/packages/wallet/backend/src/transaction/service.ts +++ b/packages/wallet/backend/src/transaction/service.ts @@ -153,7 +153,7 @@ export class TransactionService implements ITransactionService { if (transactionsResponse.data.length > newTransactions.length) { shouldFetchNext = false } - page++; + page++ const transactionsToSave: Partial[] = newTransactions.map( (transaction) => ({