{
  "openapi": "3.0.3",
  "info": {
    "title": "PixUSDT API",
    "version": "1.0.0",
    "description": "API REST para gerar cobranças PIX e receber USDT na sua carteira automaticamente. Auth via Bearer token, webhooks assinados via HMAC-SHA256.",
    "contact": {
      "name": "PixUSDT Suporte",
      "url": "https://pixusdt.org/support",
      "email": "suporte@pixusdt.org"
    }
  },
  "servers": [
    { "url": "https://pixusdt.org/v1", "description": "Produção" }
  ],
  "security": [
    { "BearerAuth": [] }
  ],
  "paths": {
    "/account": {
      "get": {
        "summary": "Info da conta",
        "description": "Retorna informações do dono da API key. Útil pra validar a chave durante setup.",
        "tags": ["Conta"],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Account" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/balance": {
      "get": {
        "summary": "Saldo bruto e líquido",
        "tags": ["Conta"],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Balance" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/charges": {
      "post": {
        "summary": "Cria cobrança PIX",
        "description": "Gera um QR code PIX. Aceita webhook_url inline pra receber callback assinado quando for pago/expirar.",
        "tags": ["Cobranças"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateChargeRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Cobrança criada",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Charge" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimit" },
          "502": { "$ref": "#/components/responses/PspError" }
        }
      },
      "get": {
        "summary": "Lista cobranças",
        "tags": ["Cobranças"],
        "parameters": [
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["pending", "paid", "expired", "failed", "refunded"] } },
          { "name": "limit",  "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 200 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "default": 0 } }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data":   { "type": "array", "items": { "$ref": "#/components/schemas/ChargeListItem" } },
                    "total":  { "type": "integer" },
                    "limit":  { "type": "integer" },
                    "offset": { "type": "integer" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/transactions": {
      "get": {
        "summary": "Lista conversões USDT",
        "description": "Histórico de envios USDT confirmados, com TX hash on-chain.",
        "tags": ["Conversões USDT"],
        "parameters": [
          { "name": "status",      "in": "query", "schema": { "type": "string", "enum": ["sent", "confirmed", "pending"] } },
          { "name": "charge_id",   "in": "query", "schema": { "type": "string" } },
          { "name": "external_id", "in": "query", "schema": { "type": "string" }, "description": "Filtra pelo external_id da charge" },
          { "name": "network",     "in": "query", "schema": { "type": "string", "enum": ["TRON", "POLYGON", "BSC"] } },
          { "name": "limit",       "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 200 } },
          { "name": "offset",      "in": "query", "schema": { "type": "integer", "default": 0 } }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data":   { "type": "array", "items": { "$ref": "#/components/schemas/Transaction" } },
                    "total":  { "type": "integer" },
                    "limit":  { "type": "integer" },
                    "offset": { "type": "integer" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/transactions/{id}": {
      "get": {
        "summary": "Consulta uma conversão USDT",
        "tags": ["Conversões USDT"],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Transaction" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/charges/{id}": {
      "get": {
        "summary": "Consulta cobrança por id ou external_id",
        "tags": ["Cobranças"],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "ID retornado pelo PixUSDT OU external_id que você passou na criação." }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Charge" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    },
    "schemas": {
      "Account": {
        "type": "object",
        "properties": {
          "id":    { "type": "integer", "example": 1 },
          "name":  { "type": "string",  "example": "Loja Exemplo" },
          "email": { "type": "string",  "format": "email" },
          "role":  { "type": "string",  "example": "admin" },
          "usdt_wallet": {
            "type": "object",
            "description": "Carteira USDT cadastrada (não-custodial — operador envia USDT direto pra cá após cada PIX pago)",
            "properties": {
              "address":    { "type": "string",  "nullable": true, "example": "TXyz...AbCd" },
              "network":    { "type": "string",  "nullable": true, "enum": ["TRON", "POLYGON", "BSC"], "example": "TRON" },
              "configured": { "type": "boolean", "description": "true quando address está preenchido" }
            }
          }
        }
      },
      "Balance": {
        "type": "object",
        "properties": {
          "gross_received_brl": { "type": "number", "example": 12483.50 },
          "pending_brl":        { "type": "number", "example": 347.00 },
          "total_fees_brl":     { "type": "number", "example": 374.50 },
          "net_brl":            { "type": "number", "example": 12109.00 }
        }
      },
      "CreateChargeRequest": {
        "type": "object",
        "required": ["amount"],
        "properties": {
          "amount":             { "type": "number", "minimum": 1, "maximum": 100000, "example": 49.90, "description": "Valor em reais (BRL). Limite absoluto da API é R$ 100.000, mas existe limite per-conta (max_charge_brl, default R$ 200). Ultrapassar retorna 400 amount_above_limit." },
          "expires_in_minutes": { "type": "integer", "minimum": 5, "maximum": 1440, "default": 30, "description": "Tempo até o QR expirar. Default 30. Valores abaixo de 30 são automaticamente elevados pra 30 (piso silencioso) pra evitar pix.expired prematuro antes do pix.received quando o PSP confirma o pagamento atrasado." },
          "external_id":        { "type": "string", "maxLength": 120, "example": "pedido-12345", "description": "ID do seu sistema (idempotência)" },
          "description":        { "type": "string", "maxLength": 255, "example": "Plano Premium mensal" },
          "webhook_url":        { "type": "string", "format": "uri", "maxLength": 500, "example": "https://seu-sistema.com/webhook/pixusdt", "description": "URL de callback assinada com HMAC-SHA256. Recebe pix.received e pix.expired." },
          "customer": {
            "type": "object",
            "properties": {
              "name":     { "type": "string", "example": "João Silva" },
              "email":    { "type": "string", "format": "email" },
              "document": { "type": "string", "example": "12345678901" }
            }
          }
        }
      },
      "Charge": {
        "type": "object",
        "properties": {
          "id":           { "type": "string", "example": "1842" },
          "external_id":  { "type": "string", "nullable": true, "example": "pedido-12345" },
          "status":       { "type": "string", "enum": ["pending", "paid", "expired", "failed", "refunded"] },
          "amount":       { "type": "number", "example": 49.90 },
          "amount_cents": { "type": "integer", "example": 4990 },
          "fees": {
            "type": "object",
            "properties": {
              "platform_fee_pct":   { "type": "number", "example": 3.0 },
              "percentage_fee_brl": { "type": "number", "example": 1.50 },
              "fixed_fee_brl":      { "type": "number", "example": 0.55, "description": "Taxa fixa aplicada em todas as cobranças, independente do valor" },
              "total_fee_brl":      { "type": "number", "example": 2.05 },
              "net_brl":            { "type": "number", "example": 47.85 }
            }
          },
          "pix": {
            "type": "object",
            "properties": {
              "qr_code":        { "type": "string", "description": "Código PIX copia-e-cola" },
              "qr_code_base64":     { "type": "string", "nullable": true, "description": "String PRONTA pra <img src=>. Pode ser data URI (data:image/png;base64,...) ou URL externa. NÃO concatene 'data:image/png;base64,' antes desse valor — vai duplicar o prefixo. Pra montar o data URI manualmente, use qr_code_base64_raw." },
              "qr_code_image_url":  { "type": "string", "nullable": true, "description": "Alias de qr_code_base64 (mesmo valor, nome mais explícito). Use direto em <img src=>." },
              "qr_code_base64_raw": { "type": "string", "nullable": true, "description": "Base64 PURO da imagem PNG, sem o prefixo data:image/png;base64,. Null quando o PSP devolve URL ao invés de base64. Use SÓ se for montar o data URI manualmente." },
              "copy_paste":     { "type": "string", "description": "Mesmo conteúdo de qr_code (alias)" }
            }
          },
          "payer": {
            "type": "object",
            "nullable": true,
            "properties": {
              "name":     { "type": "string" },
              "document": { "type": "string" }
            }
          },
          "expires_at":  { "type": "string", "format": "date-time" },
          "paid_at":     { "type": "string", "format": "date-time", "nullable": true },
          "created_at":  { "type": "string", "format": "date-time" },
          "psp_id":      { "type": "string", "nullable": true },
          "webhook_url": { "type": "string", "nullable": true }
        }
      },
      "ChargeListItem": {
        "type": "object",
        "properties": {
          "id":          { "type": "string" },
          "external_id": { "type": "string", "nullable": true },
          "status":      { "type": "string" },
          "amount":      { "type": "number" },
          "created_at":  { "type": "string", "format": "date-time" },
          "paid_at":     { "type": "string", "format": "date-time", "nullable": true },
          "expires_at":  { "type": "string", "format": "date-time" }
        }
      },
      "Transaction": {
        "type": "object",
        "description": "Conversão BRL→USDT realizada. Cada PIX paid gera uma transaction quando o operador envia o USDT on-chain.",
        "properties": {
          "id":              { "type": "string" },
          "charge_id":       { "type": "string", "nullable": true },
          "external_id":     { "type": "string", "nullable": true, "description": "external_id da charge associada" },
          "psp_id":          { "type": "string", "nullable": true },
          "amount_brl":      { "type": "number", "description": "Valor PIX original em reais" },
          "usdt_amount":     { "type": "number", "description": "Quantidade de USDT enviada" },
          "rate_brl":        { "type": "number", "description": "Cotação BRL/USDT no momento" },
          "spread_pct":      { "type": "number" },
          "network":         { "type": "string", "enum": ["TRON", "POLYGON", "BSC"] },
          "to_address":      { "type": "string", "description": "Carteira de destino do lojista" },
          "tx_hash":         { "type": "string", "nullable": true },
          "explorer_url":    { "type": "string", "nullable": true, "description": "Link direto pro block explorer da rede" },
          "network_fee_usd": { "type": "number" },
          "status":          { "type": "string", "enum": ["pending", "sent", "confirmed", "failed"] },
          "sent_at":         { "type": "string", "format": "date-time", "nullable": true },
          "confirmed_at":    { "type": "string", "format": "date-time", "nullable": true },
          "created_at":      { "type": "string", "format": "date-time" }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "description": "Payload enviado para o webhook_url. Validação anti-replay: cliente DEVE verificar (1) X-Webhook-Timestamp dentro de janela ±5min e (2) X-Webhook-Signature == HMAC-SHA256 de '{timestamp}.{body_bruto}' usando webhook_secret. Comparação deve ser em tempo constante.",
        "properties": {
          "event":     { "type": "string", "enum": ["pix.received", "pix.expired", "usdt.sent", "usdt.confirmed"] },
          "timestamp": { "type": "string", "format": "date-time" },
          "data": {
            "type": "object",
            "properties": {
              "charge": {
                "type": "object",
                "properties": {
                  "id":             { "type": "integer" },
                  "external_id":    { "type": "string", "nullable": true },
                  "amount_brl":     { "type": "number" },
                  "status":         { "type": "string" },
                  "paid_at":        { "type": "string", "nullable": true },
                  "created_at":     { "type": "string" },
                  "psp_id":         { "type": "string", "nullable": true },
                  "payer_name":     { "type": "string", "nullable": true },
                  "payer_document": { "type": "string", "nullable": true }
                }
              },
              "transaction": {
                "type": "object",
                "nullable": true,
                "description": "Presente em eventos usdt.sent e usdt.confirmed",
                "properties": {
                  "id":            { "type": "integer" },
                  "usdt_amount":   { "type": "number" },
                  "rate_brl":      { "type": "number" },
                  "network":       { "type": "string", "enum": ["TRON", "POLYGON", "BSC"] },
                  "to_address":    { "type": "string" },
                  "tx_hash":       { "type": "string" },
                  "network_fee":   { "type": "number" },
                  "explorer_url":  { "type": "string" },
                  "batch":         { "type": "boolean", "description": "true se foi parte de envio em lote" },
                  "batch_size":    { "type": "integer", "nullable": true },
                  "status":        { "type": "string" }
                }
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error":   { "type": "string", "example": "validation_error" },
          "message": { "type": "string", "example": "Dados inválidos" },
          "details": { "type": "object" }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "API key ausente, inválida ou revogada",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "ValidationError": {
        "description": "Body inválido ou campo faltando",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "Recurso não encontrado",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "RateLimit": {
        "description": "Limite de requisições excedido (50.000/24h global, 100/min por IP)",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "PspError": {
        "description": "Erro no PSP que processa o PIX",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  },
  "tags": [
    { "name": "Conta",          "description": "Informações da conta autenticada" },
    { "name": "Cobranças",      "description": "Criar e consultar cobranças PIX" },
    { "name": "Conversões USDT","description": "Histórico de conversões BRL→USDT enviadas on-chain" }
  ]
}
