import BaseService from "@Services/BaseService"; import { Service } from "typedi"; import { BackendVariables } from "@Common/config/variables/Variables"; import DocumentsService from "@Services/super-admin/DocumentsService/DocumentsService"; import FilesService from "../FilesService/FilesService"; export type EnrollmentResponse = { url: string; id: number; api_key: string; callback_url: string; browser_callback_url: string; client_reference: string; status: string; reason: string | null; create_date: string; starting_time: string | null; finished_time: string | null; }; type EnrollmentInfoResponse = { status: string; browser_callback_url: string; browser_callback_token: string; user_process_done_time: string | null; routes: { uses_biometric: boolean; datas: {}; document_classes: string[]; constraints: string[]; id: string; status: string; }[]; }; type ReportInfoReponse = { enrollment: { callback_url: string; browser_callback_url: string; client_reference: string; }; process_uuid: string; id: number; status: string; process_id: number; external_methods: { france_connect: { status: string; creation_time: string; starting_time: string; finished_time: string; supplier_reference: string; results: { france_connect_out_token: [ { token_type: string; scope: string; access_token: string; expires_in: number; id_token: string; expires_at: number; }, ]; france_connect_out_userinfo: [ { sub: string; given_name: string; family_name: string; birthdate: string; gender: string; preferred_username: string; email: string; }, ]; }; }; }; archive_reference: string; }; type LoginResponse = { token: string; }; @Service() export default class Id360Service extends BaseService { private id360Token: string | null = null; private id360TokenExpiration: number | null = null; constructor(private variables: BackendVariables, private documentsService: DocumentsService, private filesService: FilesService) { super(); } public async createFranceConnectEnrollment() { const authHeader = `Token ${await this.getId360Token()}`; const bodyArgs = { callback_url: `${this.variables.BACK_API_HOST}/id360/customer-callback/`, browser_callback_url: `${this.variables.APP_HOST}/id360/customer-callback/`, callback_headers: {}, }; const res = await fetch( `${this.variables.DOCAPOST_BASE_URL + this.variables.DOCAPOST_ROOT + this.variables.DOCAPOST_VERSION}/process/${ this.variables.DOCAPOST_CONNECT_PROCESS_ID }/enrollment/`, { method: "POST", body: JSON.stringify(bodyArgs), headers: { "Content-Type": "application/json", Accept: "application/json", Authorization: authHeader }, }, ); const resJson = (await res.json()) as EnrollmentResponse; return { franceConnectUrl: `${this.variables.DOCAPOST_BASE_URL}static/process_ui/index.html#/enrollment/${resJson.api_key}`, processId: resJson.id, }; } public async createEnrollment(documentUid: string) { const authHeader = `Token ${await this.getId360Token()}`; const bodyArgs = { callback_url: `${this.variables.BACK_API_HOST}/id360/enrollment-callback/`, browser_callback_url: `${ this.variables.APP_LABEL + this.variables.BACK_API_HOST + this.variables.APP_ROOT_URL }/id360/enrollment-callback/`, client_reference: documentUid, callback_headers: {}, }; const res = await fetch( `${this.variables.DOCAPOST_BASE_URL + this.variables.DOCAPOST_ROOT + this.variables.DOCAPOST_VERSION}/process/${ this.variables.DOCAPOST_DOCUMENT_PROCESS_ID }/enrollment/`, { method: "POST", body: JSON.stringify(bodyArgs), headers: { "Content-Type": "application/json", Accept: "application/json", Authorization: authHeader }, }, ); const resJson = (await res.json()) as EnrollmentResponse; const route = await this.getRouteId(resJson.api_key); await this.selectRoute(resJson.api_key, route); await this.uploadDocument(resJson.api_key, documentUid); return await this.getReport(resJson.id); } public async uploadDocument(apiKey: string, documentUid: string) { const document = await this.documentsService.getByUidWithFiles(documentUid); if (document && document.files && document.files.length > 1) { const IDrecto = await this.filesService.download(document.files[0]!.uid); const IDverso = await this.filesService.download(document.files[1]!.uid); const rectoBlobData = new Blob([new Uint8Array(Array.from(IDrecto!.buffer))], { type: "application/octet-stream" }); const versoBlobData = new Blob([new Uint8Array(Array.from(IDverso!.buffer))], { type: "application/octet-stream" }); const resRecto = await fetch( `${ this.variables.DOCAPOST_BASE_URL + this.variables.DOCAPOST_ROOT + this.variables.DOCAPOST_VERSION }/enrollment/flow/document/id_document_image/?total_pages=2&uploaded_page=0`, { method: "POST", body: rectoBlobData, headers: { "Content-Type": "application/octet-stream", Accept: "*/*", "x-api-key": apiKey }, }, ); const resVerso = await fetch( `${ this.variables.DOCAPOST_BASE_URL + this.variables.DOCAPOST_ROOT + this.variables.DOCAPOST_VERSION }/enrollment/flow/document/id_document_image/?total_pages=2&uploaded_page=1`, { method: "POST", body: versoBlobData, headers: { "Content-Type": "application/octet-stream", Accept: "*/*", "x-api-key": apiKey }, }, ); console.log(await resRecto.json(), await resVerso.json()); await this.finalizeEnrollment(apiKey); } } public async getReport(enrollmentId: number) { const authHeader = `Token ${await this.getId360Token()}`; const res = await fetch( `${ this.variables.DOCAPOST_BASE_URL + this.variables.DOCAPOST_ROOT + this.variables.DOCAPOST_VERSION }/enrollment/${enrollmentId}/report/`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: authHeader }, }, ); return (await res.json()) as ReportInfoReponse; } public async getEnrollment(token: string) { return await fetch( `${ this.variables.DOCAPOST_BASE_URL + this.variables.DOCAPOST_ROOT + this.variables.DOCAPOST_VERSION }/enrollment/status/${token}`, { method: "GET" }, ); } public async finalizeEnrollment(apiKey: string) { return await fetch( `${ this.variables.DOCAPOST_BASE_URL + this.variables.DOCAPOST_ROOT + this.variables.DOCAPOST_VERSION }/enrollment/flow/finalize_enrollment/`, { method: "POST", headers: { "x-api-key": apiKey, Accept: "application/json" }, }, ); } public async getRouteId(apiKey: string) { const res = await fetch( `${ this.variables.DOCAPOST_BASE_URL + this.variables.DOCAPOST_ROOT + this.variables.DOCAPOST_VERSION }/enrollment/flow/enrollment_info/`, { method: "GET", headers: { "x-api-key": apiKey, Accept: "application/json" }, }, ); const resJson = (await res.json()) as EnrollmentInfoResponse; return resJson.routes[0]!.id; } public async selectRoute(apiKey: string, routeId: string) { return await fetch( `${ this.variables.DOCAPOST_BASE_URL + this.variables.DOCAPOST_ROOT + this.variables.DOCAPOST_VERSION }/enrollment/flow/route/${routeId}/select`, { method: "POST", headers: { "x-api-key": apiKey, Accept: "application/json" }, }, ); } public async getId360Token() { if (!this.id360Token || !this.id360TokenExpiration || this.id360TokenExpiration < Date.now()) { this.id360Token = await this.login(); this.id360TokenExpiration = Date.now() + 1000 * 60 * 14; // 14 minutes } return this.id360Token; } public async login(): Promise { const bodyArgs = { username: this.variables.DOCAPOST_APP_ID, password: this.variables.DOCAPOST_APP_PASSWORD, }; const res = await fetch( `${this.variables.DOCAPOST_BASE_URL + this.variables.DOCAPOST_ROOT + this.variables.DOCAPOST_VERSION}/user/login/`, { method: "POST", body: JSON.stringify(bodyArgs), headers: { "Content-Type": "application/json", Accept: "application/json" }, }, ); return ((await res.json()) as LoginResponse).token; } }