Node.js examples
Minimal implementation using only fetch (Node ≥ 18) and node:crypto. No external dependencies.
Reusable client
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 }
}
// Usage: create a charge
const charge = await infiRequest('/pix', {
method: 'POST',
body: {
amountCents: 1000,
externalRef: 'order-123',
customer: {
name: 'Maria Silva',
email: 'maria@example.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)
// Usage: initiate a withdrawal (mind 202 = failed_unknown).
// Currently only the registered account holder's CPF/CNPJ.
const withdraw = await infiRequest('/withdraw', {
method: 'POST',
body: {
amountCents: 5000,
pixKey: '12345678901',
pixKeyType: 'cpf',
},
})
if (withdraw.status === 202) {
// failed_unknown — DO NOT resend
console.warn('indeterminate withdrawal', withdraw.body.transactionId)
} else if (!withdraw.ok) {
throw new Error(withdraw.body.error)
}Receive 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'))
// Idempotency: treat (transactionId, status) as the key
// Enqueue processing and respond in < 8s.
res.status(200).end()
})
app.listen(3000)