{
  "openapi": "3.0.3",
  "info": {
    "title": "MesBazaar API",
    "version": "1.0.0",
    "description": "API رسمی MesBazaar برای دریافت قیمت و **خرید و فروش مس**.\n\n### واحدها\nهمه‌ی قیمت‌ها به **ریال** و به ازای هر **گرم** هستند (تومان = ریال ÷ ۱۰). مقدار مس به **میلی‌گرم** است (گرم = mg ÷ ۱۰۰۰).\n\n### احراز هویت\nendpointهای قیمت و محتوا عمومی‌اند. برای خرید/فروش، کیف‌پول و KYC باید با **توکن کاربر** درخواست بزنید:\n1. `POST /auth/login` با موبایل یا ایمیل (کد یکبارمصرف ارسال می‌شود).\n2. `POST /auth/verify-otp` → پاسخ شامل `token` (JWT) است.\n3. در سایر درخواست‌ها هدر `Authorization: Bearer <token>` را بفرستید.\nتوکن کوتاه‌عمر است؛ با `POST /auth/refresh` آن را تازه کنید.\n\n### جریان خرید/فروش\n`POST /orders/quote` (قفل قیمت) → `POST /orders/{id}/confirm` → برای **خرید** `POST /payments/start/{id}` و رفتن به `redirect_url`؛ برای **فروش** مس قفل و تسویه می‌شود. وضعیت را با `GET /orders/{id}` دنبال کنید."
  },
  "servers": [
    { "url": "https://mesbazaar.ir/api/v1", "description": "Production" },
    { "url": "http://localhost:8080/api/v1", "description": "Local" }
  ],
  "tags": [
    { "name": "Auth", "description": "ورود، کد یکبارمصرف و توکن" },
    { "name": "Trading", "description": "خرید و فروش مس" },
    { "name": "Payments", "description": "پرداخت سفارش خرید" },
    { "name": "Wallet", "description": "کیف پول تومانی و مس" },
    { "name": "KYC", "description": "احراز هویت و شبا" },
    { "name": "Price", "description": "قیمت لحظه‌ای و تاریخچه" },
    { "name": "Content", "description": "محتوای عمومی سایت" }
  ],
  "security": [],
  "paths": {
    "/auth/login": {
      "post": {
        "tags": ["Auth"],
        "summary": "ارسال کد یکبارمصرف برای ورود",
        "description": "یکی از `phone` یا `email` را بدهید. کد به همان مقصد ارسال می‌شود.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IdentifierRequest" }, "example": { "phone": "09123456789" } } } },
        "responses": { "202": { "description": "کد ارسال شد" }, "404": { "description": "کاربر یافت نشد" }, "429": { "description": "تعداد درخواست زیاد" } }
      }
    },
    "/auth/register": {
      "post": {
        "tags": ["Auth"],
        "summary": "ثبت‌نام (ارسال کد)",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IdentifierRequest" } } } },
        "responses": { "202": { "description": "کد ارسال شد" } }
      }
    },
    "/auth/verify-otp": {
      "post": {
        "tags": ["Auth"],
        "summary": "تایید کد و دریافت توکن",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VerifyOTPRequest" }, "example": { "target": "+989123456789", "code": "123456", "purpose": "login" } } } },
        "responses": { "200": { "description": "توکن صادر شد", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthResponse" } } } }, "401": { "description": "کد نامعتبر" } }
      }
    },
    "/auth/login-password": {
      "post": {
        "tags": ["Auth"],
        "summary": "ورود با رمز عبور",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/IdentifierRequest" }, { "type": "object", "required": ["password"], "properties": { "password": { "type": "string", "minLength": 8 } } } ] } } } },
        "responses": { "200": { "description": "ورود موفق", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthResponse" } } } }, "401": { "description": "اعتبار نامعتبر" } }
      }
    },
    "/auth/refresh": {
      "post": {
        "tags": ["Auth"],
        "summary": "تازه‌سازی توکن (rotation)",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["refresh_token"], "properties": { "refresh_token": { "type": "string" } } } } } },
        "responses": { "200": { "description": "توکن جدید", "content": { "application/json": { "schema": { "type": "object", "properties": { "token": { "type": "string" }, "refresh_token": { "type": "string" } } } } } }, "401": { "description": "توکن نامعتبر" } }
      }
    },
    "/auth/me": {
      "get": {
        "tags": ["Auth"],
        "summary": "اطلاعات کاربر جاری",
        "security": [{ "bearerAuth": [] }, { "apiKeyAuth": [] }],
        "responses": { "200": { "description": "کاربر", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } } }, "401": { "description": "نیاز به ورود" } }
      }
    },

    "/orders/quote": {
      "post": {
        "tags": ["Trading"],
        "summary": "گرفتن قیمت قطعی (قفل قیمت)",
        "description": "قیمت را برای چند ثانیه قفل می‌کند و یک سفارش در وضعیت `quoted` می‌سازد. سپس آن را `confirm` کنید.",
        "security": [{ "bearerAuth": [] }, { "apiKeyAuth": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuoteRequest" }, "example": { "side": "buy", "amount_irr": 10000000 } } } },
        "responses": {
          "201": { "description": "قیمت قفل شد", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Quote" } } } },
          "403": { "description": "برای فروش: احراز هویت یا شبای تاییدشده لازم است" },
          "422": { "description": "مبلغ خارج از حد مجاز یا بازار بسته" }
        }
      }
    },
    "/orders/{id}/confirm": {
      "post": {
        "tags": ["Trading"],
        "summary": "تایید سفارش",
        "description": "سفارش `quoted` را به `pending_payment` می‌برد. برای فروش، مس در کیف‌پول قفل می‌شود.",
        "security": [{ "bearerAuth": [] }, { "apiKeyAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/OrderId" }],
        "responses": { "200": { "description": "سفارش", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Order" } } } }, "409": { "description": "قیمت منقضی شده — دوباره quote بگیرید" } }
      }
    },
    "/orders/{id}/cancel": {
      "post": {
        "tags": ["Trading"],
        "summary": "لغو سفارش",
        "security": [{ "bearerAuth": [] }, { "apiKeyAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/OrderId" }],
        "requestBody": { "required": false, "content": { "application/json": { "schema": { "type": "object", "properties": { "reason": { "type": "string" } } } } } },
        "responses": { "200": { "description": "لغو شد", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Order" } } } } }
      }
    },
    "/orders/{id}": {
      "get": {
        "tags": ["Trading"],
        "summary": "وضعیت یک سفارش",
        "security": [{ "bearerAuth": [] }, { "apiKeyAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/OrderId" }],
        "responses": { "200": { "description": "سفارش", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Order" } } } }, "404": { "description": "یافت نشد" } }
      }
    },
    "/orders": {
      "get": {
        "tags": ["Trading"],
        "summary": "فهرست سفارش‌های کاربر",
        "security": [{ "bearerAuth": [] }, { "apiKeyAuth": [] }],
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 20 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "default": 0 } }
        ],
        "responses": { "200": { "description": "آرایه‌ی سفارش‌ها", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Order" } } } } } }
      }
    },
    "/orders/{id}/events": {
      "get": {
        "tags": ["Trading"],
        "summary": "تایم‌لاین وضعیت سفارش",
        "security": [{ "bearerAuth": [] }, { "apiKeyAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/OrderId" }],
        "responses": { "200": { "description": "رویدادها", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "object" } } } } } }
      }
    },
    "/payments/start/{orderID}": {
      "post": {
        "tags": ["Payments"],
        "summary": "شروع پرداخت سفارش خرید",
        "description": "برای سفارش `pending_payment` (خرید) لینک درگاه می‌سازد. کاربر را به `redirect_url` بفرستید.",
        "security": [{ "bearerAuth": [] }, { "apiKeyAuth": [] }],
        "parameters": [{ "name": "orderID", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": { "200": { "description": "لینک درگاه", "content": { "application/json": { "schema": { "type": "object", "properties": { "gateway": { "type": "string" }, "redirect_url": { "type": "string" }, "gateway_ref": { "type": "string" } } } } } } }
      }
    },

    "/wallet": {
      "get": {
        "tags": ["Wallet"],
        "summary": "موجودی کیف‌پول‌ها",
        "security": [{ "bearerAuth": [] }, { "apiKeyAuth": [] }],
        "responses": { "200": { "description": "آرایه‌ی کیف‌پول", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Wallet" } } } } } }
      }
    },

    "/kyc": {
      "get": {
        "tags": ["KYC"],
        "summary": "وضعیت احراز هویت",
        "security": [{ "bearerAuth": [] }, { "apiKeyAuth": [] }],
        "responses": { "200": { "description": "وضعیت KYC یا null" } }
      },
      "post": {
        "tags": ["KYC"],
        "summary": "ثبت هویت (بررسی شاهکار)",
        "security": [{ "bearerAuth": [] }, { "apiKeyAuth": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["national_id", "birth_date"], "properties": { "national_id": { "type": "string" }, "birth_date": { "type": "string", "example": "1990-03-21" } } } } } },
        "responses": { "200": { "description": "تایید شد" }, "401": { "description": "عدم تطابق شاهکار" } }
      }
    },
    "/kyc/sheba": {
      "get": { "tags": ["KYC"], "summary": "فهرست شباها", "security": [{ "bearerAuth": [] }, { "apiKeyAuth": [] }], "responses": { "200": { "description": "آرایه‌ی شبا" } } },
      "post": {
        "tags": ["KYC"],
        "summary": "افزودن شبا (بررسی مالکیت)",
        "security": [{ "bearerAuth": [] }, { "apiKeyAuth": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["sheba"], "properties": { "sheba": { "type": "string", "example": "IR000000000000000000000000" }, "make_primary": { "type": "boolean" } } } } } },
        "responses": { "200": { "description": "افزوده شد" }, "422": { "description": "نام صاحب حساب مطابقت ندارد" } }
      }
    },

    "/price/latest": {
      "get": {
        "tags": ["Price"],
        "summary": "آخرین قیمت لحظه‌ای",
        "responses": { "200": { "description": "قیمت", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Live" }, "example": { "market": "gerami_copper", "price_irr": 23300, "freshness": "fresh", "age_seconds": 12, "trading_open": true, "fetched_at": "2026-05-30T14:02:47Z" } } } } }
      }
    },
    "/price/chart": {
      "get": {
        "tags": ["Price"],
        "summary": "نمودار قیمت بر اساس بازه",
        "parameters": [{ "name": "range", "in": "query", "schema": { "type": "string", "enum": ["1d", "1m", "1y"], "default": "1d" } }],
        "responses": { "200": { "description": "نقاط نمودار", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ChartPoint" } } } } } }
      }
    },
    "/price/stream": {
      "get": {
        "tags": ["Price"],
        "summary": "جریان زنده‌ی قیمت (SSE)",
        "description": "اتصال Server-Sent Events؛ رویداد `price` با ساختار `/price/latest`.\n\n```js\nconst es = new EventSource('https://mesbazaar.ir/api/v1/price/stream');\nes.addEventListener('price', e => console.log(JSON.parse(e.data)));\n```",
        "responses": { "200": { "description": "text/event-stream", "content": { "text/event-stream": { "schema": { "type": "string" } } } } }
      }
    },
    "/content/{key}": {
      "get": {
        "tags": ["Content"],
        "summary": "محتوای عمومی صفحه",
        "parameters": [{ "name": "key", "in": "path", "required": true, "schema": { "type": "string", "enum": ["landing"] } }],
        "responses": { "200": { "description": "بلوک JSON" }, "404": { "description": "کلید ناشناخته" } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "توکن دریافتی از /auth/verify-otp" },
      "apiKeyAuth": { "type": "apiKey", "in": "header", "name": "X-API-Key", "description": "کلید شخصی API که از پنل کاربری می‌سازید (پنل ← کلید API). برای دسترسی برنامه‌ای به endpointهای حساب، جایگزین توکن است." }
    },
    "parameters": {
      "OrderId": { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
    },
    "schemas": {
      "IdentifierRequest": {
        "type": "object",
        "description": "یکی از phone یا email الزامی است.",
        "properties": { "phone": { "type": "string", "example": "09123456789" }, "email": { "type": "string", "format": "email" } }
      },
      "VerifyOTPRequest": {
        "type": "object",
        "required": ["target", "code", "purpose"],
        "properties": {
          "target": { "type": "string", "description": "موبایل E.164 یا ایمیل" },
          "code": { "type": "string", "minLength": 6, "maxLength": 6 },
          "purpose": { "type": "string", "enum": ["login", "register"] }
        }
      },
      "AuthResponse": {
        "type": "object",
        "properties": { "token": { "type": "string" }, "refresh_token": { "type": "string" }, "user": { "$ref": "#/components/schemas/User" } }
      },
      "User": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "phone": { "type": "string" },
          "email": { "type": "string" },
          "status": { "type": "string" },
          "has_password": { "type": "boolean" },
          "has_google": { "type": "boolean" },
          "kyc_verified": { "type": "boolean" }
        }
      },
      "QuoteRequest": {
        "type": "object",
        "description": "سفارش با **یکی** از این دو واحد قیمت‌گذاری می‌شود: مبلغ ریالی (`amount_irr`) یا وزن مس بر حسب میلی‌گرم (`amount_copper_mg`). ارسال هر دو یا هیچ‌کدام خطای اعتبارسنجی است.",
        "required": ["side"],
        "properties": {
          "side": { "type": "string", "enum": ["buy", "sell"] },
          "amount_irr": { "type": "integer", "description": "مبلغ به ریال (در صورت قیمت‌گذاری بر اساس تومان)", "example": 10000000 },
          "amount_copper_mg": { "type": "integer", "description": "وزن مس به میلی‌گرم (در صورت قیمت‌گذاری بر اساس واحد مس)؛ گرم × ۱۰۰۰", "example": 10000 }
        }
      },
      "Quote": {
        "type": "object",
        "properties": {
          "order_id": { "type": "string", "format": "uuid" },
          "side": { "type": "string", "enum": ["buy", "sell"] },
          "quote_price_irr": { "type": "integer", "description": "قیمت قفل‌شده هر گرم (ریال)" },
          "spread_pct": { "type": "number" },
          "amount_irr": { "type": "integer" },
          "amount_copper_mg": { "type": "integer", "description": "مقدار مس به میلی‌گرم" },
          "fee_irr": { "type": "integer" },
          "quote_expires_at": { "type": "string", "format": "date-time" },
          "expires_in_seconds": { "type": "integer" }
        }
      },
      "Order": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "side": { "type": "string", "enum": ["buy", "sell"] },
          "status": { "type": "string", "enum": ["draft", "quoted", "expired", "pending_payment", "paid", "completed", "failed", "cancelled", "slippage_exceeded", "refund_pending", "refunded"] },
          "quote_price_irr": { "type": "integer" },
          "settlement_price_irr": { "type": "integer" },
          "amount_irr": { "type": "integer" },
          "amount_copper_mg": { "type": "integer" },
          "fee_irr": { "type": "integer" },
          "quote_expires_at": { "type": "string", "format": "date-time" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "Wallet": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "currency": { "type": "string", "enum": ["IRR", "COPPER_MG"] },
          "balance": { "type": "integer" },
          "locked_balance": { "type": "integer" },
          "available": { "type": "integer" }
        }
      },
      "Live": {
        "type": "object",
        "properties": {
          "market": { "type": "string" },
          "price_irr": { "type": "integer", "description": "ریال به ازای هر گرم" },
          "freshness": { "type": "string", "enum": ["fresh", "stale_ok", "stale_warn", "frozen"] },
          "age_seconds": { "type": "integer" },
          "trading_open": { "type": "boolean" },
          "fetched_at": { "type": "string", "format": "date-time" }
        }
      },
      "ChartPoint": {
        "type": "object",
        "properties": { "time": { "type": "string", "format": "date-time" }, "price_irr": { "type": "integer" } }
      }
    }
  }
}
