{
  "openapi": "3.0.3",
  "info": {
    "title": "API de Generación de Códigos de Compra — Firma Digital",
    "version": "1.0.0",
    "description": "Permite a un partner generar sus propios códigos de compra sin intervención manual, y consultar su estado. Cada código de compra nace siempre en blanco (sin flujo asignado): el comportamiento de canje (flujo completo o reducido) lo decide la configuración del partner al momento de usarlo, no la generación — ver la API de Partners.\n\nEsta es la v1: la versión vive en la URL (`/api/partner/v1`). Una eventual v2 conviviría como una sección aparte, sin romper esta."
  },
  "servers": [
    { "url": "https://partner-pcode-dev.tsp.workers.dev/api/partner/v1", "description": "Desarrollo" },
    { "url": "https://partner-pcode.tsp.workers.dev/api/partner/v1", "description": "Producción" }
  ],
  "components": {
    "securitySchemes": {
      "PartnerApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-gw",
        "description": "API Key del partner."
      }
    },
    "schemas": {
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": false },
          "error": { "type": "string" }
        }
      },
      "PCode": {
        "type": "object",
        "properties": {
          "pcode": { "type": "string", "example": "MIPARTNER-26-FV01-A1B2C3D4" },
          "partnerName": { "type": "string" },
          "category": { "type": "integer", "example": 0 },
          "status": {
            "type": "string",
            "enum": ["sin_utilizar", "en_onboarding", "emitido", "bloqueado", "expirado"],
            "description": "Derivado en cada lectura, no es una columna almacenada. expirado: se venció el plazo de 14 días para empezar a usarlo, sin relación con la vigencia del certificado ya emitido."
          },
          "processing": { "type": "integer" },
          "batch_id": { "type": "string", "nullable": true },
          "external_reference_id": { "type": "string", "nullable": true },
          "expires_at": { "type": "string", "format": "date-time", "nullable": true },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      }
    }
  },
  "security": [{ "PartnerApiKey": [] }],
  "paths": {
    "/pcode": {
      "post": {
        "summary": "Generar un código de compra",
        "description": "Si se envía `external_reference_id` y ya existe un código de compra vigente (sin usar, sin bloquear, dentro de su plazo de 14 días) para esa misma referencia, se devuelve ESE mismo código de compra (`reused: true`) en vez de crear uno nuevo — a menos que se pase `force_new: true`, que además anula cualquier código de compra anterior con esa referencia.",
        "operationId": "createPCode",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["productCode"],
                "properties": {
                  "productCode": { "type": "string", "enum": ["FV00", "FV01", "FV02", "FV03"], "description": "FV00=6 meses, FV01=1 año, FV02=2 años, FV03=3 años." },
                  "external_reference_id": { "type": "string", "description": "Identificador propio del partner, para idempotencia." },
                  "force_new": { "type": "boolean", "default": false }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Código de compra creado (o reutilizado — ver `data.reused`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "data": {
                      "type": "object",
                      "properties": {
                        "pCode": { "type": "string" },
                        "productCode": { "type": "string" },
                        "status": { "type": "string", "example": "ACTIVE" },
                        "reused": { "type": "boolean" },
                        "createdAt": { "type": "string", "format": "date-time" }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "productCode inválido/faltante, o el partner no tiene ese producto habilitado (`partner_config.allowed_products` — si esa columna es NULL o no existe fila de config, no hay restricción y cualquier productCode válido pasa).",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
          },
          "401": { "description": "API Key inválida." },
          "429": { "description": "Rate limit excedido (60 requests/60s por partner)." }
        }
      }
    },
    "/pcode/bulk": {
      "post": {
        "summary": "Generar códigos de compra en lote",
        "description": "Hasta 500 por request. Todos quedan asociados al mismo `batchId`, útil para revisarlos después como grupo con `GET /pcodes?batch_id=`.",
        "operationId": "createBulkPCodes",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["productCode", "quantity"],
                "properties": {
                  "productCode": { "type": "string", "enum": ["FV00", "FV01", "FV02", "FV03"] },
                  "quantity": { "type": "integer", "minimum": 1, "maximum": 500 }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Lote creado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "data": {
                      "type": "object",
                      "properties": {
                        "batchId": { "type": "string" },
                        "pcodes": { "type": "array", "items": { "type": "string" } }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": { "description": "productCode o quantity inválidos, o el partner no tiene ese producto habilitado (`partner_config.allowed_products`)." },
          "401": { "description": "API Key inválida." },
          "429": { "description": "Rate limit excedido." }
        }
      }
    },
    "/pcodes": {
      "get": {
        "summary": "Listar códigos de compra del partner",
        "operationId": "listPCodes",
        "parameters": [
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["sin_utilizar", "en_onboarding", "emitido", "bloqueado", "expirado"] } },
          { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 100 } },
          { "name": "created_from", "in": "query", "schema": { "type": "string" }, "description": "Fecha/hora UTC (`YYYY-MM-DD` o timestamp completo)." },
          { "name": "created_to", "in": "query", "schema": { "type": "string" }, "description": "Fecha/hora UTC. Una fecha pelada incluye el día completo." },
          { "name": "external_reference_id", "in": "query", "schema": { "type": "string" } },
          { "name": "batch_id", "in": "query", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "data": {
                      "type": "object",
                      "properties": {
                        "pcodes": { "type": "array", "items": { "$ref": "#/components/schemas/PCode" } },
                        "pagination": {
                          "type": "object",
                          "properties": {
                            "total": { "type": "integer" },
                            "page": { "type": "integer" },
                            "limit": { "type": "integer" }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": { "description": "API Key inválida." }
        }
      }
    }
  }
}
