Python examples

Using requests (client) and Flask (webhook). hmac and hashlib are from the standard library.

Reusable client

import os, requests
 
BASE_URL = "https://api.internationalfinance.com.br/v1"
 
class INFIError(Exception):
    def __init__(self, status, message):
        super().__init__(message)
        self.status = status
 
def infi_request(path, method="GET", body=None, params=None):
    headers = {"Authorization": f"Bearer {os.environ['INFI_API_KEY']}"}
    if body is not None:
        headers["Content-Type"] = "application/json"
    res = requests.request(method, f"{BASE_URL}{path}", headers=headers, json=body, params=params)
    data = res.json() if res.headers.get("content-type", "").startswith("application/json") else {}
    return res.status_code, data
 
# Create charge
status, data = infi_request("/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 status >= 400:
    raise INFIError(status, data.get("error"))
print(data["transactionId"], data["pixPayload"])
 
# Initiate withdrawal (mind 202).
# Currently only the registered account holder's CPF/CNPJ.
status, data = infi_request("/withdraw", method="POST", body={
    "amountCents": 5000,
    "pixKey": "12345678901",
    "pixKeyType": "cpf",
})
if status == 202:
    # failed_unknown — do not resend
    pass
elif status >= 400:
    raise INFIError(status, data.get("error"))

Receive webhook (Flask)

import hmac, hashlib, os, time
from flask import Flask, request, abort
 
app = Flask(__name__)
 
@app.post("/webhooks/infi")
def infi_webhook():
    ts = request.headers.get("X-Infi-Timestamp", "")
    sig_header = request.headers.get("X-Infi-Signature", "")
    sig = sig_header[7:] if sig_header.startswith("sha256=") else ""
 
    try:
        ts_num = int(ts)
    except ValueError:
        abort(401)
    if abs(time.time() - ts_num) > 300:
        abort(401)
 
    raw = request.get_data()
    payload = f"{ts}.".encode() + raw
    expected = hmac.new(
        os.environ["INFI_WEBHOOK_SECRET"].encode(),
        payload,
        hashlib.sha256,
    ).hexdigest()
 
    if not hmac.compare_digest(expected, sig):
        abort(401)
 
    event = request.get_json()
    # Idempotency by (transactionId, status). Respond < 8s.
    return "", 200