Throne

Referência

Exemplos por linguagem

Cliente HTTP mínimo pronto para copiar em PHP, Node.js e Python, com timeout e repetição segura.

Ver em Markdown

Não existe pacote para instalar#

A API é REST com JSON: qualquer cliente HTTP serve. O Throne não publica pacote no npm, Packagist, PyPI ou outro repositório. Pacotes com nomes parecidos com "throne" não são nossos: não instale.

Abaixo há um cliente mínimo, pronto para copiar, em cada linguagem. Ele já:

  • monta a autenticação e manda Accept: application/json;
  • usa timeout (20 segundos por padrão; passe 60 segundos na criação de checkout de cartão);
  • aceita uma Idempotency-Key sua nos POST, para você repetir com a mesma chave depois de um timeout;
  • transforma erros em exceção com o código error, o status, o retryable e o Retry-After. Sem resposta (falha de conexão ou timeout), o status é 0.
final class ThroneError extends RuntimeException
{
    public function __construct(
        public readonly int $status,          // 0 = sem resposta (conexão caiu ou timeout)
        public readonly string $error,
        string $message,
        public readonly ?bool $retryable = null,
        public readonly ?int $retryAfter = null,
    ) {
        parent::__construct("{$error}: {$message}", $status);
    }
}

final class Throne
{
    public function __construct(
        private string $pk,
        private string $sk,
        private string $base = 'https://app.thronecorporate.com/api/v1',
    ) {}

    public function get(string $path, int $timeout = 20): array
    {
        return $this->request('GET', $path, null, null, $timeout);
    }

    public function post(string $path, array $body, ?string $idempotencyKey = null, int $timeout = 20): array
    {
        return $this->request('POST', $path, $body, $idempotencyKey ?? bin2hex(random_bytes(16)), $timeout);
    }

    private function request(string $method, string $path, ?array $body, ?string $key, int $timeout): array
    {
        $headers = [
            "Authorization: Bearer {$this->pk}:{$this->sk}",
            'Accept: application/json',
            'Content-Type: application/json',
        ];
        if ($key !== null) {
            $headers[] = "Idempotency-Key: {$key}";
        }

        $retryAfter = null;
        $ch = curl_init($this->base . $path);
        curl_setopt_array($ch, [
            CURLOPT_CUSTOMREQUEST => $method,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_TIMEOUT => $timeout,
            CURLOPT_HEADERFUNCTION => function ($ch, string $line) use (&$retryAfter): int {
                if (stripos($line, 'Retry-After:') === 0) {
                    $retryAfter = (int) trim(substr($line, 12));
                }
                return strlen($line);
            },
        ]);
        if ($body !== null) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
        }

        $raw = curl_exec($ch);
        if ($raw === false) {
            // Sem resposta: repita com a MESMA Idempotency-Key.
            throw new ThroneError(0, 'network', curl_error($ch));
        }
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $json = json_decode((string) $raw, true) ?? [];

        if ($status >= 400) {
            throw new ThroneError(
                $status,
                $json['error'] ?? 'http_' . $status,
                $json['message'] ?? '',
                $json['retryable'] ?? null,
                $retryAfter,
            );
        }

        return $json;
    }
}

$throne = new Throne(getenv('THRONE_PK'), getenv('THRONE_SK'));

// Pix
$pix = $throne->post('/transactions', [
    'payment_method' => 'pix',
    'amount_cents' => 4990,
    'customer' => ['name' => 'Maria Souza', 'document' => '12345678909', 'email' => 'maria@exemplo.com'],
]);

// Cartão: guarde a chave junto do pedido antes de chamar; 60 segundos de timeout.
$chave = bin2hex(random_bytes(16));
$checkout = $throne->post('/elements/checkout', ['amount_cents' => 4990, 'currency' => 'usd'], $chave, 60);

Repetir com segurança#

Com os clientes acima, a decisão fica assim:

A exceção traz O que fazer
status 0 (sem resposta) Repita com a mesma Idempotency-Key e o mesmo corpo. Se a primeira chegou, você recebe a resposta original, sem duplicar nada
502, 503 ou 504 com código começando em http_ Página de erro do servidor, sem o JSON da API. Trate como sem resposta: repita com a mesma chave
409 idempotency_key_processing Espere alguns segundos e repita com a mesma chave. Se continuar por mais de 2 minutos, confira no painel se algo foi criado antes de usar uma chave nova
429, ou 503 com o código da API Espere o retryAfter e repita com uma chave nova, poucas vezes, aumentando a espera (recuo)
502 com o código da API e retryable verdadeiro ou ausente (o 502 do Pix não traz retryable) Repita com uma chave nova
502 com retryable falso Não repita igual
500 Algo pode ter sido criado. Não use chave nova antes de conferir no painel. Em POST /elements/checkout, repita antes com a mesma chave: se nada foi criado, a chamada roda de novo
Qualquer outro 4xx Corrija o que for preciso e repita com uma chave nova

Por isso os clientes aceitam uma Idempotency-Key sua: gere a chave antes, guarde junto do pedido e reuse na repetição. A tabela completa está em como repetir com segurança.

Validar webhooks#

Os exemplos de validação da assinatura X-Throne-Signature em PHP, Node.js, Python e Go estão em Webhooks.