fix convert message
This commit is contained in:
parent
8406d79800
commit
f7820ddccf
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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) {
|
||||
@ -284,8 +373,8 @@ export class OrangeAdapter {
|
||||
|
||||
return mappedResponse;
|
||||
} catch (error) {
|
||||
return {
|
||||
challengeId,
|
||||
return {
|
||||
userAlias:'undefined',
|
||||
merchantId: originalRequest.merchantId,
|
||||
status: OtpChallengeStatusEnum.FAILED,
|
||||
error: {
|
||||
|
||||
@ -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>;
|
||||
|
||||
|
||||
@ -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}`);
|
||||
|
||||
@ -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: {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user