Ejemplos en Node.js
Implementación minimalista usando solo fetch (Node ≥ 18) y node:crypto. Sin dependencias externas.
Cliente reutilizable
const BASE_URL = 'https://api.internationalfinance.com.br/v1'
async function infiRequest(path, { method = 'GET', body, query } = {}) {
const url = new URL(BASE_URL + path)
if (query) for (const [k, v] of Object.entries(query)) url.searchParams.set(k, v)
const headers = { Authorization: `Bearer ${process.env.INFI_API_KEY}` }
if (body !== undefined) headers['Content-Type'] = 'application/json'
const res = await fetch(url, {
method,
headers,
body: body !== undefined ? JSON.stringify(body) : undefined,
})
const json = await res.json().catch(() => ({}))
return { status: res.status, ok: res.ok, body: json }
}
// Uso: crear cobro
const charge = await infiRequest('/pix', {
method: 'POST',
body: {
amountCents: 1000,
externalRef: 'pedido-123',
customer: {
name: 'Maria Silva',
email: 'maria@ejemplo.com',
phone: '11999998888',
document: { number: '12345678901', type: 'cpf' },
},
},
})
if (!charge.ok) throw new Error(charge.body.error)
console.log(charge.body.transactionId, charge.body.pixPayload)
// Uso: iniciar retiro (cuidado con 202 = failed_unknown).
// Actualmente solo CPF/CNPJ del titular registrado.
const withdraw = await infiRequest('/withdraw', {
method: 'POST',
body: {
amountCents: 5000,
pixKey: '12345678901',
pixKeyType: 'cpf',
},
})
if (withdraw.status === 202) {
// failed_unknown — NO reenviar
console.warn('retiro indeterminado', withdraw.body.transactionId)
} else if (!withdraw.ok) {
throw new Error(withdraw.body.error)
}Recibir webhook (Express)
import express from 'express'
import crypto from 'node:crypto'
const app = express()
app.use('/webhooks/infi', express.raw({ type: 'application/json' }))
app.post('/webhooks/infi', (req, res) => {
const ts = req.header('X-Infi-Timestamp') || ''
const sigHeader = req.header('X-Infi-Signature') || ''
const sigHex = sigHeader.startsWith('sha256=') ? sigHeader.slice(7) : ''
const tsNum = Number(ts)
if (!Number.isFinite(tsNum) || Math.abs(Date.now() / 1000 - tsNum) > 300) {
return res.status(401).end()
}
const payload = `${ts}.${req.body.toString('utf8')}`
const expected = crypto
.createHmac('sha256', process.env.INFI_WEBHOOK_SECRET)
.update(payload)
.digest('hex')
if (
sigHex.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(sigHex), Buffer.from(expected))
) {
return res.status(401).end()
}
const event = JSON.parse(req.body.toString('utf8'))
// Idempotencia: trata (transactionId, status) como la clave
// Encola el procesamiento y responde en < 8s.
res.status(200).end()
})
app.listen(3000)