mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2025-08-25 05:42:27 +00:00
feat: add vpan to card entity
This commit is contained in:
@ -23,6 +23,10 @@ export class Card {
|
|||||||
@Column({ name: 'card_reference', nullable: false, type: 'varchar' })
|
@Column({ name: 'card_reference', nullable: false, type: 'varchar' })
|
||||||
cardReference!: string;
|
cardReference!: string;
|
||||||
|
|
||||||
|
@Index({ unique: true })
|
||||||
|
@Column({ name: 'vpan', nullable: false, type: 'varchar' })
|
||||||
|
vpan!: string;
|
||||||
|
|
||||||
@Column({ length: 6, name: 'first_six_digits', nullable: false, type: 'varchar' })
|
@Column({ length: 6, name: 'first_six_digits', nullable: false, type: 'varchar' })
|
||||||
firstSixDigits!: string;
|
firstSixDigits!: string;
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ export class CardRepository {
|
|||||||
scheme: CardScheme.VISA,
|
scheme: CardScheme.VISA,
|
||||||
issuer: CardIssuers.NEOLEAP,
|
issuer: CardIssuers.NEOLEAP,
|
||||||
accountId: accountId,
|
accountId: accountId,
|
||||||
|
vpan: card.vpan,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -34,9 +35,9 @@ export class CardRepository {
|
|||||||
return this.cardRepository.findOne({ where: { cardReference: referenceNumber }, relations: ['account'] });
|
return this.cardRepository.findOne({ where: { cardReference: referenceNumber }, relations: ['account'] });
|
||||||
}
|
}
|
||||||
|
|
||||||
getCardByAccountNumber(accountNumber: string): Promise<Card | null> {
|
getCardByVpan(vpan: string): Promise<Card | null> {
|
||||||
return this.cardRepository.findOne({
|
return this.cardRepository.findOne({
|
||||||
where: { account: { accountNumber } },
|
where: { vpan },
|
||||||
relations: ['account'],
|
relations: ['account'],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,8 @@ export class CardService {
|
|||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCardByAccountNumber(accountNumber: string): Promise<Card> {
|
async getCardByVpan(vpan: string): Promise<Card> {
|
||||||
const card = await this.cardRepository.getCardByAccountNumber(accountNumber);
|
const card = await this.cardRepository.getCardByVpan(vpan);
|
||||||
|
|
||||||
if (!card) {
|
if (!card) {
|
||||||
throw new BadRequestException('CARD.NOT_FOUND');
|
throw new BadRequestException('CARD.NOT_FOUND');
|
||||||
@ -55,7 +55,7 @@ export class CardService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updateCardStatus(body: AccountCardStatusChangedWebhookRequest) {
|
async updateCardStatus(body: AccountCardStatusChangedWebhookRequest) {
|
||||||
const card = await this.getCardByAccountNumber(body.cardId);
|
const card = await this.getCardByVpan(body.cardId);
|
||||||
const { description, status } = CardStatusMapper[body.newStatus] || CardStatusMapper['99'];
|
const { description, status } = CardStatusMapper[body.newStatus] || CardStatusMapper['99'];
|
||||||
|
|
||||||
return this.cardRepository.updateCardStatus(card.id, status, description);
|
return this.cardRepository.updateCardStatus(card.id, status, description);
|
||||||
|
@ -20,7 +20,7 @@ export class TransactionService {
|
|||||||
|
|
||||||
@Transactional()
|
@Transactional()
|
||||||
async createCardTransaction(body: CardTransactionWebhookRequest) {
|
async createCardTransaction(body: CardTransactionWebhookRequest) {
|
||||||
const card = await this.cardService.getCardByAccountNumber(body.cardId);
|
const card = await this.cardService.getCardByVpan(body.cardId);
|
||||||
const existingTransaction = await this.findExistingTransaction(body.transactionId, card.account.accountReference);
|
const existingTransaction = await this.findExistingTransaction(body.transactionId, card.account.accountReference);
|
||||||
|
|
||||||
if (existingTransaction) {
|
if (existingTransaction) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Body, Controller, Post } from '@nestjs/common';
|
import { Body, Controller, Post } from '@nestjs/common';
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { ResponseFactory } from '~/core/utils';
|
||||||
import {
|
import {
|
||||||
AccountCardStatusChangedWebhookRequest,
|
AccountCardStatusChangedWebhookRequest,
|
||||||
AccountTransactionWebhookRequest,
|
AccountTransactionWebhookRequest,
|
||||||
@ -14,16 +15,19 @@ export class NeoLeapWebhooksController {
|
|||||||
|
|
||||||
@Post('card-transaction')
|
@Post('card-transaction')
|
||||||
async handleCardTransactionWebhook(@Body() body: CardTransactionWebhookRequest) {
|
async handleCardTransactionWebhook(@Body() body: CardTransactionWebhookRequest) {
|
||||||
return this.neoleapWebhookService.handleCardTransactionWebhook(body);
|
await this.neoleapWebhookService.handleCardTransactionWebhook(body);
|
||||||
|
return ResponseFactory.data({ message: 'Card transaction processed successfully', status: 'success' });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('account-transaction')
|
@Post('account-transaction')
|
||||||
async handleAccountTransactionWebhook(@Body() body: AccountTransactionWebhookRequest) {
|
async handleAccountTransactionWebhook(@Body() body: AccountTransactionWebhookRequest) {
|
||||||
return this.neoleapWebhookService.handleAccountTransactionWebhook(body);
|
await this.neoleapWebhookService.handleAccountTransactionWebhook(body);
|
||||||
|
return ResponseFactory.data({ message: 'Account transaction processed successfully', status: 'success' });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('account-card-status-changed')
|
@Post('account-card-status-changed')
|
||||||
async handleAccountCardStatusChangedWebhook(@Body() body: AccountCardStatusChangedWebhookRequest) {
|
async handleAccountCardStatusChangedWebhook(@Body() body: AccountCardStatusChangedWebhookRequest) {
|
||||||
return this.neoleapWebhookService.handleAccountCardStatusChangedWebhook(body);
|
await this.neoleapWebhookService.handleAccountCardStatusChangedWebhook(body);
|
||||||
|
return ResponseFactory.data({ message: 'Card status updated successfully', status: 'success' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
src/db/migrations/1754210729273-add-vpan-to-card.ts
Normal file
22
src/db/migrations/1754210729273-add-vpan-to-card.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class AddVpanToCard1754210729273 implements MigrationInterface {
|
||||||
|
name = 'AddVpanToCard1754210729273';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "cards" ADD "vpan" character varying`);
|
||||||
|
await queryRunner.query(`
|
||||||
|
UPDATE "cards"
|
||||||
|
SET "vpan" = 'TEMP_VPAN_' || id
|
||||||
|
`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "cards" ALTER COLUMN "vpan" SET NOT NULL`);
|
||||||
|
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_1ec2ef68b0370f26639261e87b" ON "cards" ("vpan") `);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_1ec2ef68b0370f26639261e87b"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "cards" DROP COLUMN "vpan"`);
|
||||||
|
}
|
||||||
|
}
|
@ -4,3 +4,4 @@ export * from './1734247702310-seeds-goals-categories';
|
|||||||
export * from './1753869637732-seed-default-avatar';
|
export * from './1753869637732-seed-default-avatar';
|
||||||
export * from './1753874205042-add-neoleap-related-entities';
|
export * from './1753874205042-add-neoleap-related-entities';
|
||||||
export * from './1753948642040-add-account-number-and-iban-to-account-entity';
|
export * from './1753948642040-add-account-number-and-iban-to-account-entity';
|
||||||
|
export * from './1754210729273-add-vpan-to-card';
|
||||||
|
Reference in New Issue
Block a user