From 755e549965b3bec83f3a5d8e705c4c8fb607500a Mon Sep 17 00:00:00 2001 From: dragosp1011 Date: Fri, 25 Oct 2024 02:34:23 +0200 Subject: [PATCH 1/8] feat: fund card wallet --- packages/wallet/backend/src/gatehub/service.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/wallet/backend/src/gatehub/service.ts b/packages/wallet/backend/src/gatehub/service.ts index 093dc3f56..d8c75acf8 100644 --- a/packages/wallet/backend/src/gatehub/service.ts +++ b/packages/wallet/backend/src/gatehub/service.ts @@ -14,6 +14,7 @@ import { getRandomValues } from 'crypto' import { EmailService } from '@/email/service' import { Transaction } from '@/transaction/model' import { transformBalance } from '@/utils/helpers' +import { TransactionTypeEnum } from '@/gatehub/consts' export class GateHubService { constructor( @@ -249,6 +250,15 @@ export class GateHubService { isCard: !!walletAddressName }) + await this.gateHubClient.createTransaction({ + amount: 10, + vault_uuid: this.gateHubClient.getVaultUuid(account.assetCode), + receiving_address: account.gateHubWalletId, + sending_address: this.env.GATEHUB_SETTLEMENT_WALLET_ADDRESS, + type: TransactionTypeEnum.HOSTED, + message: 'Transfer' + }) + return { account, walletAddress } } From a71d84445affcf21f0a4ad4a742c9d3a69cbcf5c Mon Sep 17 00:00:00 2001 From: dragosp1011 Date: Fri, 25 Oct 2024 03:01:03 +0200 Subject: [PATCH 2/8] feat: fund card account after is verified --- ...0241025020455_add_is_funded_to_accounts.js | 19 +++++++++ packages/wallet/backend/src/account/model.ts | 1 + .../wallet/backend/src/gatehub/service.ts | 41 +++++++++++++++---- 3 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 packages/wallet/backend/migrations/20241025020455_add_is_funded_to_accounts.js diff --git a/packages/wallet/backend/migrations/20241025020455_add_is_funded_to_accounts.js b/packages/wallet/backend/migrations/20241025020455_add_is_funded_to_accounts.js new file mode 100644 index 000000000..8843b7363 --- /dev/null +++ b/packages/wallet/backend/migrations/20241025020455_add_is_funded_to_accounts.js @@ -0,0 +1,19 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return knex.schema.table('accounts', function (table) { + table.boolean('isFunded').defaultTo(false) + }) +} + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return knex.schema.table('accounts', function (table) { + table.dropColumn('isFunded') + }) +} diff --git a/packages/wallet/backend/src/account/model.ts b/packages/wallet/backend/src/account/model.ts index 28bb12a4a..1177a3f1c 100644 --- a/packages/wallet/backend/src/account/model.ts +++ b/packages/wallet/backend/src/account/model.ts @@ -16,6 +16,7 @@ export class Account extends BaseModel { public user!: User public walletAddresses!: Array public cardId?: string + public isFunded!: boolean static relationMappings = () => ({ user: { diff --git a/packages/wallet/backend/src/gatehub/service.ts b/packages/wallet/backend/src/gatehub/service.ts index d8c75acf8..c0ac3d255 100644 --- a/packages/wallet/backend/src/gatehub/service.ts +++ b/packages/wallet/backend/src/gatehub/service.ts @@ -70,6 +70,7 @@ export class GateHubService { }) if (this.gateHubClient.isProduction) { await this.emailService.sendKYCVerifiedEmail(user.email) + await this.fundAccount(user.id) } break } @@ -250,15 +251,6 @@ export class GateHubService { isCard: !!walletAddressName }) - await this.gateHubClient.createTransaction({ - amount: 10, - vault_uuid: this.gateHubClient.getVaultUuid(account.assetCode), - receiving_address: account.gateHubWalletId, - sending_address: this.env.GATEHUB_SETTLEMENT_WALLET_ADDRESS, - type: TransactionTypeEnum.HOSTED, - message: 'Transfer' - }) - return { account, walletAddress } } @@ -361,4 +353,35 @@ export class GateHubService { private async updateUserFlag(userId: string, changes: Partial) { await User.query().findById(userId).patch(changes) } + + private async fundAccount(userId: string) { + if ( + this.env.NODE_ENV !== 'production' || + this.env.GATEHUB_ENV !== 'production' + ) { + return + } + + const account = await Account.query().findOne({ userId, assetCode: 'EUR' }) + if (!account) { + this.logger.error(`No account found for user ${userId} on fund account`) + return + } + + if (account.isFunded) { + this.logger.warn(`Account was already funded ${account.id}`) + return + } + + await Account.query().findById(account.id).patch({ isFunded: true }) + + await this.gateHubClient.createTransaction({ + amount: 10, + vault_uuid: this.gateHubClient.getVaultUuid(account.assetCode), + receiving_address: account.gateHubWalletId, + sending_address: this.env.GATEHUB_SETTLEMENT_WALLET_ADDRESS, + type: TransactionTypeEnum.HOSTED, + message: 'Transfer' + }) + } } From 12eb50f131e08597e01a6d8e526b9bb15b2656b0 Mon Sep 17 00:00:00 2001 From: dragosp1011 Date: Fri, 25 Oct 2024 03:06:11 +0200 Subject: [PATCH 3/8] fix: check for card id --- packages/wallet/backend/src/gatehub/service.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/wallet/backend/src/gatehub/service.ts b/packages/wallet/backend/src/gatehub/service.ts index c0ac3d255..090beee0b 100644 --- a/packages/wallet/backend/src/gatehub/service.ts +++ b/packages/wallet/backend/src/gatehub/service.ts @@ -363,8 +363,10 @@ export class GateHubService { } const account = await Account.query().findOne({ userId, assetCode: 'EUR' }) - if (!account) { - this.logger.error(`No account found for user ${userId} on fund account`) + if (!account?.cardId) { + this.logger.error( + `No account with card found for user ${userId} on kyc verified processing` + ) return } From 4f7bac68dd64f79fd779d13f730f72d2221bd941 Mon Sep 17 00:00:00 2001 From: dragosp1011 Date: Fri, 25 Oct 2024 13:43:24 +0200 Subject: [PATCH 4/8] feat: move fund account on set pin --- packages/wallet/backend/src/card/service.ts | 32 ++++++++++++-- .../wallet/backend/src/gatehub/service.ts | 42 ++----------------- 2 files changed, 32 insertions(+), 42 deletions(-) diff --git a/packages/wallet/backend/src/card/service.ts b/packages/wallet/backend/src/card/service.ts index 88fee812e..a059866f7 100644 --- a/packages/wallet/backend/src/card/service.ts +++ b/packages/wallet/backend/src/card/service.ts @@ -16,13 +16,17 @@ import { ICardResponse } from '@wallet/shared' import { UserService } from '@/user/service' import { Logger } from 'winston' import { User } from '@/user/model' +import { Account } from '@/account/model' +import { TransactionTypeEnum } from '@/gatehub/consts' +import { Env } from '@/config/env' export class CardService { constructor( private gateHubClient: GateHubClient, private accountService: AccountService, private userService: UserService, - private logger: Logger + private logger: Logger, + private env: Env ) {} async getCardsByCustomer( @@ -131,11 +135,13 @@ export class CardService { token: string, cypher: string ): Promise { - await this.ensureAccountExists(userId, cardId) + const cardAccount = await this.ensureAccountExists(userId, cardId) await this.gateHubClient.changePin(token, cypher) await User.query().findById(userId).patch({ isPinSet: true }) + + await this.fundAccount(cardAccount) } async lock( @@ -183,11 +189,13 @@ export class CardService { private async ensureAccountExists( userId: string, cardId: string - ): Promise { + ): Promise { const account = await this.accountService.getAccountByCardId(userId, cardId) if (!account) { throw new NotFound('Card not found or not associated with the user.') } + + return account } private async ensureGatehubUserUuid( @@ -207,4 +215,22 @@ export class CardService { return { user, gateHubUserId: user.gateHubUserId } } + + private async fundAccount(cardAccount: Account) { + if (cardAccount.isFunded) { + this.logger.warn(`Account was already funded ${cardAccount.id}`) + return + } + + await Account.query().findById(cardAccount.id).patch({ isFunded: true }) + + await this.gateHubClient.createTransaction({ + amount: 20, + vault_uuid: this.gateHubClient.getVaultUuid(cardAccount.assetCode), + receiving_address: cardAccount.gateHubWalletId, + sending_address: this.env.GATEHUB_SETTLEMENT_WALLET_ADDRESS, + type: TransactionTypeEnum.HOSTED, + message: 'Transfer' + }) + } } diff --git a/packages/wallet/backend/src/gatehub/service.ts b/packages/wallet/backend/src/gatehub/service.ts index 090beee0b..1b88441ca 100644 --- a/packages/wallet/backend/src/gatehub/service.ts +++ b/packages/wallet/backend/src/gatehub/service.ts @@ -14,7 +14,6 @@ import { getRandomValues } from 'crypto' import { EmailService } from '@/email/service' import { Transaction } from '@/transaction/model' import { transformBalance } from '@/utils/helpers' -import { TransactionTypeEnum } from '@/gatehub/consts' export class GateHubService { constructor( @@ -68,10 +67,6 @@ export class GateHubService { id: user.id, gateHubUserId: user.gateHubUserId }) - if (this.gateHubClient.isProduction) { - await this.emailService.sendKYCVerifiedEmail(user.email) - await this.fundAccount(user.id) - } break } case 'id.verification.action_required': @@ -321,12 +316,14 @@ export class GateHubService { gateHubUser.id ) + const activeCard = cards.find((card) => card.status !== 'SoftDelete') + await this.createDefaultAccountAndWAForManagedUser( userId, true, walletAddressName, walletAddressPublicName, - cards[0].id + activeCard?.id ) await User.query().findById(userId).patch({ @@ -353,37 +350,4 @@ export class GateHubService { private async updateUserFlag(userId: string, changes: Partial) { await User.query().findById(userId).patch(changes) } - - private async fundAccount(userId: string) { - if ( - this.env.NODE_ENV !== 'production' || - this.env.GATEHUB_ENV !== 'production' - ) { - return - } - - const account = await Account.query().findOne({ userId, assetCode: 'EUR' }) - if (!account?.cardId) { - this.logger.error( - `No account with card found for user ${userId} on kyc verified processing` - ) - return - } - - if (account.isFunded) { - this.logger.warn(`Account was already funded ${account.id}`) - return - } - - await Account.query().findById(account.id).patch({ isFunded: true }) - - await this.gateHubClient.createTransaction({ - amount: 10, - vault_uuid: this.gateHubClient.getVaultUuid(account.assetCode), - receiving_address: account.gateHubWalletId, - sending_address: this.env.GATEHUB_SETTLEMENT_WALLET_ADDRESS, - type: TransactionTypeEnum.HOSTED, - message: 'Transfer' - }) - } } From ebf7ca8577fe5626f5a9860ec1aa50586edc411a Mon Sep 17 00:00:00 2001 From: dragosp1011 Date: Fri, 25 Oct 2024 13:46:03 +0200 Subject: [PATCH 5/8] fix: send kyc email --- packages/wallet/backend/src/gatehub/service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/wallet/backend/src/gatehub/service.ts b/packages/wallet/backend/src/gatehub/service.ts index 1b88441ca..bf8c6b297 100644 --- a/packages/wallet/backend/src/gatehub/service.ts +++ b/packages/wallet/backend/src/gatehub/service.ts @@ -67,6 +67,10 @@ export class GateHubService { id: user.id, gateHubUserId: user.gateHubUserId }) + + if (this.gateHubClient.isProduction) { + await this.emailService.sendKYCVerifiedEmail(user.email) + } break } case 'id.verification.action_required': From c79060478921853b4f503421e08fe909238f2bda Mon Sep 17 00:00:00 2001 From: dragosp1011 Date: Fri, 25 Oct 2024 15:02:53 +0200 Subject: [PATCH 6/8] fix: tests --- .../backend/tests/cards/controller.test.ts | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/wallet/backend/tests/cards/controller.test.ts b/packages/wallet/backend/tests/cards/controller.test.ts index 57db2a703..787dc87cb 100644 --- a/packages/wallet/backend/tests/cards/controller.test.ts +++ b/packages/wallet/backend/tests/cards/controller.test.ts @@ -164,8 +164,10 @@ describe('CardController', () => { it('should get card details successfully', async () => { const next = jest.fn() + const password = 'some password' req.query = { publicKeyBase64: 'test-public-key' } req.params = { cardId: 'test-card-id' } + req.body = { password } const mockedCardDetails: ICardDetailsResponse = { cipher: 'encrypted-card-data' @@ -175,10 +177,14 @@ describe('CardController', () => { await cardController.getCardDetails(req, res, next) - expect(mockCardService.getCardDetails).toHaveBeenCalledWith(userId, { - cardId: 'test-card-id', - publicKey: 'test-public-key' - }) + expect(mockCardService.getCardDetails).toHaveBeenCalledWith( + userId, + password, + { + cardId: 'test-card-id', + publicKey: 'test-public-key' + } + ) expect(res.statusCode).toBe(200) expect(res._getJSONData()).toEqual({ success: true, @@ -517,7 +523,11 @@ describe('CardController', () => { it('should get pin successfully', async () => { const next = jest.fn() + const password = 'some password' req.query = { publicKeyBase64: 'test-public-key' } + req.body = { + password + } const mockedCardDetails: ICardDetailsResponse = { cipher: 'encrypted-card-pin' @@ -527,7 +537,7 @@ describe('CardController', () => { await cardController.getPin(req, res, next) - expect(mockCardService.getPin).toHaveBeenCalledWith(userId, { + expect(mockCardService.getPin).toHaveBeenCalledWith(userId, password, { cardId: 'test-card-id', publicKey: 'test-public-key' }) From 04db23704c35141d9fa41b8d9263e4b65c088ebf Mon Sep 17 00:00:00 2001 From: dragosp1011 Date: Fri, 25 Oct 2024 18:22:56 +0200 Subject: [PATCH 7/8] fix: search active card --- packages/wallet/backend/src/card/service.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/wallet/backend/src/card/service.ts b/packages/wallet/backend/src/card/service.ts index a059866f7..0e82b4856 100644 --- a/packages/wallet/backend/src/card/service.ts +++ b/packages/wallet/backend/src/card/service.ts @@ -44,12 +44,14 @@ export class CardService { gateHubUserId ) - Object.assign(cards[0], { + const activeCard = cards.find((card) => card.status !== 'SoftDelete') + + Object.assign(activeCard, { isPinSet: user.isPinSet, walletAddress: user.cardWalletAddress }) - return cards + return [activeCard] } async getCardDetails( From 2c790e7f6e2d082c762b6fc07090ddf3d58b4042 Mon Sep 17 00:00:00 2001 From: dragosp1011 Date: Fri, 25 Oct 2024 18:25:27 +0200 Subject: [PATCH 8/8] fix: select first --- packages/wallet/backend/src/card/service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/wallet/backend/src/card/service.ts b/packages/wallet/backend/src/card/service.ts index 0e82b4856..dece516d6 100644 --- a/packages/wallet/backend/src/card/service.ts +++ b/packages/wallet/backend/src/card/service.ts @@ -44,7 +44,8 @@ export class CardService { gateHubUserId ) - const activeCard = cards.find((card) => card.status !== 'SoftDelete') + const activeCard = + cards.find((card) => card.status !== 'SoftDelete') || cards[0] Object.assign(activeCard, { isPinSet: user.isPinSet,