Skip to content

Commit

Permalink
Address comments
Browse files Browse the repository at this point in the history
  • Loading branch information
sanducb committed Sep 26, 2024
1 parent c8c0868 commit 18bb708
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 78 deletions.
6 changes: 5 additions & 1 deletion packages/wallet/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,11 @@ export class App {
)

// Cards
router.get('/:customerId/cards', isAuth, cardController.getCardsByCustomer)
router.get(
'/customers/:customerId/cards',
isAuth,
cardController.getCardsByCustomer
)
router.get('/cards/:cardId/details', isAuth, cardController.getCardDetails)

// Return an error for invalid routes
Expand Down
20 changes: 5 additions & 15 deletions packages/wallet/backend/src/card/controller.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { Request, Response, NextFunction } from 'express'
import { Controller, NotFound } from '@shared/backend'
import { Controller } from '@shared/backend'
import { CardService } from '@/card/service'
import { toSuccessResponse } from '@shared/backend'
import {
ICardDetailsRequest,
ICardDetailsResponse,
ICardResponse
} from './types'
import { WalletAddressService } from '@/walletAddress/service'
import { validate } from '@/shared/validate'
import { getCardsByCustomerSchema, getCardDetailsSchema } from './validation'

Expand All @@ -17,10 +16,7 @@ export interface ICardController {
}

export class CardController implements ICardController {
constructor(
private cardService: CardService,
private walletAddressService: WalletAddressService
) {}
constructor(private cardService: CardService) {}

public getCardsByCustomer = async (
req: Request,
Expand Down Expand Up @@ -49,17 +45,11 @@ export class CardController implements ICardController {
const { cardId } = params
const { publicKeyBase64 } = body

const walletAddress = await this.walletAddressService.getByCardId(
const requestBody: ICardDetailsRequest = { cardId, publicKeyBase64 }
const cardDetails = await this.cardService.getCardDetails(
userId,
cardId
requestBody
)

if (!walletAddress) {
throw new NotFound('Card not found or not associated with the user.')
}

const requestBody: ICardDetailsRequest = { cardId, publicKeyBase64 }
const cardDetails = await this.cardService.getCardDetails(requestBody)
res.status(200).json(toSuccessResponse(cardDetails))
} catch (error) {
next(error)
Expand Down
18 changes: 17 additions & 1 deletion packages/wallet/backend/src/card/service.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
import { WalletAddressService } from '@/walletAddress/service'
import { GateHubClient } from '../gatehub/client'
import {
ICardDetailsRequest,
ICardDetailsResponse,
ICardResponse
} from './types'
import { NotFound } from '@shared/backend'

export class CardService {
constructor(private gateHubClient: GateHubClient) {}
constructor(
private gateHubClient: GateHubClient,
private walletAddressService: WalletAddressService
) {}

async getCardsByCustomer(customerId: string): Promise<ICardResponse[]> {
return this.gateHubClient.getCardsByCustomer(customerId)
}

async getCardDetails(
userId: string,
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.')
}

return this.gateHubClient.getCardDetails(requestBody)
}
}
102 changes: 60 additions & 42 deletions packages/wallet/backend/src/gatehub/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class GateHubClient {

private iframeMappings: Record<
IFRAME_TYPE,
(managedUserId: string) => Promise<string>
(managedUserUuid: string) => Promise<string>
> = {
deposit: this.getDepositUrl.bind(this),
withdrawal: this.getWithdrawalUrl.bind(this),
Expand Down Expand Up @@ -85,61 +85,61 @@ export class GateHubClient {
return `https://onboarding.${this.mainUrl}`
}

async getWithdrawalUrl(managedUserId: string): Promise<string> {
async getWithdrawalUrl(managedUserUuid: string): Promise<string> {
const token = await this.getIframeAuthorizationToken(
this.clientIds.onOffRamp,
DEFAULT_APP_SCOPE,
managedUserId
managedUserUuid
)

return `${this.rampUrl}/?paymentType=${PAYMENT_TYPE.withdrawal}&bearer=${token}`
}

async getDepositUrl(managedUserId: string): Promise<string> {
async getDepositUrl(managedUserUuid: string): Promise<string> {
const token = await this.getIframeAuthorizationToken(
this.clientIds.onOffRamp,
DEFAULT_APP_SCOPE,
managedUserId
managedUserUuid
)

return `${this.rampUrl}/?paymentType=${PAYMENT_TYPE.deposit}&bearer=${token}`
}

async getOnboardingUrl(managedUserId: string): Promise<string> {
async getOnboardingUrl(managedUserUuid: string): Promise<string> {
const token = await this.getIframeAuthorizationToken(
this.clientIds.onboarding,
ONBOARDING_APP_SCOPE,
managedUserId
managedUserUuid
)

return `${this.onboardingUrl}/?bearer=${token}`
}

async getExchangeUrl(managedUserId: string): Promise<string> {
async getExchangeUrl(managedUserUuid: string): Promise<string> {
const token = await this.getIframeAuthorizationToken(
this.clientIds.exchange,
DEFAULT_APP_SCOPE,
managedUserId
managedUserUuid
)

return `${this.exchangeUrl}/?bearer=${token}`
}

async getIframeUrl(
type: IFRAME_TYPE,
managedUserId: string
managedUserUuid: string
): Promise<string> {
if (!this.iframeMappings[type]) {
throw new BadRequest('Invalid iframe type')
}

return await this.iframeMappings[type](managedUserId)
return await this.iframeMappings[type](managedUserUuid)
}

async getIframeAuthorizationToken(
clientId: string,
scope: string[],
managedUserId: string
managedUserUuid: string
): Promise<string> {
const url = `${this.apiUrl}/auth/v1/tokens?clientId=${clientId}`
const body: ITokenRequest = { scope }
Expand All @@ -148,7 +148,9 @@ export class GateHubClient {
'POST',
url,
JSON.stringify(body),
managedUserId
{
managedUserUuid
}
)

return response.token
Expand Down Expand Up @@ -191,21 +193,18 @@ export class GateHubClient {
}

async connectUserToGateway(
userUuid: string,
managedUserUuid: string,
gatewayUuid: string
): Promise<boolean> {
const url = `${this.apiUrl}/id/v1/users/${userUuid}/hubs/${gatewayUuid}`
const url = `${this.apiUrl}/id/v1/users/${managedUserUuid}/hubs/${gatewayUuid}`

await this.request<IConnectUserToGatewayResponse>(
'POST',
url,
undefined,
userUuid
)
await this.request<IConnectUserToGatewayResponse>('POST', url, undefined, {
managedUserUuid
})

if (!this.isProduction) {
// Auto approve user to gateway in sandbox environment
await this.approveUserToGateway(userUuid, gatewayUuid)
await this.approveUserToGateway(managedUserUuid, gatewayUuid)

return true
}
Expand Down Expand Up @@ -234,10 +233,10 @@ export class GateHubClient {
}

async createWallet(
userUuid: string,
managedUserUuid: string,
name: string
): Promise<ICreateWalletResponse> {
const url = `${this.apiUrl}/core/v1/users/${userUuid}/wallets`
const url = `${this.apiUrl}/core/v1/users/${managedUserUuid}/wallets`
const body: ICreateWalletRequest = {
name,
type: HOSTED_WALLET_TYPE
Expand All @@ -247,7 +246,9 @@ export class GateHubClient {
'POST',
url,
JSON.stringify(body),
userUuid
{
managedUserUuid
}
)

return response
Expand Down Expand Up @@ -312,24 +313,27 @@ export class GateHubClient {

// This should be called before creating customers to get the product codes for the card and account
async fetchCardApplicationProducts(): Promise<ICardProductResponse[]> {
const path = `/v1/card-applications/${this.env.GATEHUB_CARD_APP_ID}/card-products`
const response = await this.request<ICardProductResponse[]>('GET', path)
const url = `${this.apiUrl}/v1/card-applications/${this.env.GATEHUB_CARD_APP_ID}/card-products`
const response = await this.request<ICardProductResponse[]>('GET', url)
return response
}

async createCustomer(
request: ICreateCustomerRequest
requestBody: ICreateCustomerRequest
): Promise<ICreateCustomerResponse> {
const url = `${this.apiUrl}/v1/customers`
return this.request<ICreateCustomerResponse>(
'POST',
url,
JSON.stringify(request)
JSON.stringify(requestBody),
{
cardAppId: this.env.GATEHUB_CARD_APP_ID
}
)
}

async getCardsByCustomer(customerId: string): Promise<ICardResponse[]> {
const url = `/v1/customers/${customerId}/cards`
const url = `${this.apiUrl}/v1/customers/${customerId}/cards`
return this.request<ICardResponse[]>('GET', url)
}

Expand All @@ -341,7 +345,10 @@ export class GateHubClient {
const response = await this.request<ILinksResponse>(
'POST',
url,
JSON.stringify(requestBody)
JSON.stringify(requestBody),
{
cardAppId: this.env.GATEHUB_CARD_APP_ID
}
)

const token = response.token
Expand All @@ -356,8 +363,9 @@ export class GateHubClient {
'GET',
cardDetailsUrl,
undefined,
undefined,
token
{
token
}
)

return cardDetailsResponse
Expand All @@ -367,17 +375,19 @@ export class GateHubClient {
method: HTTP_METHODS,
url: string,
body?: string,
managedUserUuid?: string,
token?: string
headersOptions?: {
managedUserUuid?: string
token?: string
cardAppId?: string
}
): Promise<T> {
const timestamp = Date.now().toString()
const headers = this.getRequestHeaders(
timestamp,
method,
url,
body ?? '',
managedUserUuid,
token
headersOptions
)

try {
Expand Down Expand Up @@ -412,19 +422,27 @@ export class GateHubClient {
method: HTTP_METHODS,
url: string,
body?: string,
managedUserUuid?: string,
token?: string
headersOptions?: {
managedUserUuid?: string
token?: string
cardAppId?: string
}
) {
return {
'Content-Type': 'application/json',
'x-gatehub-app-id': this.env.GATEHUB_ACCESS_KEY,
'x-gatehub-timestamp': timestamp,
'x-gatehub-signature': this.getSignature(timestamp, method, url, body),
'x-gatehub-card-app-id': this.env.GATEHUB_CARD_APP_ID,
...(managedUserUuid && {
'x-gatehub-managed-user-uuid': managedUserUuid
...(headersOptions?.managedUserUuid && {
'x-gatehub-managed-user-uuid': headersOptions.managedUserUuid
}),
...(token && { Authorization: token })
...(headersOptions?.cardAppId && {
'x-gatehub-card-app-id': headersOptions.cardAppId
}),
...(headersOptions?.token && {
Authorization: `Bearer ${headersOptions.token}`
})
}
}

Expand Down
Loading

0 comments on commit 18bb708

Please sign in to comment.