diff --git a/packages/wallet/backend/src/backfillTrxDetails.ts b/packages/wallet/backend/src/backfillTrxDetails.ts new file mode 100644 index 000000000..aa0714a58 --- /dev/null +++ b/packages/wallet/backend/src/backfillTrxDetails.ts @@ -0,0 +1,124 @@ +import { createContainer } from '@/createContainer' +import { env } from '@/config/env' +import { Account } from '@/account/model' +import { Transaction } from '@/transaction/model' +import { Model } from 'objection' +import { transformBalance, urlToPaymentId } from '@/utils/helpers' + +async function backfillTrxDetails() { + const container = await createContainer(env) + const knex = container.resolve('knex') + Model.knex(knex) + const rafikiClient = container.resolve('rafikiClient') + const rafikiService = container.resolve('rafikiService') + const gateHubClient = container.resolve('gateHubClient') + + async function getOutgoingPaymentSecondParty(paymentId: string) { + try { + const outgoingPayment = + await rafikiClient.getOutgoingPaymentById(paymentId) + + return rafikiService.getOutgoingPaymentSecondPartyByIncomingPaymentId( + urlToPaymentId(outgoingPayment.receiver) + ) + } catch (e) { + console.warn('Error on getting receiver wallet address', e) + } + } + + async function getIncomingPaymentSecondParty(paymentId: string) { + return rafikiService.getIncomingPaymentSenders(paymentId) + } + + async function backfillCardTrx(account: Account) { + if (!account.cardId || !account.user?.gateHubUserId) { + return + } + + let page = 1 + const pageSize = 10 + let shouldFetchNext = true + while (shouldFetchNext) { + const transactionsResponse = await gateHubClient.getCardTransactions( + account.cardId, + account.user.gateHubUserId, + pageSize, + page + ) + + if (transactionsResponse.data.length < pageSize) { + shouldFetchNext = false + } + + for (const cardTrx of transactionsResponse.data) { + console.log('processing trx: ', cardTrx.id) + const existentTrx = await Transaction.query().findOne( + 'paymentId', + cardTrx.id + ) + if (!existentTrx) { + console.log('trx not found: ', cardTrx.id) + continue + } + + await Transaction.query() + .where('id', existentTrx.id) + .update({ + secondParty: cardTrx.merchantName, + txAmount: cardTrx.transactionAmount + ? transformBalance(Number(cardTrx.transactionAmount), 2) + : undefined, + conversionRate: cardTrx.mastercardConversion?.convRate, + txCurrency: cardTrx.transactionCurrency, + cardTxType: cardTrx.type + }) + console.log('trx updated: ', existentTrx.paymentId) + } + + page++ + } + } + + try { + const accounts = await Account.query().withGraphFetched('user') + + for (const account of accounts) { + console.log('processing account: ', account) + const rafikiTransactions = await Transaction.query() + .where('accountId', account.id) + .whereNull('isCard') + for (const tx of rafikiTransactions) { + console.log('processing trx: ', tx) + if (!tx.walletAddressId) { + continue + } + + let secondParty + if (tx.type === 'INCOMING') { + secondParty = await getIncomingPaymentSecondParty(tx.paymentId) + } else { + secondParty = await getOutgoingPaymentSecondParty(tx.paymentId) + } + await Transaction.query().where('id', tx.id).update({ secondParty }) + console.log('trx updated') + } + + await backfillCardTrx(account) + } + } catch (error: unknown) { + console.log(`An error occurred: ${(error as Error).message}`) + } finally { + await knex.destroy() + await container.dispose() + } +} + +backfillTrxDetails() + .then(() => { + console.log('finished') + process.exit(0) + }) + .catch((error) => { + console.error(`Script failed: ${error.message}`) + process.exit(1) + }) diff --git a/packages/wallet/backend/src/rafiki/backend/generated/graphql.ts b/packages/wallet/backend/src/rafiki/backend/generated/graphql.ts index 78aba55d4..12d868460 100644 --- a/packages/wallet/backend/src/rafiki/backend/generated/graphql.ts +++ b/packages/wallet/backend/src/rafiki/backend/generated/graphql.ts @@ -1637,6 +1637,13 @@ export type CreateIncomingPaymentMutationVariables = Exact<{ export type CreateIncomingPaymentMutation = { __typename?: 'Mutation', createIncomingPayment: { __typename?: 'IncomingPaymentResponse', payment?: { __typename?: 'IncomingPayment', createdAt: string, metadata?: any | null, expiresAt: string, id: string, walletAddressId: string, state: IncomingPaymentState, incomingAmount?: { __typename?: 'Amount', assetCode: string, assetScale: number, value: bigint } | null, receivedAmount: { __typename?: 'Amount', assetCode: string, assetScale: number, value: bigint } } | null } }; +export type GetIncomingPaymentQueryVariables = Exact<{ + id: Scalars['String']['input']; +}>; + + +export type GetIncomingPaymentQuery = { __typename?: 'Query', incomingPayment?: { __typename?: 'IncomingPayment', id: string, walletAddressId: string } | null }; + export type WithdrawLiquidityMutationVariables = Exact<{ eventId: Scalars['String']['input']; idempotencyKey: Scalars['String']['input']; @@ -1671,6 +1678,13 @@ export type GetOutgoingPaymentsQueryVariables = Exact<{ export type GetOutgoingPaymentsQuery = { __typename?: 'Query', outgoingPayments: { __typename?: 'OutgoingPaymentConnection', edges: Array<{ __typename?: 'OutgoingPaymentEdge', cursor: string, node: { __typename?: 'OutgoingPayment', id: string, walletAddressId: string, receiver: string, grantId?: string | null, state: OutgoingPaymentState, createdAt: string, sentAmount: { __typename?: 'Amount', assetCode: string, assetScale: number, value: bigint } } }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } }; +export type GetOutgoingPaymentQueryVariables = Exact<{ + id: Scalars['String']['input']; +}>; + + +export type GetOutgoingPaymentQuery = { __typename?: 'Query', outgoingPayment?: { __typename?: 'OutgoingPayment', id: string, walletAddressId: string, receiver: string, grantId?: string | null, state: OutgoingPaymentState, createdAt: string, sentAmount: { __typename?: 'Amount', assetCode: string, assetScale: number, value: bigint } } | null }; + export type CreateQuoteMutationVariables = Exact<{ input: CreateQuoteInput; }>; diff --git a/packages/wallet/backend/src/rafiki/backend/request/incoming-payment.request.ts b/packages/wallet/backend/src/rafiki/backend/request/incoming-payment.request.ts index 7176be0f6..4f5c69af8 100644 --- a/packages/wallet/backend/src/rafiki/backend/request/incoming-payment.request.ts +++ b/packages/wallet/backend/src/rafiki/backend/request/incoming-payment.request.ts @@ -24,3 +24,12 @@ export const createIncomingPaymentMutation = gql` } } ` + +export const getIncomingPaymentQuery = gql` + query GetIncomingPaymentQuery($id: String!) { + incomingPayment(id: $id) { + id + walletAddressId + } + } +` diff --git a/packages/wallet/backend/src/rafiki/backend/request/outgoing-payment.request.ts b/packages/wallet/backend/src/rafiki/backend/request/outgoing-payment.request.ts index b9bf5caa7..165f56e33 100644 --- a/packages/wallet/backend/src/rafiki/backend/request/outgoing-payment.request.ts +++ b/packages/wallet/backend/src/rafiki/backend/request/outgoing-payment.request.ts @@ -91,6 +91,24 @@ export const getOutgoingPayments = gql` } ` +export const getOutgoingPaymentQuery = gql` + query GetOutgoingPaymentQuery($id: String!) { + outgoingPayment(id: $id) { + id + walletAddressId + receiver + grantId + sentAmount { + assetCode + assetScale + value + } + state + createdAt + } + } +` + export type OutgoingPaymentsGqlResponse = Pick< OutgoingPayment, | 'id' diff --git a/packages/wallet/backend/src/rafiki/rafiki-client.ts b/packages/wallet/backend/src/rafiki/rafiki-client.ts index 121958acc..f0104dbff 100644 --- a/packages/wallet/backend/src/rafiki/rafiki-client.ts +++ b/packages/wallet/backend/src/rafiki/rafiki-client.ts @@ -45,20 +45,28 @@ import { GetReceiverQueryVariables, QueryOutgoingPaymentsArgs, GetOutgoingPaymentsQuery, - GetOutgoingPaymentsQueryVariables + GetOutgoingPaymentsQueryVariables, + GetOutgoingPaymentQuery, + GetOutgoingPaymentQueryVariables, + GetIncomingPaymentQuery, + GetIncomingPaymentQueryVariables } from './backend/generated/graphql' import { createAssetMutation, getAssetQuery, getAssetsQuery } from './backend/request/asset.request' -import { createIncomingPaymentMutation } from './backend/request/incoming-payment.request' +import { + createIncomingPaymentMutation, + getIncomingPaymentQuery +} from './backend/request/incoming-payment.request' import { depositLiquidityMutation, withdrawLiquidityMutation } from './backend/request/liquidity.request' import { createOutgoingPaymentMutation, + getOutgoingPaymentQuery, getOutgoingPayments, OutgoingPaymentsGqlResponse } from './backend/request/outgoing-payment.request' @@ -417,4 +425,24 @@ export class RafikiClient implements IRafikiClient { (el: { node: OutgoingPaymentsGqlResponse }) => el.node ) } + + public async getOutgoingPaymentById( + id: string + ): Promise { + const response = await this.backendGraphQLClient.request< + GetOutgoingPaymentQuery, + GetOutgoingPaymentQueryVariables + >(getOutgoingPaymentQuery, { id }) + + return response.outgoingPayment as OutgoingPaymentsGqlResponse + } + + public async getIncomingPaymentById(id: string): Promise { + const response = await this.backendGraphQLClient.request< + GetIncomingPaymentQuery, + GetIncomingPaymentQueryVariables + >(getIncomingPaymentQuery, { id }) + + return response.incomingPayment as IncomingPayment + } } diff --git a/packages/wallet/backend/src/rafiki/service.ts b/packages/wallet/backend/src/rafiki/service.ts index 812a1de25..21832824d 100644 --- a/packages/wallet/backend/src/rafiki/service.ts +++ b/packages/wallet/backend/src/rafiki/service.ts @@ -231,28 +231,7 @@ export class RafikiService implements IRafikiService { await this.rafikiClient.withdrawLiqudity(wh.id) - let senders - try { - const outgoingPayments = - await this.rafikiClient.getOutgoingPaymentsByReceiver( - `${this.env.OPEN_PAYMENTS_HOST}/incoming-payments/${wh.data.id}` - ) - - const walletAddressIds = outgoingPayments.map( - (payment) => payment.walletAddressId - ) - const walletAddresses = - await this.walletAddressService.getByIds(walletAddressIds) - senders = walletAddresses - .filter((wa) => wa.account?.user) - .map((wa) => `${wa.account.user.firstName} ${wa.account.user.lastName}`) - .join(', ') - } catch (e) { - this.logger.warn( - 'Error on getting outgoing payments by incoming payment', - e - ) - } + const senders = await this.getIncomingPaymentSenders(wh.data.id) await this.transactionService.updateTransaction( { paymentId: wh.data.id }, @@ -295,19 +274,9 @@ export class RafikiService implements IRafikiService { return } - let secondParty - try { - const receiver = await this.rafikiClient.getReceiverById(wh.data.receiver) - const receiverWA = await this.walletAddressService.getByUrl( - receiver.walletAddressUrl - ) - - if (receiverWA?.account?.user) { - secondParty = `${receiverWA.account.user.firstName} ${receiverWA.account.user.lastName}` - } - } catch (e) { - this.logger.warn('Error on getting receiver wallet address', e) - } + const secondParty = await this.getOutgoingPaymentSecondPartyByReceiver( + wh.data.receiver + ) await this.transactionService.createOutgoingTransaction( wh.data, @@ -457,4 +426,60 @@ export class RafikiService implements IRafikiService { gateHubUserId: account.user.gateHubUserId } } + + async getIncomingPaymentSenders(id: string) { + try { + const outgoingPayments = + await this.rafikiClient.getOutgoingPaymentsByReceiver( + `${this.env.OPEN_PAYMENTS_HOST}/incoming-payments/${id}` + ) + + const walletAddressIds = outgoingPayments.map( + (payment) => payment.walletAddressId + ) + const walletAddresses = + await this.walletAddressService.getByIds(walletAddressIds) + // return senders + return walletAddresses + .filter((wa) => wa.account?.user) + .map((wa) => `${wa.account.user.firstName} ${wa.account.user.lastName}`) + .join(', ') + } catch (e) { + this.logger.warn( + 'Error on getting outgoing payments by incoming payment', + e + ) + } + } + + async getOutgoingPaymentSecondPartyByReceiver(receiverId: string) { + try { + const receiver = await this.rafikiClient.getReceiverById(receiverId) + const receiverWA = await this.walletAddressService.getByUrl( + receiver.walletAddressUrl + ) + + if (receiverWA?.account?.user) { + return `${receiverWA.account.user.firstName} ${receiverWA.account.user.lastName}` + } + } catch (e) { + this.logger.warn('Error on getting receiver wallet address', e) + } + } + + async getOutgoingPaymentSecondPartyByIncomingPaymentId(paymentId: string) { + try { + const receiver = await this.rafikiClient.getIncomingPaymentById(paymentId) + + const receiverWA = await this.walletAddressService.getByIdWIthUserDetails( + receiver.walletAddressId + ) + + if (receiverWA?.account?.user) { + return `${receiverWA.account.user.firstName} ${receiverWA.account.user.lastName}` + } + } catch (e) { + this.logger.warn('Error on getting receiver wallet address', e) + } + } } diff --git a/packages/wallet/backend/src/walletAddress/service.ts b/packages/wallet/backend/src/walletAddress/service.ts index 502819328..8c122c5de 100644 --- a/packages/wallet/backend/src/walletAddress/service.ts +++ b/packages/wallet/backend/src/walletAddress/service.ts @@ -303,4 +303,12 @@ export class WalletAddressService implements IWalletAddressService { return walletAddress } + + async getByIdWIthUserDetails(id: string): Promise { + const walletAddress = await WalletAddress.query() + .findById(id) + .withGraphFetched('account.[user]') + + return walletAddress + } } diff --git a/packages/wallet/frontend/next.config.js b/packages/wallet/frontend/next.config.js index 9e98bb8b9..5e5f45006 100644 --- a/packages/wallet/frontend/next.config.js +++ b/packages/wallet/frontend/next.config.js @@ -2,7 +2,7 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true' }) -let NEXT_PUBLIC_FEATURES_ENABLED = 'true' +let NEXT_PUBLIC_FEATURES_ENABLED = 'false' if ( process.env.NODE_ENV === 'production' &&