Merge branch 'alpha' into passenger/feat/iam-integration

This commit is contained in:
Abubeker Yasin
2026-06-03 10:20:25 +03:00
102 changed files with 4839 additions and 1993 deletions

View File

@@ -10,7 +10,7 @@ export class PassengerInputDto {
@ApiProperty({ example: 'NATIONAL_ID', enum: IdDocumentType, description: 'NATIONAL_ID for Ethiopians (Verifayda verified), PASSPORT for others' }) @IsEnum(IdDocumentType) idDocumentType: IdDocumentType;
@ApiPropertyOptional({ example: 'ET123456789', description: 'Ethiopian national ID - verified via Verifayda 2.0 (NOT stored in database)' }) @IsOptional() @IsString() idDocumentNumber?: string;
@ApiPropertyOptional({ example: 'P1234567', description: 'Passport number for non-Ethiopian passengers (no verification)' }) @IsOptional() @IsString() passportNumber?: string;
@ApiPropertyOptional({ example: 'Kenya', description: 'Passport issuing country for non-Ethiopians' }) @IsOptional() @IsString() passportCountry?: string;
@ApiPropertyOptional({ example: 'Djibouti', description: 'Passport issuing country for non-Ethiopians' }) @IsOptional() @IsString() passportCountry?: string;
@ApiPropertyOptional({ example: 'Ethiopian', description: 'Ethiopian (Verifayda + Telebirr/CBE/eBirr), Djiboutian (Passport + Waafi), Other (Passport + Card)' }) @IsOptional() @IsString() nationality?: string;
}

View File

@@ -22,7 +22,7 @@ export class GuestPassengerDto {
@ApiPropertyOptional({ example: 'P1234567', description: 'Passport number for non-Ethiopians' })
@IsOptional() @IsString() passportNumber?: string;
@ApiPropertyOptional({ example: 'Kenya', description: 'Passport issuing country' })
@ApiPropertyOptional({ example: 'Djibouti', description: 'Passport issuing country' })
@IsOptional() @IsString() passportCountry?: string;
@ApiPropertyOptional({ example: 'Ethiopian', description: 'Ethiopian (Verifayda), Djiboutian, Other' })

View File

@@ -71,28 +71,42 @@ export class GuestBookingService {
let verifaydaData: Record<string, any> | undefined;
let nationality = passenger.nationality;
// Verifayda verification ONLY for Ethiopian nationals with National ID
const isEthiopian = !passenger.nationality || passenger.nationality === 'Ethiopian' ||
(passenger.idDocumentType === IdDocumentType.NATIONAL_ID && !passenger.passportCountry);
// Determine if passenger is Ethiopian
const isEthiopian = passenger.nationality === 'Ethiopian' ||
passenger.nationality === 'ETHIOPIAN' ||
passenger.idDocumentType === IdDocumentType.NATIONAL_ID;
if (isEthiopian && passenger.idDocumentType === IdDocumentType.NATIONAL_ID && passenger.idDocumentNumber) {
const verification = await this.verifaydaService.verifyNationalId(passenger.idDocumentNumber);
if (!verification.verified) {
throw new BadRequestException(
`Verifayda verification failed for ${passenger.passengerName}: ${verification.failureReason}`
);
// Ethiopian with National ID
if (isEthiopian && passenger.idDocumentType === IdDocumentType.NATIONAL_ID) {
if (passenger.idDocumentNumber) {
// Attempt Fayda verification
const verification = await this.verifaydaService.verifyNationalId(passenger.idDocumentNumber);
if (!verification.verified) {
throw new BadRequestException(
`Verifayda verification failed for ${passenger.passengerName}: ${verification.failureReason}`
);
}
passengerName = verification.passengerData?.fullName || passengerName;
verifaydaVerified = true;
verifaydaData = verification.passengerData?.profileData;
}
passengerName = verification.passengerData?.fullName || passengerName;
verifaydaVerified = true;
verifaydaData = verification.passengerData?.profileData;
nationality = 'Ethiopian';
} else if (passenger.idDocumentType === IdDocumentType.PASSPORT) {
}
// International passenger with Passport (non-Ethiopian)
else if (!isEthiopian && passenger.idDocumentType === IdDocumentType.PASSPORT) {
// Passport details are required for international passengers
if (!passenger.passportNumber || !passenger.passportCountry) {
throw new BadRequestException(`Passport number and country required for ${passenger.passengerName}`);
}
nationality = nationality || (passenger.passportCountry === 'Djibouti' ? 'Djiboutian' : 'Other');
} else if (passenger.idDocumentType === IdDocumentType.NATIONAL_ID && !isEthiopian) {
// Non-Ethiopian with national ID (e.g., Djiboutian national ID)
}
// Ethiopian with Passport (manual entry without Fayda)
else if (isEthiopian && passenger.idDocumentType === IdDocumentType.PASSPORT) {
// Ethiopians can use passport instead of national ID
nationality = 'Ethiopian';
}
// International with National ID (e.g., Djiboutian national ID)
else if (!isEthiopian && passenger.idDocumentType === IdDocumentType.NATIONAL_ID) {
nationality = nationality || 'Other';
}
@@ -175,11 +189,23 @@ export class GuestBookingService {
createdAccount = true;
} else {
// Create anonymous guest passenger with minimal data
const uniqueId = `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
// Check if email exists and use a unique guest email if it does
let guestEmail = firstPassenger.email || `guest-${uniqueId}@edr-platform.com`;
if (firstPassenger.email) {
const existingUser = await this.prisma.user.findUnique({ where: { email: firstPassenger.email } });
if (existingUser) {
// Email exists, use guest email instead for anonymous booking
guestEmail = `guest-${uniqueId}@edr-platform.com`;
}
}
const tempUser = await this.prisma.user.create({
data: {
fullName: firstPassenger.passengerName,
email: firstPassenger.email || `guest-${Date.now()}@edr-platform.com`,
phone: firstPassenger.phone || `+251${Date.now()}`,
email: guestEmail,
phone: firstPassenger.phone || `+251${uniqueId.replace(/[^0-9]/g, '').slice(0, 9)}`,
passwordHash: await bcrypt.hash(Math.random().toString(36), 10),
role: 'PASSENGER',
},