UNPKG

efi-easy-pix

Version:

A simple Node.js module for integrating with Efipay's PIX API

302 lines (250 loc) 9.94 kB
const axios = require('axios'); const fs = require('fs-extra'); const https = require('https'); const { QRCode } = require('./QRCode'); class EfipayClient { constructor(config) { if (!config) { throw new Error('Configuration object is required'); } if (!config.clientId || !config.clientSecret || !config.certificatePath) { throw new Error('clientId, clientSecret, and certificatePath are required'); } this.config = { clientId: config.clientId, clientSecret: config.clientSecret, certificatePath: config.certificatePath, sandbox: config.sandbox || false, baseUrl: config.sandbox ? 'https://pix-h.api.efipay.com.br' : 'https://pix.api.efipay.com.br' }; this.accessToken = null; this.tokenExpiration = null; } async getAccessToken() { if (this.accessToken && this.tokenExpiration > Date.now()) { return this.accessToken; } const auth = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64'); const certificate = await fs.readFile(this.config.certificatePath); const httpsAgent = new https.Agent({ pfx: certificate, passphrase: '' }); try { const response = await axios({ method: 'POST', url: `${this.config.baseUrl}/oauth/token`, headers: { 'Authorization': `Basic ${auth}`, 'Content-Type': 'application/json' }, data: { grant_type: 'client_credentials' }, httpsAgent }); this.accessToken = response.data.access_token; this.tokenExpiration = Date.now() + (response.data.expires_in * 1000); return this.accessToken; } catch (error) { throw new Error(`Failed to get access token: ${error.message}`); } } async request(method, endpoint, data = null, headers = {}) { const token = await this.getAccessToken(); const certificate = await fs.readFile(this.config.certificatePath); const httpsAgent = new https.Agent({ pfx: certificate, passphrase: '' }); try { const response = await axios({ method, url: `${this.config.baseUrl}${endpoint}`, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', ...headers }, data, httpsAgent }); return { data: response.data, headers: response.headers }; } catch (error) { throw new Error(`API request failed: ${error.message}`); } } // PIX Charge Methods async createCharge(chargeData) { return this.request('POST', '/v2/cob', chargeData); } async createChargeWithTxid(txid, chargeData) { return this.request('PUT', `/v2/cob/${txid}`, chargeData); } async updateCharge(txid, chargeData) { return this.request('PATCH', `/v2/cob/${txid}`, chargeData); } async getCharge(txid, revision = null) { const endpoint = revision ? `/v2/cob/${txid}?revisao=${revision}` : `/v2/cob/${txid}`; return this.request('GET', endpoint); } async listCharges(params) { if (!params.inicio || !params.fim) { throw new Error('inicio and fim parameters are required'); } const queryParams = new URLSearchParams(params).toString(); return this.request('GET', `/v2/cob?${queryParams}`); } // PIX Charge with Due Date Methods async createDueDateCharge(txid, chargeData) { if (!chargeData.calendario?.dataDeVencimento) { throw new Error('dataDeVencimento is required in calendario object'); } return this.request('PUT', `/v2/cobv/${txid}`, chargeData); } async updateDueDateCharge(txid, chargeData) { return this.request('PATCH', `/v2/cobv/${txid}`, chargeData); } async getDueDateCharge(txid, revision = null) { const endpoint = revision ? `/v2/cobv/${txid}?revisao=${revision}` : `/v2/cobv/${txid}`; return this.request('GET', endpoint); } async listDueDateCharges(params) { if (!params.inicio || !params.fim) { throw new Error('inicio and fim parameters are required'); } const queryParams = new URLSearchParams(params).toString(); return this.request('GET', `/v2/cobv?${queryParams}`); } // PIX Send Methods async sendPix(idEnvio, pixData) { const response = await this.request('PUT', `/v3/gn/pix/${idEnvio}`, pixData); return { ...response.data, bucketSize: response.headers['bucket-size'], retryAfter: response.headers['retry-after'] }; } async getSentPixByE2eId(e2eId) { return this.request('GET', `/v2/gn/pix/enviados/${e2eId}`); } async getSentPixByIdEnvio(idEnvio) { return this.request('GET', `/v2/gn/pix/enviados/id-envio/${idEnvio}`); } async listSentPix(params) { if (!params.inicio || !params.fim) { throw new Error('inicio and fim parameters are required'); } const queryParams = new URLSearchParams(params).toString(); return this.request('GET', `/v2/gn/pix/enviados?${queryParams}`); } // QR Code Methods async getQrCodeDetails(pixCopiaECola) { return this.request('POST', '/v2/gn/qrcodes/detalhar', { pixCopiaECola }); } async payQrCode(idEnvio, paymentData) { return this.request('PUT', `/v2/gn/pix/${idEnvio}/qrcode`, paymentData); } // PIX Transaction Methods async getPixTransaction(e2eId, showBankCode = false) { const endpoint = showBankCode ? `/v2/pix/${e2eId}?exibirCodigoBanco=true` : `/v2/pix/${e2eId}`; return this.request('GET', endpoint); } async listPixTransactions(params) { if (!params.inicio || !params.fim) { throw new Error('inicio and fim parameters are required'); } const queryParams = new URLSearchParams(params).toString(); return this.request('GET', `/v2/pix?${queryParams}`); } async requestRefund(e2eId, refundId, refundData) { if (!refundData.valor) { throw new Error('valor is required for refund'); } return this.request('PUT', `/v2/pix/${e2eId}/devolucao/${refundId}`, refundData); } async getRefund(e2eId, refundId) { return this.request('GET', `/v2/pix/${e2eId}/devolucao/${refundId}`); } // QR Code Generation Methods async generateQRCode(chargeData) { const response = await this.request('POST', '/v2/loc', chargeData); return new QRCode({ content: response.data.qrcode, base64: response.data.qrcodeImage }); } async generateQRCodeWithDueDate(chargeData) { const response = await this.request('POST', '/v2/loc', { ...chargeData, calendario: { ...chargeData.calendario, dataDeVencimento: chargeData.calendario.dataDeVencimento, validadeAposVencimento: chargeData.calendario.validadeAposVencimento } }); return new QRCode({ content: response.data.qrcode, base64: response.data.qrcodeImage }); } async getQRCodeDetails(qrCode) { return this.request('GET', `/v2/loc/${qrCode.content}`); } // Webhook Methods async configureWebhook(chave, webhookUrl, skipMtls = false) { const headers = skipMtls ? { 'x-skip-mtls-checking': 'true' } : {}; return this.request('PUT', `/v2/webhook/${chave}`, { webhookUrl }, headers); } async getWebhookInfo(chave) { return this.request('GET', `/v2/webhook/${chave}`); } async listWebhooks(params) { if (!params.inicio || !params.fim) { throw new Error('inicio and fim parameters are required'); } const queryParams = new URLSearchParams(params).toString(); return this.request('GET', `/v2/webhook?${queryParams}`); } async deleteWebhook(chave) { return this.request('DELETE', `/v2/webhook/${chave}`); } async resendWebhook(type, e2eids) { return this.request('POST', '/v2/gn/webhook/reenviar', { tipo: type, e2eids: e2eids }); } // Automatic PIX Webhook Methods async configureAutoPixWebhook(webhookUrl) { return this.request('PUT', '/v2/webhookrec', { webhookUrl }); } async getAutoPixWebhookInfo() { return this.request('GET', '/v2/webhookrec'); } async deleteAutoPixWebhook() { return this.request('DELETE', '/v2/webhookrec'); } // Automatic PIX Charge Webhook Methods async configureAutoPixChargeWebhook(webhookUrl) { return this.request('PUT', '/v2/webhookcobr', { webhookUrl }); } async getAutoPixChargeWebhookInfo() { return this.request('GET', '/v2/webhookcobr'); } async deleteAutoPixChargeWebhook() { return this.request('DELETE', '/v2/webhookcobr'); } } module.exports = EfipayClient;