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> {
|
async get<T>(key: string, prefix?: string): Promise<T | null> {
|
||||||
try {
|
try {
|
||||||
const fullKey = this.buildKey(key, prefix);
|
const fullKey = this.buildKey(key, prefix);
|
||||||
|
this.logger.debug(`Cache fullkey: ${fullKey}`);
|
||||||
const data = await this.redis.get(fullKey);
|
const data = await this.redis.get(fullKey);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
this.logger.debug(`Cache miss: ${fullKey}`);
|
this.logger.debug(`Error Cache miss: ${fullKey}`);
|
||||||
return null;
|
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
|
* Builder pour construire des requêtes Orange Challenge
|
||||||
*/
|
*/
|
||||||
@ -147,6 +169,16 @@ export class OrangeChallengeRequestBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withInfo(infoValue: string): this {
|
||||||
|
this.request.challenge.inputs?.push(
|
||||||
|
{
|
||||||
|
"type": "info",
|
||||||
|
"value": infoValue
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construire la requête finale
|
* Construire la requête finale
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -2,7 +2,8 @@ import axios, { AxiosInstance, AxiosError } from 'axios';
|
|||||||
import {
|
import {
|
||||||
OrangeChallengeRequest,
|
OrangeChallengeRequest,
|
||||||
OrangeChallengeResponse,
|
OrangeChallengeResponse,
|
||||||
OrangeChallengeRequestBuilder
|
OrangeChallengeRequestBuilder,
|
||||||
|
OrangeVerifyResponse
|
||||||
} from './dtos/orange.challenge.dto'
|
} from './dtos/orange.challenge.dto'
|
||||||
import {
|
import {
|
||||||
OrangeConfig,
|
OrangeConfig,
|
||||||
@ -13,7 +14,7 @@ import {
|
|||||||
|
|
||||||
//import { OtpChallengeResponseDto, OtpChallengeStatusEnum } from '../../dtos/otp-challenge-response.dto';
|
//import { OtpChallengeResponseDto, OtpChallengeStatusEnum } from '../../dtos/otp-challenge-response.dto';
|
||||||
import { OtpChallengeRequestDto } from '../dto/challenge.request.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 { Logger } from '@nestjs/common';
|
||||||
import { log } from 'console';
|
import { log } from 'console';
|
||||||
|
|
||||||
@ -90,7 +91,49 @@ export class OrangeAdapter {
|
|||||||
/**
|
/**
|
||||||
* Convertir la requête générique en format Orange
|
* 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();
|
const builder = new OrangeChallengeRequestBuilder();
|
||||||
|
|
||||||
// Mapper le pays
|
// Mapper le pays
|
||||||
@ -116,17 +159,11 @@ export class OrangeAdapter {
|
|||||||
builder.withConfirmationCode(''); // Orange requiert ce champ même vide
|
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
|
// Nom de l'expéditeur
|
||||||
const senderName = request.config?.senderName || this.config.defaultSenderName;
|
const senderName = request.config?.senderName || this.config.defaultSenderName;
|
||||||
builder.withSenderName(senderName);
|
builder.withInfo("ise2");
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
@ -134,7 +171,7 @@ export class OrangeAdapter {
|
|||||||
/**
|
/**
|
||||||
* Convertir la réponse Orange en format générique
|
* Convertir la réponse Orange en format générique
|
||||||
*/
|
*/
|
||||||
private mapFromOrangeResponse(
|
private mapFromOrangeChallengeResponse(
|
||||||
orangeResponse: OrangeChallengeResponse,
|
orangeResponse: OrangeChallengeResponse,
|
||||||
request: OtpChallengeRequestDto
|
request: OtpChallengeRequestDto
|
||||||
): OtpChallengeResponseDto {
|
): OtpChallengeResponseDto {
|
||||||
@ -167,6 +204,36 @@ export class OrangeAdapter {
|
|||||||
return response;
|
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
|
* Mapper le statut Orange vers le statut générique
|
||||||
*/
|
*/
|
||||||
@ -182,6 +249,16 @@ export class OrangeAdapter {
|
|||||||
return OtpChallengeStatusEnum.PENDING;
|
return OtpChallengeStatusEnum.PENDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private mapOrangeResponseStatus(orangeResponse: OrangeVerifyResponse): OtpChallengeStatusEnum {
|
||||||
|
if (orangeResponse.error) {
|
||||||
|
return OtpChallengeStatusEnum.FAILED;
|
||||||
|
}else{
|
||||||
|
return OtpChallengeStatusEnum.VERIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gérer les erreurs HTTP
|
* Gérer les erreurs HTTP
|
||||||
*/
|
*/
|
||||||
@ -208,7 +285,7 @@ export class OrangeAdapter {
|
|||||||
//this.logger.debug(`initiateChallenge --> acces token ${token}`);
|
//this.logger.debug(`initiateChallenge --> acces token ${token}`);
|
||||||
|
|
||||||
// Mapper la requête
|
// Mapper la requête
|
||||||
const orangeRequest = this.mapToOrangeRequest(request);
|
const orangeRequest = this.mapToOrangeRequestChallenge(request);
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`[request to orange ]: ${JSON.stringify(orangeRequest, null, 2)}`,
|
`[request to orange ]: ${JSON.stringify(orangeRequest, null, 2)}`,
|
||||||
@ -226,7 +303,7 @@ export class OrangeAdapter {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Mapper la réponse
|
// Mapper la réponse
|
||||||
return this.mapFromOrangeResponse(response.data, request);
|
return this.mapFromOrangeChallengeResponse(response.data, request);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// En cas d'erreur, retourner une réponse avec le statut FAILED
|
// En cas d'erreur, retourner une réponse avec le statut FAILED
|
||||||
return {
|
return {
|
||||||
@ -249,7 +326,7 @@ export class OrangeAdapter {
|
|||||||
challengeId: string,
|
challengeId: string,
|
||||||
otpCode: string,
|
otpCode: string,
|
||||||
originalRequest: OtpChallengeRequestDto
|
originalRequest: OtpChallengeRequestDto
|
||||||
): Promise<OtpChallengeResponseDto> {
|
): Promise<OtpVerifResponseDto> {
|
||||||
try {
|
try {
|
||||||
// Créer une nouvelle requête avec le code de confirmation
|
// Créer une nouvelle requête avec le code de confirmation
|
||||||
const verifyRequest: OtpChallengeRequestDto = {
|
const verifyRequest: OtpChallengeRequestDto = {
|
||||||
@ -261,11 +338,15 @@ export class OrangeAdapter {
|
|||||||
const token = await this.getAccessToken();
|
const token = await this.getAccessToken();
|
||||||
|
|
||||||
// Mapper la requête
|
// 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
|
// Appeler l'API Orange pour vérification todo use request otp challenge
|
||||||
const response = await this.axiosInstance.post<OrangeChallengeResponse>(
|
//
|
||||||
this.config.challengeEndpoint,
|
const response = await this.axiosInstance.post<OrangeVerifyResponse>(
|
||||||
|
`${this.config.challengeEndpoint}/${challengeId}`,
|
||||||
orangeRequest,
|
orangeRequest,
|
||||||
{
|
{
|
||||||
headers: {
|
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
|
// 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é
|
// Si pas d'erreur, c'est vérifié
|
||||||
if (!mappedResponse.error) {
|
if (!mappedResponse.error) {
|
||||||
@ -285,7 +374,7 @@ export class OrangeAdapter {
|
|||||||
return mappedResponse;
|
return mappedResponse;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
challengeId,
|
userAlias:'undefined',
|
||||||
merchantId: originalRequest.merchantId,
|
merchantId: originalRequest.merchantId,
|
||||||
status: OtpChallengeStatusEnum.FAILED,
|
status: OtpChallengeStatusEnum.FAILED,
|
||||||
error: {
|
error: {
|
||||||
|
|||||||
@ -40,6 +40,36 @@ export class OtpChallengeResponseDto {
|
|||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
requiresConfirmation?: boolean;
|
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()
|
@IsOptional()
|
||||||
metadata?: Record<string, any>;
|
metadata?: Record<string, any>;
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
OtpChallengeResponseDto,
|
OtpChallengeResponseDto,
|
||||||
OtpChallengeStatusEnum,
|
OtpChallengeStatusEnum,
|
||||||
|
OtpVerifResponseDto,
|
||||||
} from './dto/challenge.response.dto';
|
} from './dto/challenge.response.dto';
|
||||||
import { OtpChallengeRequestDto } from './dto/challenge.request.dto';
|
import { OtpChallengeRequestDto } from './dto/challenge.request.dto';
|
||||||
import { OtpChallengeService } from './otp.challenge.service';
|
import { OtpChallengeService } from './otp.challenge.service';
|
||||||
@ -177,7 +178,7 @@ export class OtpChallengeController {
|
|||||||
@Body('otpCode') otpCode: string,
|
@Body('otpCode') otpCode: string,
|
||||||
@Headers('X-Merchant-ID') merchantId: string,
|
@Headers('X-Merchant-ID') merchantId: string,
|
||||||
@Headers('x-API-KEY') apiKey: string,
|
@Headers('x-API-KEY') apiKey: string,
|
||||||
): Promise<OtpChallengeResponseDto> {
|
): Promise<OtpVerifResponseDto> {
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
`[VERIFY] Merchant: ${merchantId}, Challenge: ${challengeId}`,
|
`[VERIFY] Merchant: ${merchantId}, Challenge: ${challengeId}`,
|
||||||
);
|
);
|
||||||
@ -193,6 +194,7 @@ export class OtpChallengeController {
|
|||||||
otpCode,
|
otpCode,
|
||||||
merchantId,
|
merchantId,
|
||||||
);
|
);
|
||||||
|
this.logger.log(`[VERIFY] Result - object: ${response}`);
|
||||||
|
|
||||||
// Logger le résultat
|
// Logger le résultat
|
||||||
this.logger.log(`[VERIFY] Result - Status: ${response.status}`);
|
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 { OrangeAdapter } from './adaptor/orange.adaptor';
|
||||||
import { OtpChallengeRequestDto } from './dto/challenge.request.dto';
|
import { OtpChallengeRequestDto } from './dto/challenge.request.dto';
|
||||||
import { IOtpChallengeService } from './otp.challenge.interface';
|
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 { RedisCacheService } from 'src/common/services/cache.redis';
|
||||||
import { CACHE_MANAGER } from '@nestjs/cache-manager';
|
import { CACHE_MANAGER } from '@nestjs/cache-manager';
|
||||||
|
|
||||||
@ -58,14 +58,15 @@ export class OtpChallengeService implements IOtpChallengeService {
|
|||||||
challengeId: string,
|
challengeId: string,
|
||||||
otpCode: string,
|
otpCode: string,
|
||||||
merchantId: string
|
merchantId: string
|
||||||
): Promise<OtpChallengeResponseDto> {
|
): Promise<any> {
|
||||||
try {
|
try {
|
||||||
// Récupérer le challenge depuis le cache
|
// 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) {
|
if (!cached) {
|
||||||
return {
|
return {
|
||||||
challengeId,
|
userAlias:"",
|
||||||
merchantId,
|
merchantId,
|
||||||
status: OtpChallengeStatusEnum.FAILED,
|
status: OtpChallengeStatusEnum.FAILED,
|
||||||
error: {
|
error: {
|
||||||
@ -79,7 +80,7 @@ export class OtpChallengeService implements IOtpChallengeService {
|
|||||||
// Vérifier que le merchantId correspond
|
// Vérifier que le merchantId correspond
|
||||||
if (cached.request.merchantId !== merchantId) {
|
if (cached.request.merchantId !== merchantId) {
|
||||||
return {
|
return {
|
||||||
challengeId,
|
userAlias:"",
|
||||||
merchantId,
|
merchantId,
|
||||||
status: OtpChallengeStatusEnum.FAILED,
|
status: OtpChallengeStatusEnum.FAILED,
|
||||||
error: {
|
error: {
|
||||||
@ -105,7 +106,7 @@ export class OtpChallengeService implements IOtpChallengeService {
|
|||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
challengeId,
|
userAlias:"",
|
||||||
merchantId,
|
merchantId,
|
||||||
status: OtpChallengeStatusEnum.FAILED,
|
status: OtpChallengeStatusEnum.FAILED,
|
||||||
error: {
|
error: {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user