PHP examples

Using Guzzle as the HTTP client. hash_hmac and hash_equals are from the standard library.

Reusable client

<?php
use GuzzleHttp\Client;
 
const BASE_URL = 'https://api.internationalfinance.com.br/v1';
 
function infiRequest(string $path, string $method = 'GET', ?array $body = null, ?array $query = null): array {
  $client = new Client(['http_errors' => false]);
  $headers = ['Authorization' => 'Bearer ' . getenv('INFI_API_KEY')];
  if ($body !== null) $headers['Content-Type'] = 'application/json';
 
  $res = $client->request($method, BASE_URL . $path, [
    'headers' => $headers,
    'json'    => $body,
    'query'   => $query,
  ]);
  $status = $res->getStatusCode();
  $data   = json_decode((string) $res->getBody(), true) ?? [];
  return ['status' => $status, 'body' => $data];
}
 
// Create charge
$res = infiRequest('/pix', 'POST', [
  'amountCents'   => 1000,
  'externalRef'   => 'order-123',
  'customer' => [
    'name'  => 'Maria Silva',
    'email' => 'maria@example.com',
    'phone' => '11999998888',
    'document' => ['number' => '12345678901', 'type' => 'cpf'],
  ],
]);
if ($res['status'] >= 400) {
  throw new RuntimeException($res['body']['error'] ?? 'INFI error');
}
 
// Initiate withdrawal — mind 202.
// Currently only the registered account holder's CPF/CNPJ.
$res = infiRequest('/withdraw', 'POST', [
  'amountCents' => 5000,
  'pixKey'      => '12345678901',
  'pixKeyType'  => 'cpf',
]);
if ($res['status'] === 202) {
  // failed_unknown — do not resend
} elseif ($res['status'] >= 400) {
  throw new RuntimeException($res['body']['error']);
}

Receive webhook

<?php
$ts        = $_SERVER['HTTP_X_INFI_TIMESTAMP']  ?? '';
$sigHeader = $_SERVER['HTTP_X_INFI_SIGNATURE'] ?? '';
$sig       = str_starts_with($sigHeader, 'sha256=') ? substr($sigHeader, 7) : '';
 
if (!ctype_digit($ts) || abs(time() - (int)$ts) > 300) {
  http_response_code(401);
  exit;
}
 
$raw      = file_get_contents('php://input');
$payload  = $ts . '.' . $raw;
$expected = hash_hmac('sha256', $payload, getenv('INFI_WEBHOOK_SECRET'));
 
if (!hash_equals($expected, $sig)) {
  http_response_code(401);
  exit;
}
 
$event = json_decode($raw, true);
// Idempotency by (transactionId, status). Respond < 8s.
http_response_code(200);