fix convert message

This commit is contained in:
Mamadou Khoussa [028918 DSI/DAC/DIF/DS] 2025-10-30 01:43:10 +00:00
parent 8406d79800
commit f7820ddccf
6 changed files with 185 additions and 30 deletions

View File

@ -41,10 +41,11 @@ export class RedisCacheService {
async get<T>(key: string, prefix?: string): Promise<T | null> {
try {
const fullKey = this.buildKey(key, prefix);
this.logger.debug(`Cache fullkey: ${fullKey}`);
const data = await this.redis.get(fullKey);
if (!data) {
this.logger.debug(`Cache miss: ${fullKey}`);
this.logger.debug(`Error Cache miss: ${fullKey}`);
return null;
}

View File

@ -34,6 +34,28 @@ export interface OrangeChallengeResponse {
};
}
export interface OrangeVerifyResponse {
challenge: {
method: string,
country: string,
service: string,
partnerId:string,
inputs: [ ]
result:any[ ],/*[
{
type: 'ise2',
value: 'PDKSUB-200-KzIyMTc3MTcxNzE3MS1TRU4tMTc2MTc4MzI2NjAy'
}
]*/
}
error?: {
code: number | string;
message: string;
description?: string;
};
}
/**
* Builder pour construire des requêtes Orange Challenge
*/
@ -147,6 +169,16 @@ export class OrangeChallengeRequestBuilder {
return this;
}
withInfo(infoValue: string): this {
this.request.challenge.inputs?.push(
{
"type": "info",
"value": infoValue
}
)
return this;
}
/**
* Construire la requête finale
*/

View File

@ -2,7 +2,8 @@ import axios, { AxiosInstance, AxiosError } from 'axios';
import {
OrangeChallengeRequest,
OrangeChallengeResponse,
OrangeChallengeRequestBuilder
OrangeChallengeRequestBuilder,
OrangeVerifyResponse
} from './dtos/orange.challenge.dto'
import {
OrangeConfig,
@ -13,7 +14,7 @@ import {
//import { OtpChallengeResponseDto, OtpChallengeStatusEnum } from '../../dtos/otp-challenge-response.dto';
import { OtpChallengeRequestDto } from '../dto/challenge.request.dto';
import { OtpChallengeResponseDto, OtpChallengeStatusEnum } from '../dto/challenge.response.dto';
import { OtpChallengeResponseDto, OtpChallengeStatusEnum, OtpVerifResponseDto } from '../dto/challenge.response.dto';
import { Logger } from '@nestjs/common';
import { log } from 'console';
@ -90,7 +91,49 @@ export class OrangeAdapter {
/**
* Convertir la requête générique en format Orange
*/
private mapToOrangeRequest(request: OtpChallengeRequestDto): OrangeChallengeRequest {
private mapToOrangeRequestChallenge(request: OtpChallengeRequestDto): OrangeChallengeRequest {
const builder = new OrangeChallengeRequestBuilder();
// Mapper le pays
const orangeCountry = COUNTRY_CODE_MAPPING[request.country] || request.country;
builder.withCountry(orangeCountry);
// Mapper la méthode
const orangeMethod = OTP_METHOD_MAPPING[request.method] || 'OTP-SMS-AUTH';
builder.withMethod(orangeMethod);
// Ajouter les informations de base
builder
.withService(request.service)
.withPartnerId(this.config.partnerId);
// Ajouter l'identifiant
builder.withIdentifier(request.identifier.type, request.identifier.value);
// Ajouter le code de confirmation si présent
/* todo voir si mandatory
if (request.confirmationCode) {
builder.withConfirmationCode(request.confirmationCode);
} else {
builder.withConfirmationCode(''); // Orange requiert ce champ même vide
}*/
// Configuration du message OTP
const message = request.config?.message || this.config.defaultOtpMessage;
builder.withMessage(message);
// Longueur de l'OTP
const otpLength = request.config?.length || this.config.defaultOtpLength;
builder.withOtpLength(otpLength);
// Nom de l'expéditeur
const senderName = request.config?.senderName || this.config.defaultSenderName;
builder.withSenderName(senderName);
return builder.build();
}
private mapToOrangeRequestVerify(request: OtpChallengeRequestDto): OrangeChallengeRequest {
const builder = new OrangeChallengeRequestBuilder();
// Mapper le pays
@ -116,17 +159,11 @@ export class OrangeAdapter {
builder.withConfirmationCode(''); // Orange requiert ce champ même vide
}
// Configuration du message OTP
const message = request.config?.message || this.config.defaultOtpMessage;
builder.withMessage(message);
// Longueur de l'OTP
const otpLength = request.config?.length || this.config.defaultOtpLength;
builder.withOtpLength(otpLength);
// Nom de l'expéditeur
const senderName = request.config?.senderName || this.config.defaultSenderName;
builder.withSenderName(senderName);
builder.withInfo("ise2");
return builder.build();
}
@ -134,7 +171,7 @@ export class OrangeAdapter {
/**
* Convertir la réponse Orange en format générique
*/
private mapFromOrangeResponse(
private mapFromOrangeChallengeResponse(
orangeResponse: OrangeChallengeResponse,
request: OtpChallengeRequestDto
): OtpChallengeResponseDto {
@ -167,6 +204,36 @@ export class OrangeAdapter {
return response;
}
private mapFromOrangeVerifyResponse(
orangeResponse: OrangeVerifyResponse,
request: OtpChallengeRequestDto
): OtpVerifResponseDto {
console.log('mapFromOrangeVerifyResponse',orangeResponse.challenge.result)
const response: OtpVerifResponseDto = {
merchantId: request.merchantId,
status: this.mapOrangeResponseStatus(orangeResponse),
userAlias: orangeResponse.challenge.result?.[0]['value'] || 'not presenter',
metadata: {
provider: 'orange',
country: request.country,
method: request.method
}
};
// Ajouter l'erreur si présente
if (orangeResponse.error) {
response.error = {
code: orangeResponse.error.code.toString(),
message: orangeResponse.error.message,
description: orangeResponse.error.description
};
response.status = OtpChallengeStatusEnum.FAILED;
}
return response;
}
/**
* Mapper le statut Orange vers le statut générique
*/
@ -182,6 +249,16 @@ export class OrangeAdapter {
return OtpChallengeStatusEnum.PENDING;
}
private mapOrangeResponseStatus(orangeResponse: OrangeVerifyResponse): OtpChallengeStatusEnum {
if (orangeResponse.error) {
return OtpChallengeStatusEnum.FAILED;
}else{
return OtpChallengeStatusEnum.VERIFIED;
}
}
/**
* Gérer les erreurs HTTP
*/
@ -208,7 +285,7 @@ export class OrangeAdapter {
//this.logger.debug(`initiateChallenge --> acces token ${token}`);
// Mapper la requête
const orangeRequest = this.mapToOrangeRequest(request);
const orangeRequest = this.mapToOrangeRequestChallenge(request);
this.logger.debug(
`[request to orange ]: ${JSON.stringify(orangeRequest, null, 2)}`,
@ -226,7 +303,7 @@ export class OrangeAdapter {
);
// Mapper la réponse
return this.mapFromOrangeResponse(response.data, request);
return this.mapFromOrangeChallengeResponse(response.data, request);
} catch (error) {
// En cas d'erreur, retourner une réponse avec le statut FAILED
return {
@ -249,7 +326,7 @@ export class OrangeAdapter {
challengeId: string,
otpCode: string,
originalRequest: OtpChallengeRequestDto
): Promise<OtpChallengeResponseDto> {
): Promise<OtpVerifResponseDto> {
try {
// Créer une nouvelle requête avec le code de confirmation
const verifyRequest: OtpChallengeRequestDto = {
@ -261,11 +338,15 @@ export class OrangeAdapter {
const token = await this.getAccessToken();
// Mapper la requête
const orangeRequest = this.mapToOrangeRequest(verifyRequest);
const orangeRequest = this.mapToOrangeRequestVerify(verifyRequest);
this.logger.debug(
`[request to orange (verify) ]: ${JSON.stringify(orangeRequest, null, 2)}`,
)
// Appeler l'API Orange pour vérification
const response = await this.axiosInstance.post<OrangeChallengeResponse>(
this.config.challengeEndpoint,
// Appeler l'API Orange pour vérification todo use request otp challenge
//
const response = await this.axiosInstance.post<OrangeVerifyResponse>(
`${this.config.challengeEndpoint}/${challengeId}`,
orangeRequest,
{
headers: {
@ -273,9 +354,17 @@ export class OrangeAdapter {
}
}
);
//${JSON.stringify(response, null, 2)}
this.logger.debug(
`[response from orange (verify) ${JSON.stringify(response.data, null, 2)} ]: `,
)
// Mapper la réponse
const mappedResponse = this.mapFromOrangeResponse(response.data, verifyRequest);
const mappedResponse = this.mapFromOrangeVerifyResponse(response.data, verifyRequest);
this.logger.debug(
`[response parsed from orange (verify) ${JSON.stringify(mappedResponse, null, 2)} ]: `,
)
// Si pas d'erreur, c'est vérifié
if (!mappedResponse.error) {
@ -285,7 +374,7 @@ export class OrangeAdapter {
return mappedResponse;
} catch (error) {
return {
challengeId,
userAlias:'undefined',
merchantId: originalRequest.merchantId,
status: OtpChallengeStatusEnum.FAILED,
error: {

View File

@ -40,6 +40,36 @@ export class OtpChallengeResponseDto {
@IsBoolean()
requiresConfirmation?: boolean;
@IsOptional()
metadata?: Record<string, any>;
@IsOptional()
error?: {
code: string;
message: string;
description?: string;
};
}
export class OtpVerifResponseDto {
@IsString()
@IsNotEmpty()
merchantId: string;
@IsString()
@IsNotEmpty()
userAlias: string;
@IsEnum(OtpChallengeStatusEnum)
@IsNotEmpty()
status: OtpChallengeStatusEnum;
@IsOptional()
@IsString()
message?: string;
@IsOptional()
metadata?: Record<string, any>;

View File

@ -23,6 +23,7 @@ import {
import {
OtpChallengeResponseDto,
OtpChallengeStatusEnum,
OtpVerifResponseDto,
} from './dto/challenge.response.dto';
import { OtpChallengeRequestDto } from './dto/challenge.request.dto';
import { OtpChallengeService } from './otp.challenge.service';
@ -177,7 +178,7 @@ export class OtpChallengeController {
@Body('otpCode') otpCode: string,
@Headers('X-Merchant-ID') merchantId: string,
@Headers('x-API-KEY') apiKey: string,
): Promise<OtpChallengeResponseDto> {
): Promise<OtpVerifResponseDto> {
this.logger.log(
`[VERIFY] Merchant: ${merchantId}, Challenge: ${challengeId}`,
);
@ -193,6 +194,7 @@ export class OtpChallengeController {
otpCode,
merchantId,
);
this.logger.log(`[VERIFY] Result - object: ${response}`);
// Logger le résultat
this.logger.log(`[VERIFY] Result - Status: ${response.status}`);

View File

@ -3,7 +3,7 @@ import type { OrangeConfig } from './adaptor/orange.config';
import { OrangeAdapter } from './adaptor/orange.adaptor';
import { OtpChallengeRequestDto } from './dto/challenge.request.dto';
import { IOtpChallengeService } from './otp.challenge.interface';
import { OtpChallengeResponseDto, OtpChallengeStatusEnum } from './dto/challenge.response.dto';
import { OtpChallengeResponseDto, OtpChallengeStatusEnum, OtpVerifResponseDto } from './dto/challenge.response.dto';
import { RedisCacheService } from 'src/common/services/cache.redis';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
@ -58,14 +58,15 @@ export class OtpChallengeService implements IOtpChallengeService {
challengeId: string,
otpCode: string,
merchantId: string
): Promise<OtpChallengeResponseDto> {
): Promise<any> {
try {
// Récupérer le challenge depuis le cache
const cached:any = this.cacheService.get(challengeId);
const cached:any =await this.cacheService.get(challengeId,this.CACHE_PREFIX,);
this.logger.debug(`cache retrieve , ${cached}`)
if (!cached) {
return {
challengeId,
userAlias:"",
merchantId,
status: OtpChallengeStatusEnum.FAILED,
error: {
@ -79,7 +80,7 @@ export class OtpChallengeService implements IOtpChallengeService {
// Vérifier que le merchantId correspond
if (cached.request.merchantId !== merchantId) {
return {
challengeId,
userAlias:"",
merchantId,
status: OtpChallengeStatusEnum.FAILED,
error: {
@ -105,7 +106,7 @@ export class OtpChallengeService implements IOtpChallengeService {
return response;
} catch (error) {
return {
challengeId,
userAlias:"",
merchantId,
status: OtpChallengeStatusEnum.FAILED,
error: {