{
  "info": {
    "name": "Recarga.do API v1",
    "description": "API pública de Recarga.do para recargas electrónicas y pago de servicios.\n\nLas variables de colección vienen precargadas con las credenciales del **ambiente de pruebas**: puede ejecutar todo sin configurar nada. El sandbox no genera recargas ni cargos reales.\n\n**Escenarios del sandbox** — los decide el último dígito de `identifier`:\n\n| Termina en | Resultado |\n|---|---|\n| 0 | 502 INVALID_PHONE (saldo devuelto) |\n| 1 | 502 TRANSACTION_REJECTED (saldo devuelto) |\n| 2 | 202 TRANSACTION_PENDING (saldo retenido) |\n| otro | 201 SUCCESS (saldo cobrado) |\n\n**Idempotencia:** `POST /v1/recharges` exige el header `Idempotency-Key`. La colección genera una nueva automáticamente en cada envío. Para probar la repetición, fije `idempotency_key` como variable y envíe dos veces: obtendrá el mismo `transaction_id` y el header `Idempotent-Replay: true`.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    { "key": "base_url", "value": "https://api.recarga.do", "type": "string" },
    { "key": "api_key", "value": "rk_sbx_8c4cacb52c6b8f65bf389480ce3f8891", "type": "string" },
    { "key": "api_secret", "value": "sk_sbx_a8e4de1dbd5e135a4be191cfd8901f386e541279a6550b1d30af9fbeaa65bbd0", "type": "string" },
    { "key": "service_id", "value": "23", "type": "string" },
    { "key": "transaction_id", "value": "", "type": "string" },
    { "key": "idempotency_key", "value": "", "type": "string" }
  ],
  "auth": {
    "type": "bearer",
    "bearer": [{ "key": "token", "value": "{{api_key}}.{{api_secret}}", "type": "string" }]
  },
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "// Genera una clave de idempotencia nueva si la variable está vacía.",
          "// Fíjela manualmente para probar la repetición de una misma operación.",
          "if (!pm.collectionVariables.get('idempotency_key')) {",
          "    pm.collectionVariables.set('idempotency_key', require('uuid').v4());",
          "}"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "Sistema",
      "item": [
        {
          "name": "Health",
          "request": {
            "auth": { "type": "noauth" },
            "method": "GET",
            "url": { "raw": "{{base_url}}/v1/health", "host": ["{{base_url}}"], "path": ["v1", "health"] },
            "description": "Estado del servicio. Público, sin credenciales."
          }
        },
        {
          "name": "Info",
          "request": {
            "auth": { "type": "noauth" },
            "method": "GET",
            "url": { "raw": "{{base_url}}/v1/info", "host": ["{{base_url}}"], "path": ["v1", "info"] }
          }
        },
        {
          "name": "Health profundo",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/v1/health/deep", "host": ["{{base_url}}"], "path": ["v1", "health", "deep"] },
            "description": "Estado de las dependencias internas. Requiere autenticación."
          }
        }
      ]
    },
    {
      "name": "Cuenta",
      "item": [
        {
          "name": "Mi cuenta",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200 OK', () => pm.response.to.have.status(200));",
                  "const d = pm.response.json().data;",
                  "console.log(d.sandbox ? '✓ AMBIENTE DE PRUEBAS' : '⚠ PRODUCCIÓN — las operaciones mueven dinero real');"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/v1/account", "host": ["{{base_url}}"], "path": ["v1", "account"] },
            "description": "Confirme aquí si su token opera en pruebas o en producción."
          }
        },
        {
          "name": "Balance",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200 OK', () => pm.response.to.have.status(200));",
                  "const d = pm.response.json().data;",
                  "pm.test('Invariante: actual = disponible + retenido', () => {",
                  "    pm.expect(Math.abs(d.saldo_actual - (d.saldo_disponible + d.saldo_retenido))).to.be.below(0.01);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/v1/balance", "host": ["{{base_url}}"], "path": ["v1", "balance"] }
          }
        }
      ]
    },
    {
      "name": "Catálogo",
      "item": [
        {
          "name": "Categorías",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/v1/categories", "host": ["{{base_url}}"], "path": ["v1", "categories"] }
          }
        },
        {
          "name": "Servicios",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/v1/services",
              "host": ["{{base_url}}"],
              "path": ["v1", "services"],
              "query": [
                { "key": "categoria", "value": "Telefonía", "disabled": true },
                { "key": "q", "value": "claro", "disabled": true }
              ]
            },
            "description": "El `id` de cada servicio es el `service_id` que necesita para recargar."
          }
        },
        {
          "name": "Paquetes de un servicio",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/v1/services/{{service_id}}/packages",
              "host": ["{{base_url}}"],
              "path": ["v1", "services", "{{service_id}}", "packages"]
            }
          }
        }
      ]
    },
    {
      "name": "Recargas",
      "item": [
        {
          "name": "Recarga · SUCCESS (sandbox)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('201 Created', () => pm.response.to.have.status(201));",
                  "const d = pm.response.json().data;",
                  "pm.collectionVariables.set('transaction_id', d.transaction_id);",
                  "if (pm.response.headers.get('Idempotent-Replay') === 'true') {",
                  "    console.log('↻ Repetición: no se ejecutó una segunda recarga.');",
                  "}",
                  "// Libera la clave para la siguiente operación.",
                  "pm.collectionVariables.set('idempotency_key', '');"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "{{idempotency_key}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"service_id\": {{service_id}},\n  \"identifier\": \"8095551235\",\n  \"amount\": 100,\n  \"terminal_id\": \"CAJA-01\"\n}"
            },
            "url": { "raw": "{{base_url}}/v1/recharges", "host": ["{{base_url}}"], "path": ["v1", "recharges"] },
            "description": "Identifier termina en 5 → el sandbox responde SUCCESS y cobra el importe."
          }
        },
        {
          "name": "Recarga · PENDING (sandbox)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('202 Accepted', () => pm.response.to.have.status(202));",
                  "const e = pm.response.json().error;",
                  "pm.test('Código TRANSACTION_PENDING', () => pm.expect(e.code).to.eql('TRANSACTION_PENDING'));",
                  "pm.collectionVariables.set('transaction_id', e.details.transaction_id);",
                  "console.log('El importe queda RETENIDO. Consulte el estado; NO reintente con clave nueva.');",
                  "pm.collectionVariables.set('idempotency_key', '');"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "{{idempotency_key}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"service_id\": {{service_id}},\n  \"identifier\": \"8095550002\",\n  \"amount\": 75\n}"
            },
            "url": { "raw": "{{base_url}}/v1/recharges", "host": ["{{base_url}}"], "path": ["v1", "recharges"] },
            "description": "Identifier termina en 2 → resultado desconocido. Es el caso que debe probar antes de salir a producción."
          }
        },
        {
          "name": "Recarga · FAILED (sandbox)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('502 Bad Gateway', () => pm.response.to.have.status(502));",
                  "console.log('Rechazo definitivo: el importe ya fue devuelto a su saldo.');",
                  "pm.collectionVariables.set('idempotency_key', '');"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "{{idempotency_key}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"service_id\": {{service_id}},\n  \"identifier\": \"8095550000\",\n  \"amount\": 75\n}"
            },
            "url": { "raw": "{{base_url}}/v1/recharges", "host": ["{{base_url}}"], "path": ["v1", "recharges"] },
            "description": "Identifier termina en 0 → INVALID_PHONE. El saldo se devuelve completo."
          }
        }
      ]
    },
    {
      "name": "Transacciones",
      "item": [
        {
          "name": "Historial",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/v1/transactions?per_page=20",
              "host": ["{{base_url}}"],
              "path": ["v1", "transactions"],
              "query": [
                { "key": "per_page", "value": "20" },
                { "key": "status", "value": "SUCCESS", "disabled": true },
                { "key": "date_from", "value": "2026-08-01", "disabled": true },
                { "key": "identifier", "value": "8095551235", "disabled": true }
              ]
            },
            "description": "Filtrar por `identifier` es la mejor forma de localizar una recarga tras un timeout."
          }
        },
        {
          "name": "Detalle",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/v1/transactions/{{transaction_id}}",
              "host": ["{{base_url}}"],
              "path": ["v1", "transactions", "{{transaction_id}}"]
            }
          }
        },
        {
          "name": "Recibo",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/v1/transactions/{{transaction_id}}/receipt",
              "host": ["{{base_url}}"],
              "path": ["v1", "transactions", "{{transaction_id}}", "receipt"]
            },
            "description": "Solo disponible para transacciones en estado SUCCESS."
          }
        }
      ]
    }
  ]
}
