sdk-node-apis-efi
Version:
Module for integration with Efi Bank API
168 lines (155 loc) • 5.54 kB
text/typescript
import { endpoints } from './constants/endpoints';
import { httpRequest, HttpClientOptions } from './httpClient';
import { PixCreateImmediateChargeBody, PixCreateImmediateChargeResponse } from './types/pix';
import axios from 'axios';
import https from 'https';
import fs from 'fs';
export interface SdkOptions {
sandbox: boolean;
client_id: string;
client_secret: string;
certificate?: string;
pemKey?: string;
cert_base64?: boolean;
partner_token?: string;
validateMtls?: boolean;
idempotencyKey?: string;
}
interface AuthData {
access_token: string;
expires_in: number;
token_type: string;
scope?: string;
authDate: number; // timestamp em segundos
}
type ApiType = 'PIX' | 'DEFAULT';
function getApiConfig(api: ApiType, sandbox: boolean) {
const apiConfig = endpoints.APIS[api];
return {
baseUrl: sandbox ? apiConfig.URL.SANDBOX : apiConfig.URL.PRODUCTION,
authRoute: apiConfig.ENDPOINTS.authorize.route,
};
}
export class EfiSdk {
private options: SdkOptions;
private tokenMap: Record<ApiType, AuthData | null> = { PIX: null, DEFAULT: null };
constructor(options: SdkOptions) {
if (!options.client_id || !options.client_secret) {
throw new Error('client_id e client_secret são obrigatórios');
}
this.options = options;
}
private isTokenExpired(token: AuthData | null): boolean {
if (!token) return true;
const now = Math.floor(Date.now() / 1000);
if (typeof token.expires_in !== 'number' || typeof token.authDate !== 'number') return true;
return now > (token.authDate + token.expires_in - 60); // 60s de margem
}
private buildAgent(): https.Agent | undefined {
if (!this.options.certificate) return undefined;
try {
if (!this.options.cert_base64) {
if (this.options.pemKey) {
return new https.Agent({
cert: fs.readFileSync(this.options.certificate),
key: fs.readFileSync(this.options.pemKey),
passphrase: '',
});
} else {
return new https.Agent({
pfx: fs.readFileSync(this.options.certificate),
passphrase: '',
});
}
} else {
if (this.options.pemKey) {
return new https.Agent({
cert: Buffer.from(this.options.certificate, 'base64'),
key: Buffer.from(this.options.pemKey, 'base64'),
passphrase: '',
});
} else {
return new https.Agent({
pfx: Buffer.from(this.options.certificate, 'base64'),
passphrase: '',
});
}
}
} catch (error) {
throw new Error('Erro ao ler o certificado ou chave: ' + (error as Error).message);
}
}
private async authenticate(api: ApiType): Promise<string> {
const { baseUrl, authRoute } = getApiConfig(api, this.options.sandbox);
const currentToken = this.tokenMap[api];
if (currentToken && !this.isTokenExpired(currentToken)) {
return currentToken.access_token;
}
const agent = this.buildAgent();
const isDefault = api === 'DEFAULT';
let authParams: any = {
method: 'POST',
url: baseUrl + authRoute,
headers: {
'api-sdk': 'efi-node-typescript',
},
data: {
grant_type: 'client_credentials',
},
httpsAgent: agent,
};
if (isDefault) {
authParams.auth = {
username: this.options.client_id,
password: this.options.client_secret,
};
} else {
const token = Buffer.from(this.options.client_id + ':' + this.options.client_secret).toString('base64');
authParams.headers['Authorization'] = 'Basic ' + token;
authParams.headers['Content-Type'] = 'application/json';
}
const res = await axios(authParams);
this.tokenMap[api] = {
...res.data,
authDate: Math.floor(Date.now() / 1000),
};
return res.data.access_token;
}
private async authorizedRequest<T = any>(api: ApiType, opts: Omit<HttpClientOptions, 'authorization' | 'baseUrl'>): Promise<T> {
const { baseUrl } = getApiConfig(api, this.options.sandbox);
const access_token = await this.authenticate(api);
return httpRequest<T>({
...opts,
baseUrl,
authorization: `Bearer ${access_token}`,
});
}
async pixCreateImmediateCharge(body: PixCreateImmediateChargeBody, headers?: Record<string, string>): Promise<PixCreateImmediateChargeResponse> {
return this.authorizedRequest<PixCreateImmediateChargeResponse>('PIX', {
endpoint: endpoints.APIS.PIX.ENDPOINTS.pixCreateImmediateCharge,
body,
headers,
certificate: this.options.certificate,
pemKey: this.options.pemKey,
cert_base64: this.options.cert_base64,
partner_token: this.options.partner_token,
validateMtls: this.options.validateMtls,
idempotencyKey: this.options.idempotencyKey,
});
}
// Exemplo de método para DEFAULT (ex: listCharges)
async listCharges(params?: Record<string, any>, headers?: Record<string, string>): Promise<any> {
return this.authorizedRequest<any>('DEFAULT', {
endpoint: endpoints.APIS.DEFAULT.ENDPOINTS.listCharges,
params,
headers,
certificate: this.options.certificate,
pemKey: this.options.pemKey,
cert_base64: this.options.cert_base64,
partner_token: this.options.partner_token,
validateMtls: this.options.validateMtls,
idempotencyKey: this.options.idempotencyKey,
});
}
// Adicione outros métodos do SDK aqui, seguindo o mesmo padrão
}