UNPKG

sdk-node-apis-efi

Version:

Module for integration with Efi Bank API

168 lines (155 loc) 5.54 kB
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 }