Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin' into 1660-cards-wallet-backend-…
Browse files Browse the repository at this point in the history
…viewupdate-card-limits
  • Loading branch information
sanducb committed Oct 2, 2024
2 parents a35f3f7 + 314bc80 commit bef1cbf
Show file tree
Hide file tree
Showing 17 changed files with 992 additions and 305 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"eslint-plugin-react-hooks": "^4.6.2",
"only-allow": "^1.2.1",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.6",
"prettier-plugin-tailwindcss": "^0.6.8",
"typescript": "^5.6.2"
},
"engines": {
Expand Down
2 changes: 1 addition & 1 deletion packages/boutique/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"express": "^4.21.0",
"helmet": "^7.1.0",
"knex": "^3.1.0",
"objection": "^3.1.4",
"objection": "^3.1.5",
"pg": "^8.13.0",
"winston": "^3.14.2",
"zod": "^3.23.8"
Expand Down
4 changes: 2 additions & 2 deletions packages/boutique/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@
"devDependencies": {
"@tailwindcss/forms": "^0.5.9",
"@tailwindcss/typography": "^0.5.15",
"@types/react": "18.3.8",
"@types/react": "18.3.10",
"@types/react-dom": "18.3.0",
"@vitejs/plugin-react-swc": "^3.7.0",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.47",
"tailwindcss": "^3.4.13",
"typescript": "^5.6.2",
"vite": "^5.4.7"
"vite": "^5.4.8"
}
}
2 changes: 1 addition & 1 deletion packages/shared/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"@google-cloud/logging-winston": "^6.0.0",
"awilix": "^11.0.0",
"express": "^4.21.0",
"objection": "^3.1.4",
"objection": "^3.1.5",
"knex": "^3.1.0",
"winston": "^3.14.2",
"axios": "^1.7.7"
Expand Down
4 changes: 2 additions & 2 deletions packages/wallet/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"knex": "^3.1.0",
"moment": "^2.30.1",
"node-cache": "^5.1.2",
"objection": "^3.1.4",
"objection": "^3.1.5",
"pg": "^8.13.0",
"randexp": "^0.5.3",
"socket.io": "^4.8.0",
Expand All @@ -38,7 +38,7 @@
"zod": "^3.23.8"
},
"devDependencies": {
"@faker-js/faker": "^9.0.2",
"@faker-js/faker": "^9.0.3",
"@graphql-codegen/cli": "^5.0.2",
"@graphql-codegen/typescript": "^4.0.9",
"@graphql-codegen/typescript-operations": "^4.2.3",
Expand Down
12 changes: 7 additions & 5 deletions packages/wallet/backend/src/account/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,12 @@ export class AccountService implements IAccountService {
const accounts = await query

if (!includeWalletAddress) {
accounts.forEach(async (acc) => {
const balance = await this.getAccountBalance(acc)
acc.balance = transformBalance(balance, acc.assetScale)
})
await Promise.all(
accounts.map(async (acc) => {
const balance = await this.getAccountBalance(acc)
acc.balance = transformBalance(balance, acc.assetScale)
})
)
}

return accounts
Expand Down Expand Up @@ -164,7 +166,7 @@ export class AccountService implements IAccountService {
account.gateHubWalletId
)
return Number(
balances.find((balance) => balance.vault.assetCode === account.assetCode)
balances.find((balance) => balance.vault.asset_code === account.assetCode)
?.total ?? 0
)
}
Expand Down
12 changes: 12 additions & 0 deletions packages/wallet/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,19 @@ export class App {
'/cards/:cardId/limits',
isAuth,
cardController.createOrOverrideCardLimits
) router.get(
'/cards/:cardId/transactions',
isAuth,
cardController.getCardTransactions
)
router.get('/cards/:cardId/pin', isAuth, cardController.getPin)
router.post('/cards/:cardId/change-pin', isAuth, cardController.changePin)
router.put(
'/cards/:cardId/block',
isAuth,
cardController.permanentlyBlockCard
)

// Return an error for invalid routes
router.use('*', (req: Request, res: CustomResponse) => {
const e = Error(`Requested path ${req.path} was not found`)
Expand Down
98 changes: 94 additions & 4 deletions packages/wallet/backend/src/card/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,31 @@ import {
ICardResponse,
ICardUnlockRequest
} from './types'
import { IGetTransactionsResponse } from '@wallet/shared/src'
import { validate } from '@/shared/validate'
import {
getCardsByCustomerSchema,
getCardDetailsSchema,
lockCardSchema,
unlockCardSchema,
getCardLimitsSchema,
createOrOverrideCardLimitsSchema
createOrOverrideCardLimitsSchema,
getCardTransactionsSchema,
changePinSchema,
permanentlyBlockCardSchema
} from './validation'

export interface ICardController {
getCardsByCustomer: Controller<ICardDetailsResponse[]>
getCardDetails: Controller<ICardResponse>
getCardLimits: Controller<ICardLimitResponse[]>
createOrOverrideCardLimits: Controller<ICardLimitResponse[]>
getCardTransactions: Controller<IGetTransactionsResponse>
getPin: Controller<ICardResponse>
changePin: Controller<void>
lock: Controller<ICardResponse>
unlock: Controller<ICardResponse>
permanentlyBlockCard: Controller<ICardResponse>
}

export class CardController implements ICardController {
Expand Down Expand Up @@ -56,9 +64,9 @@ export class CardController implements ICardController {
) => {
try {
const userId = req.session.user.id
const { params, body } = await validate(getCardDetailsSchema, req)
const { params, query } = await validate(getCardDetailsSchema, req)
const { cardId } = params
const { publicKeyBase64 } = body
const { publicKeyBase64 } = query

const requestBody: ICardDetailsRequest = { cardId, publicKeyBase64 }
const cardDetails = await this.cardService.getCardDetails(
Expand All @@ -71,6 +79,63 @@ export class CardController implements ICardController {
}
}

public getCardTransactions = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const userId = req.session.user.id
const { params, query } = await validate(getCardTransactionsSchema, req)
const { cardId } = params
const { pageSize, pageNumber } = query

const transactions = await this.cardService.getCardTransactions(
userId,
cardId,
pageSize,
pageNumber
)

res.status(200).json(toSuccessResponse(transactions))
} catch (error) {
next(error)
}
}

public getPin = async (req: Request, res: Response, next: NextFunction) => {
try {
const userId = req.session.user.id
const { params, query } = await validate(getCardDetailsSchema, req)
const { cardId } = params
const { publicKeyBase64 } = query

const requestBody: ICardDetailsRequest = { cardId, publicKeyBase64 }
const cardPin = await this.cardService.getPin(userId, requestBody)
res.status(200).json(toSuccessResponse(cardPin))
} catch (error) {
next(error)
}
}

public changePin = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const userId = req.session.user.id
const { params, body } = await validate(changePinSchema, req)
const { cardId } = params
const { cypher } = body

const result = await this.cardService.changePin(userId, cardId, cypher)
res.status(201).json(toSuccessResponse(result))
} catch (error) {
next(error)
}
}

public getCardLimits = async (
req: Request,
res: Response,
Expand Down Expand Up @@ -116,12 +181,14 @@ export class CardController implements ICardController {

public lock = async (req: Request, res: Response, next: NextFunction) => {
try {
const userId = req.session.user.id
const { params, query, body } = await validate(lockCardSchema, req)
const { cardId } = params
const { reasonCode } = query
const requestBody: ICardLockRequest = body

const result = await this.cardService.lock(
userId,
cardId,
reasonCode,
requestBody
Expand All @@ -135,15 +202,38 @@ export class CardController implements ICardController {

public unlock = async (req: Request, res: Response, next: NextFunction) => {
try {
const userId = req.session.user.id
const { params, body } = await validate(unlockCardSchema, req)
const { cardId } = params
const requestBody: ICardUnlockRequest = body

const result = await this.cardService.unlock(cardId, requestBody)
const result = await this.cardService.unlock(userId, cardId, requestBody)

res.status(200).json(toSuccessResponse(result))
} catch (error) {
next(error)
}
}

public permanentlyBlockCard = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const userId = req.session.user.id
const { params, query } = await validate(permanentlyBlockCardSchema, req)
const { cardId } = params
const { reasonCode } = query

const result = await this.cardService.permanentlyBlockCard(
userId,
cardId,
reasonCode
)
res.status(200).json(toSuccessResponse(result))
} catch (error) {
next(error)
}
}
}
71 changes: 63 additions & 8 deletions packages/wallet/backend/src/card/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import {
ICardResponse,
ICardUnlockRequest
} from './types'
import { IGetTransactionsResponse } from '@wallet/shared/src'
import { LockReasonCode } from '@wallet/shared/src'
import { NotFound } from '@shared/backend'
import { BlockReasonCode } from '@wallet/shared/src'

export class CardService {
constructor(
Expand All @@ -27,14 +29,7 @@ export class CardService {
requestBody: ICardDetailsRequest
): Promise<ICardDetailsResponse> {
const { cardId } = requestBody

const walletAddress = await this.walletAddressService.getByCardId(
userId,
cardId
)
if (!walletAddress) {
throw new NotFound('Card not found or not associated with the user.')
}
await this.ensureWalletAddressExists(userId, cardId)

return this.gateHubClient.getCardDetails(requestBody)
}
Expand All @@ -58,21 +53,81 @@ export class CardService {
return this.gateHubClient.createOrOverrideCardLimits(cardId, requestBody)
}

async getCardTransactions(
userId: string,
cardId: string,
pageSize?: number,
pageNumber?: number
): Promise<IGetTransactionsResponse> {
await this.ensureWalletAddressExists(userId, cardId)

return this.gateHubClient.getCardTransactions(cardId, pageSize, pageNumber)
}

async getPin(
userId: string,
requestBody: ICardDetailsRequest
): Promise<ICardDetailsResponse> {
const { cardId } = requestBody
await this.ensureWalletAddressExists(userId, cardId)

return this.gateHubClient.getPin(requestBody)
}

async changePin(
userId: string,
cardId: string,
cypher: string
): Promise<void> {
await this.ensureWalletAddressExists(userId, cardId)

await this.gateHubClient.changePin(cardId, cypher)
}

async lock(
userId: string,
cardId: string,
reasonCode: LockReasonCode,
requestBody: ICardLockRequest
): Promise<ICardResponse> {
await this.ensureWalletAddressExists(userId, cardId)

return this.gateHubClient.lockCard(cardId, reasonCode, requestBody)
}

async unlock(
userId: string,
cardId: string,
requestBody: ICardUnlockRequest
): Promise<ICardResponse> {
await this.ensureWalletAddressExists(userId, cardId)

return this.gateHubClient.unlockCard(cardId, requestBody)
}

async permanentlyBlockCard(
userId: string,
cardId: string,
reasonCode: BlockReasonCode
): Promise<ICardResponse> {
await this.ensureWalletAddressExists(userId, cardId)

return this.gateHubClient.permanentlyBlockCard(cardId, reasonCode)
}

private async ensureWalletAddressExists(
userId: string,
cardId: string
): Promise<void> {
const walletAddress = await this.walletAddressService.getByCardId(
userId,
cardId
)
if (!walletAddress) {
throw new NotFound('Card not found or not associated with the user.')
}
}

private async ensureWalletAddressExists(
userId: string,
cardId: string
Expand Down
Loading

0 comments on commit bef1cbf

Please sign in to comment.