Skip to content

Commit

Permalink
feat: save card transactions (#1779)
Browse files Browse the repository at this point in the history
* feat: save card transactions

* fix: query for is card

* feat: increase page number

* fix: format
  • Loading branch information
dragosp1011 authored Oct 25, 2024
1 parent 9fe374b commit 0dce648
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 6 deletions.
4 changes: 3 additions & 1 deletion packages/shared/backend/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

Expand Down
8 changes: 7 additions & 1 deletion packages/wallet/backend/src/card/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,14 @@ export class CardService {
pageNumber?: number
): Promise<IGetTransactionsResponse> {
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(
Expand Down
8 changes: 6 additions & 2 deletions packages/wallet/backend/src/gatehub/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,10 +511,11 @@ export class GateHubClient {

async getCardTransactions(
cardId: string,
userUuid: string,
pageSize?: number,
pageNumber?: number
): Promise<IGetTransactionsResponse> {
let url = `${this.apiUrl}/v1/cards/${cardId}/transactions`
let url = `${this.apiUrl}/cards/v1/cards/${cardId}/transactions`

const queryParams: string[] = []

Expand All @@ -529,7 +530,10 @@ export class GateHubClient {
url += `?${queryParams.join('&')}`
}

return this.request<IGetTransactionsResponse>('GET', url)
return this.request<IGetTransactionsResponse>('GET', url, undefined, {
cardAppId: this.env.GATEHUB_CARD_APP_ID,
managedUserUuid: userUuid
})
}

async getCardLimits(cardId: string): Promise<ICardLimitResponse[]> {
Expand Down
83 changes: 81 additions & 2 deletions packages/wallet/backend/src/transaction/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -71,6 +77,10 @@ export class TransactionService implements ITransactionService {
filterParams,
orderByDate
}: ListAllTransactionsInput): Promise<Page<Transaction>> {
if (page === 0) {
await this.fetchCardTransactions(userId)
}

const filterParamsWithTableNames = prefixSomeObjectKeys(
filterParams,
['walletAddressId', 'assetCode', 'type', 'status', 'accountId'],
Expand All @@ -95,6 +105,75 @@ 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
}

let 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
}
page++

const transactionsToSave: Partial<Transaction>[] = 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<string | undefined> {
return this.knex.transaction(async (trx) => {
// Giving a Rafiki a little more time to process the payments before we process them.
Expand Down

0 comments on commit 0dce648

Please sign in to comment.