Ejemplo en PHP

Ejemplo de uso del API SAT en php.

Archivo fuente: php.php

<?php
/**
 * Ejemplo PHP — POST /api/v1/usuario/datos-fiscales
 * Usa cURL (incluido en PHP).
 *
 * Uso:
 *   export SAT_API_KEY="sat_xxxxxxxxxxxx"
 *   php php.php [RFC]
 */

declare(strict_types=1);

class SatApiClient
{
    public function __construct(
        private string $apiKey,
        private string $baseUrl = &#039;https://api.cfdi4.com&#039;,
        private string $userAgent = &#039;php-sat-api/1.0&#039;,
        private int $timeout = 30,
    ) {}

    /**
     * POST genérico. Devuelve el body parseado o array de error.
     */
    public function post(string $endpoint, array $body): array
    {
        $url = rtrim($this->baseUrl, &#039;/&#039;) . $endpoint;
        $ch = curl_init($url);

        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => json_encode($body, JSON_UNESCAPED_UNICODE),
            CURLOPT_TIMEOUT        => $this->timeout,
            CURLOPT_HTTPHEADER     => [
                &#039;Authorization: Bearer &#039; . $this->apiKey,
                &#039;User-Agent: &#039; . $this->userAgent,
                &#039;Content-Type: application/json&#039;,
            ],
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $err = curl_error($ch);
        curl_close($ch);

        if ($response === false) {
            return [&#039;cfdi_codigo&#039; => 5001, &#039;cfdi_texto&#039; => $err ?: &#039;cURL error&#039;];
        }

        $data = json_decode((string)$response, true);
        if (!is_array($data)) {
            return [&#039;cfdi_codigo&#039; => 3003, &#039;cfdi_texto&#039; => &#039;Respuesta no parseable&#039;];
        }

        return $data[&#039;body&#039;] ?? [&#039;cfdi_codigo&#039; => 4001, &#039;cfdi_texto&#039; => &#039;Sin body&#039;];
    }

    public function datosFiscales(string $rfc): array
    {
        return $this->post(&#039;/api/v1/usuario/datos-fiscales&#039;, [
            &#039;rfc&#039; => strtoupper(trim($rfc)),
        ]);
    }

    public function opinionCumplimiento(string $rfc): array
    {
        return $this->post(&#039;/api/v1/sat/opinion-cumplimiento&#039;, [
            &#039;rfc&#039; => strtoupper(trim($rfc)),
        ]);
    }

    public function csf(string $rfc): array
    {
        return $this->post(&#039;/api/v1/sat/constancia-situacion-fiscal&#039;, [
            &#039;rfc&#039; => strtoupper(trim($rfc)),
        ]);
    }
}

// === MAIN ===
$apiKey = getenv(&#039;SAT_API_KEY&#039;);
if (!$apiKey) {
    fwrite(STDERR, "ERROR: configura la variable de entorno SAT_API_KEY\n");
    fwrite(STDERR, "  export SAT_API_KEY=&#039;sat_xxxxxxxxxxxx&#039;\n");
    exit(1);
}

$rfc = $argv[1] ?? &#039;XAXX010101000&#039;;
$client = new SatApiClient($apiKey);
echo "Consultando datos fiscales de RFC: {$rfc}\n\n";

$result = $client->datosFiscales($rfc);

$codigo = $result[&#039;cfdi_codigo&#039;] ?? 0;
$texto  = $result[&#039;cfdi_texto&#039;] ?? &#039;&#039;;
$desc   = $result[&#039;cfdi_descripcion&#039;] ?? &#039;&#039;;
$saldo  = $result[&#039;saldo&#039;] ?? 0;
$data   = $result[&#039;data&#039;] ?? null;

printf("  Código:    %d\n", $codigo);
printf("  Texto:     %s\n", $texto);
printf("  Detalle:   %s\n", $desc);
printf("  Saldo:     %d créditos\n", $saldo);

if ($codigo === 1 && $data) {
    echo "\n  Datos fiscales:\n";
    echo &#039;  &#039; . json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
} elseif ($codigo === 1004) {
    echo "\n  ⚠️  Saldo insuficiente. Recarga en https://panel.cfdi4.com/saldos\n";
} elseif ($codigo === 1001) {
    echo "\n  ⚠️  API key inválida. Verifica la variable SAT_API_KEY\n";
}

exit($codigo === 1 ? 0 : 1);