/** * PSE Gateway. */ import { IDENTIFIERS as CORE, ILogger, IRollbar, KushkiError, } from "@kushki/core"; import { IDENTIFIERS } from "constant/Identifiers"; import * as fs from "fs"; import { ERRORS } from "infrastructure/ErrorEnum"; import { ProcessorTypeEnum } from "infrastructure/ProcessorTypeEnum"; import { PseErrorEnum } from "infrastructure/PseErrorEnum"; import { inject, injectable } from "inversify"; import { ILambdaGateway } from "repository/ILambdaGateway"; import { IPseGateway } from "repository/IPseGateway"; import { IPSESoap } from "repository/IPSESoap"; import * as Rollbar from "rollbar"; import { Observable, of, throwError } from "rxjs"; import { tag } from "rxjs-spy/operators"; import { catchError, map, mapTo, switchMap } from "rxjs/operators"; import { Client, createClientAsync, WSSecurity, WSSecurityCert } from "soap"; import { PseCreateTransaction } from "types/pse_create_transaction"; import { PseCreateTransactionRequest } from "types/pse_create_transaction_request"; import { PseFinalizeTransaction } from "types/pse_finalize_transaction"; import { PseGetBankList } from "types/pse_get_bank_list"; import { PseGetTransactionInformationRequest } from "types/pse_get_transaction_information_request"; import { PseGetTransactionInformationResponse } from "types/pse_get_transaction_information_response"; /** * Implementation */ @injectable() export class PseGateway implements IPseGateway { private _logger: ILogger; private _gateway: ILambdaGateway; private _rollbar: Rollbar; constructor( @inject(CORE.ILogger) logger: ILogger, @inject(IDENTIFIERS.LambdaGateway) gateway: ILambdaGateway, @inject(CORE.IRollbar) rollbar: IRollbar ) { this._logger = logger; this._gateway = gateway; this._rollbar = rollbar.init(); } public getBankListRequest( entityCode: string | undefined, processorType: string ): Observable { const args: object = { getBankListInformation: { entityCode: processorType === ProcessorTypeEnum.AGGREGATOR ? process.env.PSE_ENTITY_CODE : entityCode, }, }; return this._call( "getBankList", args, processorType ).pipe( map((data: PseGetBankList[]) => data[0]), tag("PseGateway | getBankListRequest") ); } public createTransactionRequest( body: PseCreateTransactionRequest, processorType: string ): Observable { body.entityurl = `${process.env.PSE_AGENT_URL}?ticketId=${body.ticketId}`; body.entityCode = processorType === ProcessorTypeEnum.AGGREGATOR ? `${process.env.PSE_ENTITY_CODE}` : body.entityCode; body.serviceCode = processorType === ProcessorTypeEnum.AGGREGATOR ? `${process.env.PSE_SERVICE_CODE}` : body.serviceCode; const args: object = { createTransactionPaymentInformation: body, }; return this._call( "createTransactionPayment", args, processorType ).pipe( map((data: PseCreateTransaction[]) => data[0]), map((response: PseCreateTransaction) => { const return_code: string = response.createTransactionPaymentResponseInformation.returnCode; const transaction_state: string = response.createTransactionPaymentResponseInformation.transactionState; if (return_code !== "SUCCESS") throw new KushkiError(ERRORS.E006, `${PseErrorEnum[return_code]}`); if (transaction_state === "NOT_AUTHORIZED") throw new KushkiError( ERRORS.E006, `${PseErrorEnum[transaction_state]}` ); return response; }), tag("PseGateway | createTransactionRequest") ); } public getTransactionInformation( body: PseGetTransactionInformationRequest, processorType: string ): Observable { body.entityCode = processorType === ProcessorTypeEnum.AGGREGATOR ? `${process.env.PSE_ENTITY_CODE}` : body.entityCode; const args: object = { getTransactionInformationBody: body, }; return this._call( "getTransactionInformation", args, processorType ).pipe( map((data: PseGetTransactionInformationResponse[]) => data[0]), tag("PseGateway | getTransactionInformation"), catchError((err: Error) => throwError(new KushkiError(ERRORS.E006, err.message)) ) ); } public finalizeTransactionPayment( body: PseGetTransactionInformationRequest, processorType: string ): Observable { body.entityCode = processorType === ProcessorTypeEnum.AGGREGATOR ? `${process.env.PSE_ENTITY_CODE}` : body.entityCode; const args: { finalizeTransactionPaymentInformation: { entityCode: string; trazabilityCode: string; }; } = { finalizeTransactionPaymentInformation: body, }; return this._call( "finalizeTransactionPayment", args, processorType ).pipe( tag("PseGateway | finalizeTransactionPayment"), mapTo(true) ); } public call( method: string, args: object, processorType: ProcessorTypeEnum ): Observable { this._logger.info(`PSE Method: ${method}`); this._logger.info("PSE Arguments", args); return this._getClient(processorType).pipe( switchMap((client: IPSESoap) => { const fn: Function = <(args: object, opts: object) => Promise>( client[`${method}Async`] ); return of(1).pipe( switchMap(() => fn(args, { timeout: Number(`${process.env.PSE_TIMEOUT_SERVICE}`), rejectUnauthorized: false, ca: fs.readFileSync( `resources/pse-ssl2-${process.env.PSE_STAGE}.cer`, "utf8" ), }) ), tag("PseGateway | call"), catchError((err: Error) => { this._rollbar.error(err); return throwError(new KushkiError(ERRORS.E006, PseErrorEnum.FAIL)); }) ); }) ); } private _call( method: string, args: object, processorType: string ): Observable { if (processorType === ProcessorTypeEnum.AGGREGATOR) return this._gateway.invokeFunction(`${process.env.LAMBDA_AGR}`, { method, args, processorType, }); return this._gateway.invokeFunction(`${process.env.LAMBDA_GTW}`, { method, args, processorType, }); } private _getClient(processorType: ProcessorTypeEnum): Observable { this._logger.info("PSE Creating Client"); const public_key: string = fs.readFileSync( `resources/pse${ processorType === ProcessorTypeEnum.AGGREGATOR ? "-agr" : "" }-public-${process.env.PSE_STAGE}.cer`, "utf8" ); this._logger.info( `PSE Public Key read: resources/pse${ processorType === ProcessorTypeEnum.AGGREGATOR ? "-agr" : "" }-public-${process.env.PSE_STAGE}.cer` ); const private_key: string = fs.readFileSync( `resources/pse${ processorType === ProcessorTypeEnum.AGGREGATOR ? "-agr" : "" }-private-${process.env.PSE_STAGE}.pem`, "utf8" ); this._logger.info( `PSE Private Key read: resources/pse${ processorType === ProcessorTypeEnum.AGGREGATOR ? "-agr" : "" }-private-${process.env.PSE_STAGE}.pem` ); const password: string = `${process.env.PSE_PASS_CERT}`; const ws_security: WSSecurity = new WSSecurityCert( private_key, public_key, password ); this._logger.info("PSE WS Security created"); const url: string = `${process.env.PSE_URL}`; this._logger.info(`PSE WSDL url: ${url}`); process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; return of(1).pipe( switchMap(() => createClientAsync(url, { forceSoap12Headers: false, }) ), map((client: Client) => { process.env.NODE_TLS_REJECT_UNAUTHORIZED = "1"; client.setSecurity(ws_security); client.on("request", (xml: string) => { this._logger.info(`PSE Request XML:\n ${xml}`); }); client.on("response", (xml: string) => { this._logger.info(`PSE Response XML:\n ${xml}`); }); this._logger.info("PSE Client created"); return client; }), tag("PseGateway | _getClient") ); } }