Skip to content

Commit

Permalink
Update endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
sanducb committed Sep 25, 2024
1 parent a81ea9f commit 1086ed4
Show file tree
Hide file tree
Showing 12 changed files with 250 additions and 131 deletions.
1 change: 1 addition & 0 deletions docker/dev/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ GATEHUB_GATEWAY_UUID=
GATEHUB_VAULT_UUID_EUR=
GATEHUB_VAULT_UUID_USD=
GATEHUB_SETTLEMENT_WALLET_ADDRESS=
GATEHUB_CARD_APP_ID=

# commerce env variables
# encoded base 64 private key
Expand Down
1 change: 1 addition & 0 deletions docker/dev/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ services:
GATEHUB_VAULT_UUID_EUR: ${GATEHUB_VAULT_UUID_EUR}
GATEHUB_VAULT_UUID_USD: ${GATEHUB_VAULT_UUID_USD}
GATEHUB_SETTLEMENT_WALLET_ADDRESS: ${GATEHUB_SETTLEMENT_WALLET_ADDRESS}
GATEHUB_CARD_APP_ID: ${GATEHUB_CARD_APP_ID}
restart: always
networks:
- testnet
Expand Down
1 change: 1 addition & 0 deletions docker/prod/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ WALLET_BACKEND_GATEHUB_GATEWAY_UUID=
WALLET_BACKEND_GATEHUB_VAULT_UUID_EUR=
WALLET_BACKEND_GATEHUB_VAULT_UUID_USD=
WALLET_BACKEND_GATEHUB_SETTLEMENT_WALLET_ADDRESS=
WALLET_BACKEND_GATEHUB_CARD_APP_ID=

# BOUTIQUE
BOUTIQUE_BACKEND_PORT=
Expand Down
1 change: 1 addition & 0 deletions docker/prod/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ services:
GATEHUB_VAULT_UUID_EUR: ${WALLET_BACKEND_GATEHUB_VAULT_UUID_EUR}
GATEHUB_VAULT_UUID_USD: ${WALLET_BACKEND_GATEHUB_VAULT_UUID_USD}
GATEHUB_SETTLEMENT_WALLET_ADDRESS: ${WALLET_BACKEND_GATEHUB_SETTLEMENT_WALLET_ADDRESS}
GATEHUB_CARD_APP_ID: ${WALLET_BACKEND_GATEHUB_CARD_APP_ID}
networks:
- testnet
ports:
Expand Down
6 changes: 1 addition & 5 deletions packages/wallet/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,11 +307,7 @@ export class App {
)

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

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

export interface ICardController {
getMaskedCardDetails: Controller<IMaskedCardDetailsResponse>
getCardDetails: Controller<IMaskedCardDetailsResponse>
getCardsByCustomer: Controller<ICardDetailsResponse[]>
getCardDetails: Controller<ICardResponse>
}

export class CardController implements ICardController {
Expand All @@ -16,27 +22,17 @@ export class CardController implements ICardController {
private walletAddressService: WalletAddressService
) {}

public getMaskedCardDetails = async (
public getCardsByCustomer = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const userId = req.session.user.id
const { cardId } = req.params

const walletAddress = await this.walletAddressService.getByCardId(
userId,
cardId
)
const { params } = await validate(getCardsByCustomerSchema, req)
const { customerId } = params

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

const maskedCardDetails =
await this.cardService.getMaskedCardDetails(cardId)
res.status(200).json(toSuccessResponse(maskedCardDetails))
const cards = await this.cardService.getCardsByCustomer(customerId)
res.status(200).json(toSuccessResponse(cards))
} catch (error) {
next(error)
}
Expand All @@ -49,7 +45,9 @@ export class CardController implements ICardController {
) => {
try {
const userId = req.session.user.id
const { cardId, publicKeyBase64 } = req.params
const { params, body } = await validate(getCardDetailsSchema, req)
const { cardId } = params
const { publicKeyBase64 } = body

const walletAddress = await this.walletAddressService.getByCardId(
userId,
Expand All @@ -60,10 +58,8 @@ export class CardController implements ICardController {
throw new NotFound('Card not found or not associated with the user.')
}

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

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

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

async getCardDetails(
cardId: string,
publicKeyBase64: string
requestBody: ICardDetailsRequest
): Promise<ICardDetailsResponse> {
return this.gateHubClient.getCardDetails(cardId, publicKeyBase64)
return this.gateHubClient.getCardDetails(requestBody)
}
}
92 changes: 81 additions & 11 deletions packages/wallet/backend/src/card/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,60 @@
export interface IMaskedCardDetailsResponse {
sourceId: string | null
nameOnCard: string | null
export interface ICardDetailsRequest {
cardId: string
publicKeyBase64: string
}

export interface ICardDetailsResponse {
cipher: string | null
}

export interface ILinksResponse {
token: string | null
links: Array<{
href: string | null
rel: string | null
method: string | null
}> | null
}

export interface ICreateCustomerRequest {
emailAddress: string
account: {
productCode: string
}
card: {
productCode: string
}
user: {
firstName: string
lastName: string
mobileNumber?: string
nationalIdentifier?: string
}
identification: {
documents: Array<{
type: string
file: string // Base64-encoded file content
}>
}
address: {
addressLine1: string
addressLine2?: string
city: string
region?: string
postalCode: string
countryCode: string
}
}

export interface ICreateCustomerResponse {
customerId: string
accountId: string
cardId: string
}

export interface ICardResponse {
sourceId: string
nameOnCard: string
productCode: string
id: string
accountId: string
Expand All @@ -14,13 +68,29 @@ export interface IMaskedCardDetailsResponse {
customerSourceId: string
}

export interface ICardDetailsResponse {}
export type CardLimitType =
| 'perTransaction'
| 'dailyOverall'
| 'weeklyOverall'
| 'monthlyOverall'
| 'dailyAtm'
| 'dailyEcomm'
| 'monthlyOpenScheme'
| 'nonEUPayments'

export interface ILinksResponse {
token: string | null
links: Array<{
href: string | null
rel: string | null
method: string | null
}> | null
export interface ICardProductLimit {
type: CardLimitType
currency: string
limit: string
isDisabled: boolean
}

export interface ICardProductResponse {
cardProductLimits: ICardProductLimit[]
deletedAt: string | null
uuid: string
accountProductCode: string
code: string
name: string
cost: string
}
16 changes: 16 additions & 0 deletions packages/wallet/backend/src/card/validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { z } from 'zod'

export const getCardsByCustomerSchema = z.object({
params: z.object({
customerId: z.string()
})
})

export const getCardDetailsSchema = z.object({
params: z.object({
cardId: z.string()
}),
body: z.object({
publicKeyBase64: z.string()
})
})
1 change: 1 addition & 0 deletions packages/wallet/backend/src/config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const envSchema = z.object({
GATEHUB_SETTLEMENT_WALLET_ADDRESS: z
.string()
.default('GATEHUB_SETTLEMENT_WALLET_ADDRESS'),
GATEHUB_CARD_APP_ID: z.string().default('GATEHUB_CARD_APP_ID'),
GRAPHQL_ENDPOINT: z.string().url().default('http://localhost:3011/graphql'),
AUTH_GRAPHQL_ENDPOINT: z
.string()
Expand Down
45 changes: 29 additions & 16 deletions packages/wallet/backend/src/gatehub/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ import { IFRAME_TYPE } from '@wallet/shared/src'
import { BadRequest } from '@shared/backend'
import {
ICardDetailsResponse,
IMaskedCardDetailsResponse,
ILinksResponse
ILinksResponse,
ICardResponse,
ICreateCustomerRequest,
ICreateCustomerResponse,
ICardProductResponse,
ICardDetailsRequest
} from '@/card/types'

export class GateHubClient {
Expand Down Expand Up @@ -306,26 +310,34 @@ export class GateHubClient {
return flatRates
}

async getMaskedCardDetails(
cardId: string
): Promise<IMaskedCardDetailsResponse> {
const url = `${this.apiUrl}/cards/${cardId}/card`

const response = await this.request<IMaskedCardDetailsResponse>('GET', url)
// 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)
return response
}

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

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

async getCardDetails(
cardId: string,
publicKeyBase64: string
requestBody: ICardDetailsRequest
): Promise<ICardDetailsResponse> {
const url = `${this.apiUrl}/token/card-data`

const requestBody = {
cardId,
publicKeyBase64
}

const response = await this.request<ILinksResponse>(
'POST',
url,
Expand All @@ -337,9 +349,9 @@ export class GateHubClient {
throw new Error('Failed to obtain token for card data retrieval')
}

// TODO
// Will get this from the response
const cardDetailsUrl = ''
// TODO
const cardDetailsResponse = await this.request<ICardDetailsResponse>(
'GET',
cardDetailsUrl
Expand Down Expand Up @@ -402,6 +414,7 @@ export class GateHubClient {
'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 })
}
}
Expand Down
Loading

0 comments on commit 1086ed4

Please sign in to comment.