{
  "openapi": "3.1.0",
  "info": {
    "title": "Jungle Pagamentos — API do Seller",
    "version": "1.0.0",
    "summary": "Cobranças PIX, checkout hospedado, transações e saques via X-Api-Key.",
    "description": "Superfície pública consumida por integradores. Todos os valores monetários são em REAIS (número decimal). Envelope: { success: true, data } ou { success: false, error }. Documentação completa: https://jungle-docs.pages.dev",
    "contact": { "name": "Jungle Pagamentos", "url": "https://jungle-docs.pages.dev" }
  },
  "servers": [{ "url": "https://app.junglepagamentos.com/api", "description": "Produção" }],
  "security": [{ "apiKey": [] }],
  "tags": [
    { "name": "Cobranças", "description": "PIX gerado pela sua API" },
    { "name": "Checkout hospedado", "description": "Sessões na página de checkout montada no painel" },
    { "name": "Saques", "description": "PIX OUT programático (assinatura obrigatória)" }
  ],
  "components": {
    "securitySchemes": {
      "apiKey": { "type": "apiKey", "in": "header", "name": "X-Api-Key", "description": "Gerada pelo seller no painel (Conta → API), uma por dashboard." }
    },
    "parameters": {
      "XSignature": { "name": "X-Signature", "in": "header", "required": false, "schema": { "type": "string" }, "description": "HMAC-SHA256 hex do canônico METHOD\\npath\\nbody\\ntimestamp\\nnonce, chave = API Key. Obrigatório só se o workspace exigir assinatura." },
      "XTimestamp": { "name": "X-Timestamp", "in": "header", "required": false, "schema": { "type": "string" }, "description": "Epoch em milissegundos (janela ±5 min)." },
      "XNonce": { "name": "X-Nonce", "in": "header", "required": false, "schema": { "type": "string" }, "description": "Valor único por requisição (UUID)." }
    },
    "schemas": {
      "Error": {
        "type": "object", "required": ["success", "error"],
        "properties": { "success": { "const": false }, "error": { "type": "string" } }
      },
      "Customer": {
        "type": "object", "required": ["name", "email", "doc"],
        "properties": {
          "name": { "type": "string", "minLength": 1 },
          "email": { "type": "string", "format": "email" },
          "doc": { "type": "string", "minLength": 11, "description": "CPF ou CNPJ" },
          "docType": { "type": "string", "enum": ["cpf", "cnpj"], "default": "cpf" },
          "phone": { "type": "string" },
          "zip": { "type": "string" },
          "street": { "type": "string" },
          "number": { "type": "string" },
          "district": { "type": "string" },
          "city": { "type": "string" },
          "state": { "type": "string" },
          "ip": { "type": "string", "description": "IP do comprador (nunca o do seu servidor)" }
        }
      },
      "TransactionStatus": { "type": "string", "enum": ["PENDING", "PAID", "FAILED", "EXPIRED", "MED"] },
      "WithdrawalStatus": { "type": "string", "enum": ["PENDING", "ENVIADO", "RECUSADO", "CANCELADO", "BLOQUEADO"] },
      "Transaction": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "acquirerTxId": { "type": ["string", "null"] },
          "endToEndId": { "type": ["string", "null"] },
          "status": { "$ref": "#/components/schemas/TransactionStatus" },
          "productName": { "type": "string" },
          "productValue": { "type": "number" },
          "amount": { "type": "number" },
          "feeRate": { "type": "number" },
          "feeAmount": { "type": "number" },
          "netAmount": { "type": "number" },
          "pixCode": { "type": ["string", "null"], "description": "null em boleto" },
          "pixExpiresAt": { "type": ["string", "null"], "format": "date-time" },
          "paidAt": { "type": ["string", "null"], "format": "date-time" },
          "customer": { "type": "object" },
          "metadata": { "type": ["object", "null"] },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "Withdrawal": {
        "type": "object",
        "properties": {
          "withdrawalId": { "type": "string" },
          "status": { "$ref": "#/components/schemas/WithdrawalStatus" },
          "amount": { "type": "number", "description": "O que cai na chave PIX" },
          "feeAmount": { "type": "number" },
          "netAmount": { "type": "number" },
          "totalAmount": { "type": "number", "description": "amount + feeAmount (debitado do saldo)" },
          "pixKeyType": { "type": "string" },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      },
      "SessionProduct": {
        "type": "object", "required": ["name", "value"],
        "properties": {
          "name": { "type": "string", "minLength": 1 },
          "value": { "type": "number", "exclusiveMinimum": 0, "description": "Preço unitário em reais" },
          "quantity": { "type": "integer", "minimum": 1, "default": 1 },
          "image": { "type": "string", "description": "URL http(s) da imagem" },
          "variant": { "type": "string" }
        }
      }
    }
  },
  "paths": {
    "/gateway/charges": {
      "post": {
        "tags": ["Cobranças"], "operationId": "createCharge", "summary": "Criar cobrança PIX",
        "parameters": [{ "$ref": "#/components/parameters/XSignature" }, { "$ref": "#/components/parameters/XTimestamp" }, { "$ref": "#/components/parameters/XNonce" }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object", "required": ["product", "customer"],
            "properties": {
              "product": { "type": "object", "required": ["name", "value"], "properties": { "name": { "type": "string", "minLength": 1 }, "value": { "type": "number", "exclusiveMinimum": 0 } } },
              "customer": { "$ref": "#/components/schemas/Customer" },
              "expiracaoSegundos": { "type": "number", "exclusiveMinimum": 0, "default": 3600 },
              "callbackUrl": { "type": "string", "format": "uri" },
              "metadata": { "type": "object", "additionalProperties": true, "description": "Livre; ecoado nos webhooks. Aceita identificadores de clique (gclid, fbclid, fbp, fbc, ttclid, ttp, gaClientId, clickAt, userAgent, sourceUrl)." }
            }
          },
          "example": { "product": { "name": "Plano Pro", "value": 149.90 }, "customer": { "name": "João Silva", "email": "joao@exemplo.com", "doc": "11122233344", "docType": "cpf" }, "callbackUrl": "https://seusite.com/webhooks/jungle", "metadata": { "pedido_id": "A-1001" } } } }
        },
        "responses": {
          "201": { "description": "Cobrança criada", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "const": true }, "data": { "type": "object", "properties": { "transactionId": { "type": "string" }, "pixCode": { "type": "string" }, "expiresAt": { "type": "string", "format": "date-time" }, "amount": { "type": "number" }, "feeAmount": { "type": "number" }, "netAmount": { "type": "number" } } } } } } } },
          "400": { "description": "Body inválido", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "X-Api-Key ausente/inválida ou assinatura inválida", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "Workspace ou conta inativa", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/gateway/transactions": {
      "get": {
        "tags": ["Cobranças"], "operationId": "listTransactions", "summary": "Listar transações do workspace",
        "parameters": [
          { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1, "minimum": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 20, "maximum": 100 } },
          { "$ref": "#/components/parameters/XSignature" }, { "$ref": "#/components/parameters/XTimestamp" }, { "$ref": "#/components/parameters/XNonce" }
        ],
        "responses": {
          "200": { "description": "Lista paginada", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "const": true }, "data": { "type": "array", "items": { "$ref": "#/components/schemas/Transaction" } }, "meta": { "type": "object", "properties": { "total": { "type": "integer" }, "page": { "type": "integer" }, "limit": { "type": "integer" }, "pages": { "type": "integer" } } } } } } } },
          "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/checkout/sessions": {
      "post": {
        "tags": ["Checkout hospedado"], "operationId": "createCheckoutSession", "summary": "Criar sessão de checkout hospedado",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object", "required": ["checkoutId", "products"],
            "properties": {
              "checkoutId": { "type": "string", "minLength": 1 },
              "products": { "type": "array", "minItems": 1, "items": { "$ref": "#/components/schemas/SessionProduct" } },
              "customer": { "type": "object", "properties": { "name": { "type": "string" }, "email": { "type": "string", "format": "email" }, "phone": { "type": "string" }, "docType": { "type": "string", "enum": ["cpf", "cnpj"] }, "doc": { "type": "string" } } },
              "externalRef": { "type": "string", "maxLength": 128, "pattern": "^[A-Za-z0-9._:-]+$", "description": "Referência do seu pedido. Fora do formato é ignorada (a sessão é criada igual)." }
            }
          } } }
        },
        "responses": {
          "201": { "description": "Sessão criada", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "const": true }, "data": { "type": "object", "properties": { "sessionId": { "type": "string" }, "amount": { "type": "number" }, "checkoutUrl": { "type": "string", "format": "uri", "description": "Use exatamente como veio" } } } } } } } },
          "400": { "description": "Body inválido", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "Checkout não encontrado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/checkout/sessions/{sessionId}": {
      "get": {
        "tags": ["Checkout hospedado"], "operationId": "getCheckoutSession", "summary": "Consultar sessão (público)", "security": [],
        "parameters": [{ "name": "sessionId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Sessão como a página do comprador a vê. `expired: true` = link vencido." },
          "404": { "description": "Sessão não encontrada", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/gateway/withdrawals": {
      "post": {
        "tags": ["Saques"], "operationId": "createWithdrawal", "summary": "Sacar via PIX",
        "description": "Assinatura OBRIGATÓRIA com o Webhook Secret: canônico METHOD\\npath\\nbody\\ntimestamp\\nnonce\\nidempotencyKey. Nasce sempre PENDING.",
        "parameters": [
          { "name": "Idempotency-Key", "in": "header", "required": true, "schema": { "type": "string" } },
          { "name": "X-Signature", "in": "header", "required": true, "schema": { "type": "string" } },
          { "name": "X-Timestamp", "in": "header", "required": true, "schema": { "type": "string" } },
          { "name": "X-Nonce", "in": "header", "required": true, "schema": { "type": "string" } }
        ],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object", "required": ["amount"],
            "properties": {
              "amount": { "type": "number", "exclusiveMinimum": 0 },
              "callbackUrl": { "type": "string", "format": "uri" },
              "pixKey": { "type": "string" },
              "pixKeyType": { "type": "string", "enum": ["cpf", "cnpj", "email", "phone", "random"], "description": "Aliases aceitos: evp, aleatoria, chave_aleatoria → random; telefone, celular → phone; e-mail → email" }
            }
          } } }
        },
        "responses": {
          "201": { "description": "Saque aceito (PENDING)", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "const": true }, "data": { "$ref": "#/components/schemas/Withdrawal" } } } } } },
          "200": { "description": "Replay idempotente — mesmo saque, sem novo débito" },
          "400": { "description": "Idempotency-Key ausente, body inválido, saldo insuficiente ou abaixo do mínimo", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Assinatura ausente/inválida", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "IP fora da allowlist ou saques bloqueados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/gateway/withdrawals/{id}": {
      "get": {
        "tags": ["Saques"], "operationId": "getWithdrawal", "summary": "Consultar saque",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Saque", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "const": true }, "data": { "$ref": "#/components/schemas/Withdrawal" } } } } } },
          "404": { "description": "Withdrawal not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    }
  },
  "webhooks": {
    "transaction": {
      "post": {
        "summary": "transaction.created | transaction.paid | transaction.failed | transaction.expired",
        "description": "Enviado ao callbackUrl da cobrança. Header X-Signature = hex(HMAC-SHA256(Webhook Secret, corpo bruto)).",
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "event": { "type": "string", "enum": ["transaction.created", "transaction.paid", "transaction.failed", "transaction.expired"] }, "data": { "type": "object" } } } } } },
        "responses": { "200": { "description": "Recebido" } }
      }
    },
    "withdrawal": {
      "post": {
        "summary": "withdrawal.created | withdrawal.paid | withdrawal.failed",
        "description": "Enviado ao callbackUrl do saque, assinado com o Webhook Secret.",
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "event": { "type": "string", "enum": ["withdrawal.created", "withdrawal.paid", "withdrawal.failed"] }, "data": { "type": "object" } } } } } },
        "responses": { "200": { "description": "Recebido" } }
      }
    }
  }
}
