{
  "info": {
    "name": "CampNow – CampingCare ERP Integration",
    "description": "CampingCare OAuth setup and category sync.\n\n**Sub-category listing, mapping, and availability are in `sub-categories.postman_collection.json` — those routes are generic across all ERP types.**\n\n**Setup:**\n1. Set `baseUrl` to your API base (default: http://localhost:5000)\n2. Set `campsiteToken` to a valid campsite JWT\n\n**Flow:**\n1. POST /initiate → copy `sessionId` into the collection variable\n2. Complete OAuth in the popup (callback is handled automatically)\n3. GET /status/:sessionId → poll until `status: \"completed\"`\n4. POST /sync → trigger a manual sync at any time",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    { "key": "baseUrl",       "value": "http://localhost:5000",          "type": "string" },
    { "key": "campsiteToken", "value": "<paste-your-campsite-jwt-here>", "type": "string" },
    { "key": "sessionId",     "value": "",                               "type": "string", "description": "Populated automatically from POST /initiate" }
  ],
  "item": [
    {
      "name": "OAuth Flow",
      "item": [
        {
          "name": "1. Initiate OAuth",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "const res = pm.response.json();",
                  "if (res.status && res.data?.sessionId) {",
                  "  pm.collectionVariables.set('sessionId', res.data.sessionId);",
                  "  console.log('sessionId saved:', res.data.sessionId);",
                  "}"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization",   "value": "Bearer {{campsiteToken}}", "type": "text" },
              { "key": "Content-Type",    "value": "application/json",         "type": "text" },
              { "key": "Accept-Language", "value": "en",                       "type": "text" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"syncAt\": 60\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/api/camping-care/initiate",
              "host": ["{{baseUrl}}"],
              "path": ["api", "camping-care", "initiate"]
            },
            "description": "Starts the CampingCare OAuth flow.\n\n- Deletes any stale pending sessions for this campsite\n- Creates a new ERP OAuth session (10-minute TTL)\n- Returns `sessionId` (for polling) and `installUrl` (open in popup)\n\n**syncAt** valid values: `30`, `60`, `120` (minutes between cron syncs)\n\nThe test script saves `sessionId` to the collection variable automatically."
          },
          "response": [
            {
              "name": "200 – Initiated",
              "status": "OK",
              "code": 200,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"status\": true,\n  \"message\": \"campingCare.initiated\",\n  \"data\": {\n    \"sessionId\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n    \"installUrl\": \"https://app.camping.care/apps/1045/install?redirect_uri=http%3A%2F%2Flocalhost%3A5000%2Fapi%2Fcamping-care%2Fcallback\"\n  }\n}"
            },
            {
              "name": "422 – Validation Error",
              "status": "Unprocessable Entity",
              "code": 422,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"success\": false,\n  \"message\": \"Validation failed\",\n  \"errors\": [\n    {\n      \"field\": \"syncAt\",\n      \"message\": \"\\\"syncAt\\\" must be one of [30, 60, 120]\"\n    }\n  ]\n}"
            },
            {
              "name": "401 – Unauthorized",
              "status": "Unauthorized",
              "code": 401,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"status\": false,\n  \"message\": \"Unauthorized\",\n  \"errors\": null,\n  \"data\": null\n}"
            }
          ]
        },
        {
          "name": "2. OAuth Callback (Public – CampingCare Redirect)",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Accept-Language", "value": "en", "type": "text" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/camping-care/callback?authtoken=SAMPLE_AUTH_TOKEN&admin_id=12345&admin_uid=abc123&chain_id=0&app_id=1045",
              "host": ["{{baseUrl}}"],
              "path": ["api", "camping-care", "callback"],
              "query": [
                { "key": "authtoken",  "value": "SAMPLE_AUTH_TOKEN", "description": "OAuth token from CampingCare (required)" },
                { "key": "admin_id",   "value": "12345",             "description": "CampingCare admin ID (required)" },
                { "key": "admin_uid",  "value": "abc123",            "description": "Admin UID (optional)" },
                { "key": "chain_id",   "value": "0",                 "description": "Chain ID (optional)" },
                { "key": "app_id",     "value": "1045",              "description": "App ID (optional)" }
              ]
            },
            "description": "**Called automatically by CampingCare** after the user installs the app. Do not call manually in production.\n\nOn success it:\n1. Saves credentials to `Availability.campingCare` (`authToken`, `adminId`, etc.)\n2. Exchanges `authToken` → `idToken` + `refreshToken` via `/oauth/token` and stores them\n3. Sets `configType = \"camping_care\"`, `isActive = true`, `syncAt`\n4. Runs the first sub-category sync immediately (builds `SubCategory` + 14 days of `AvailabilityRule`)\n5. Schedules the recurring cron job\n6. Updates session status to `\"completed\"`\n\nAlways responds with HTML that calls `window.close()` to close the popup."
          },
          "response": [
            {
              "name": "200 – Popup Close HTML",
              "status": "OK",
              "code": 200,
              "header": [{ "key": "Content-Type", "value": "text/html" }],
              "body": "<!DOCTYPE html>\n<html>\n  <head><title>Connecting...</title></head>\n  <body><script>window.close();</script></body>\n</html>"
            }
          ]
        },
        {
          "name": "3. Poll Session Status",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization",   "value": "Bearer {{campsiteToken}}", "type": "text" },
              { "key": "Accept-Language", "value": "en",                       "type": "text" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/camping-care/status/{{sessionId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "camping-care", "status", "{{sessionId}}"]
            },
            "description": "Poll after initiating OAuth (FE polls every 2 seconds).\n\n**status values:**\n- `pending` – OAuth not completed yet\n- `completed` – Credentials saved, token exchanged, initial sync done\n- `error` – Something failed; see `error` field\n\n`categoriesCount` is populated once sync completes."
          },
          "response": [
            {
              "name": "200 – Pending",
              "status": "OK",
              "code": 200,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"status\": true,\n  \"message\": \"campingCare.status\",\n  \"data\": {\n    \"status\": \"pending\",\n    \"error\": null,\n    \"categoriesCount\": 0\n  }\n}"
            },
            {
              "name": "200 – Completed",
              "status": "OK",
              "code": 200,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"status\": true,\n  \"message\": \"campingCare.status\",\n  \"data\": {\n    \"status\": \"completed\",\n    \"error\": null,\n    \"categoriesCount\": 8\n  }\n}"
            },
            {
              "name": "200 – Error",
              "status": "OK",
              "code": 200,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"status\": true,\n  \"message\": \"campingCare.status\",\n  \"data\": {\n    \"status\": \"error\",\n    \"error\": \"Failed to save integration\",\n    \"categoriesCount\": 0\n  }\n}"
            },
            {
              "name": "404 – Session Not Found or Expired",
              "status": "Not Found",
              "code": 404,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"status\": false,\n  \"message\": \"Session not found or expired\",\n  \"errors\": null,\n  \"data\": null\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Sync",
      "item": [
        {
          "name": "Manual Sync",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization",   "value": "Bearer {{campsiteToken}}", "type": "text" },
              { "key": "Accept-Language", "value": "en",                       "type": "text" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/camping-care/sync",
              "host": ["{{baseUrl}}"],
              "path": ["api", "camping-care", "sync"]
            },
            "description": "Manually triggers a full sync for the campsite.\n\n- Validates and auto-refreshes the CampingCare access token (`idToken`) if near expiry\n- Fetches all accommodations from the CampingCare API\n- Upserts `SubCategory` records (keyed on `erpCategoryId`)\n- Rebuilds 14-day `AvailabilityRule` window per sub-category\n- Soft-deletes sub-categories no longer returned by the API (`deletedAt`)\n\nThe cron job runs the same operation automatically at the `syncAt` interval."
          },
          "response": [
            {
              "name": "200 – Synced",
              "status": "OK",
              "code": 200,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"status\": true,\n  \"message\": \"campingCare.synced\",\n  \"data\": {\n    \"categoriesCount\": 8\n  }\n}"
            },
            {
              "name": "400 – No Credentials",
              "status": "Bad Request",
              "code": 400,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"status\": false,\n  \"message\": \"No CampingCare credentials found for this campsite\",\n  \"errors\": null,\n  \"data\": null\n}"
            },
            {
              "name": "400 – Re-auth Required",
              "status": "Bad Request",
              "code": 400,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"status\": false,\n  \"message\": \"No CampingCare refresh token found - re-authentication required\",\n  \"errors\": null,\n  \"data\": null\n}"
            }
          ]
        }
      ]
    }
  ]
}
