openapi: 3.1.0
info:
  title: Nasak Commerce API
  version: 1.0.0-alpha.2
  description: |
    Versioned storefront, checkout, order, address, and payment compatibility API.
    Public reads are available through the central gateway. Session-authenticated
    mutations retain same-origin enforcement during the 90-day migration window.
servers:
  - url: https://api.nasak.ir/v1/commerce
tags:
  - name: Health
  - name: Catalog
  - name: Checkout
  - name: Orders
  - name: Payments
  - name: Commerce Admin
paths:
  /live:
    get:
      operationId: getCommerceLiveness
      summary: Check commerce service liveness
      tags: [Health]
      security: []
      responses:
        '200':
          description: Commerce API is alive.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Health'
  /catalog:
    get:
      operationId: listCommerceCatalog
      summary: List the public catalog for a store
      tags: [Catalog]
      security: []
      parameters:
        - $ref: '#/components/parameters/Store'
        - name: query
          in: query
          schema: { type: string, maxLength: 120 }
        - name: category
          in: query
          schema: { type: string, maxLength: 100 }
        - name: available
          in: query
          schema: { type: boolean, default: true }
      responses:
        '200':
          description: Store catalog.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Catalog'
        '400':
          $ref: '#/components/responses/BadRequest'
  /products/{slug}:
    get:
      operationId: getCommerceProduct
      summary: Get one active product by slug
      tags: [Catalog]
      security: []
      parameters:
        - $ref: '#/components/parameters/ProductSlug'
        - $ref: '#/components/parameters/Store'
      responses:
        '200':
          description: Product details.
          content:
            application/json:
              schema:
                type: object
                required: [product]
                properties:
                  product:
                    $ref: '#/components/schemas/Product'
        '404':
          $ref: '#/components/responses/NotFound'
  /bundles/{slug}:
    get:
      operationId: getCommerceBundle
      summary: Get one active product bundle by slug
      tags: [Catalog]
      security: []
      parameters:
        - $ref: '#/components/parameters/BundleSlug'
        - $ref: '#/components/parameters/Store'
      responses:
        '200':
          description: Bundle details.
          content:
            application/json:
              schema:
                type: object
                required: [bundle]
                properties:
                  bundle:
                    $ref: '#/components/schemas/Bundle'
        '404':
          $ref: '#/components/responses/NotFound'
  /delivery-slots:
    get:
      operationId: listCommerceDeliverySlots
      summary: List available delivery slots
      tags: [Checkout]
      security: []
      parameters:
        - $ref: '#/components/parameters/Store'
        - name: days
          in: query
          schema: { type: integer, minimum: 1, maximum: 31, default: 7 }
      responses:
        '200':
          description: Delivery availability.
          content:
            application/json:
              schema:
                type: object
                required: [availability]
                properties:
                  availability:
                    type: array
                    items: { type: object, additionalProperties: true }
  /shipping-zones:
    get:
      operationId: listCommerceShippingZones
      summary: List public shipping restrictions and fees
      tags: [Checkout]
      security: []
      parameters:
        - $ref: '#/components/parameters/Store'
      responses:
        '200':
          description: Shipping zones.
          content:
            application/json:
              schema:
                type: object
                required: [zones]
                properties:
                  zones:
                    type: array
                    items: { type: object, additionalProperties: true }
  /discounts/validate:
    post:
      operationId: validateCommerceDiscount
      summary: Preview a discount code against a cart subtotal
      tags: [Checkout]
      security: []
      x-idempotency-exempt: true
      x-migration-note: Preview only; order creation revalidates server-side totals.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DiscountValidationRequest'
      responses:
        '200':
          description: Discount preview.
          content:
            application/json:
              schema:
                type: object
                required: [discount]
                properties:
                  discount: { type: object, additionalProperties: true }
        '400':
          $ref: '#/components/responses/BadRequest'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /checkout:
    post:
      operationId: createCommerceOrder
      summary: Validate a cart and create an order
      tags: [Checkout]
      security:
        - cookieSession: []
      x-idempotency-exempt: true
      x-migration-note: Idempotency-Key enforcement is scheduled for shared infrastructure phase.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CheckoutRequest'
      responses:
        '201':
          description: Order created.
          content:
            application/json:
              schema:
                type: object
                required: [order]
                properties:
                  order:
                    $ref: '#/components/schemas/Order'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /orders:
    get:
      operationId: listMyCommerceOrders
      summary: List orders belonging to the current customer
      tags: [Orders]
      security:
        - cookieSession: []
      responses:
        '200':
          description: Customer orders.
          content:
            application/json:
              schema:
                type: object
                required: [orders]
                properties:
                  orders:
                    type: array
                    items:
                      $ref: '#/components/schemas/Order'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /orders/{id}:
    get:
      operationId: getMyCommerceOrder
      summary: Get one order belonging to the current customer
      tags: [Orders]
      security:
        - cookieSession: []
      parameters:
        - $ref: '#/components/parameters/OrderId'
      responses:
        '200':
          description: Customer order.
          content:
            application/json:
              schema:
                type: object
                required: [order]
                properties:
                  order:
                    $ref: '#/components/schemas/Order'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /addresses:
    get:
      operationId: listMyCommerceAddresses
      summary: List saved checkout addresses
      tags: [Checkout]
      security:
        - cookieSession: []
      responses:
        '200':
          description: Saved addresses.
          content:
            application/json:
              schema:
                type: object
                required: [addresses]
                properties:
                  addresses:
                    type: array
                    items:
                      $ref: '#/components/schemas/Address'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      operationId: createMyCommerceAddress
      summary: Save a checkout address
      tags: [Checkout]
      security:
        - cookieSession: []
      x-idempotency-exempt: true
      x-migration-note: Duplicate-address handling remains compatible with the current storefront.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddressInput'
      responses:
        '201':
          description: Address saved.
          content:
            application/json:
              schema:
                type: object
                required: [address]
                properties:
                  address:
                    $ref: '#/components/schemas/Address'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /payments/request:
    post:
      operationId: requestCommercePayment
      summary: Create a gateway payment for a customer order
      tags: [Payments]
      security:
        - cookieSession: []
      x-idempotency-exempt: true
      x-migration-note: Existing payment creation is transaction-safe; shared Idempotency-Key storage follows.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [orderId]
              properties:
                orderId:
                  type: string
                  format: uuid
      responses:
        '200':
          description: Payment redirect created.
          content:
            application/json:
              schema:
                type: object
                required: [ok, paymentUrl, provider]
                properties:
                  ok: { type: boolean, const: true }
                  paymentUrl: { type: string, format: uri }
                  provider: { type: string }
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /payments/callback:
    get:
      operationId: receiveCommercePaymentCallbackGet
      summary: Receive a bank payment callback using query parameters
      tags: [Payments]
      security: []
      parameters:
        - $ref: '#/components/parameters/PaymentId'
        - $ref: '#/components/parameters/PaymentProvider'
      responses:
        '303':
          description: Redirect to the verified storefront callback page.
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      operationId: receiveCommercePaymentCallbackPost
      summary: Receive a bank payment callback using form data
      tags: [Payments]
      security: []
      x-idempotency-exempt: true
      x-migration-note: Bank callbacks are reconciled by payment identity.
      parameters:
        - $ref: '#/components/parameters/PaymentId'
        - $ref: '#/components/parameters/PaymentProvider'
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              additionalProperties: true
      responses:
        '303':
          description: Redirect to the verified storefront callback page.
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
        '415':
          description: Unsupported callback media type.
  /admin/products:
    get:
      operationId: listAdminCommerceProducts
      summary: List products visible to the current store manager
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      responses:
        '200': { $ref: '#/components/responses/AdminCollection' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      operationId: createAdminCommerceProduct
      summary: Create a product in the current store
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility BFF mutation; shared Idempotency-Key enforcement is scheduled.
      requestBody: { $ref: '#/components/requestBodies/AdminMutation' }
      responses:
        '201': { $ref: '#/components/responses/AdminResource' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /admin/products/{id}:
    parameters: [{ $ref: '#/components/parameters/ResourceId' }]
    patch:
      operationId: updateAdminCommerceProduct
      summary: Update a product in the current store
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility BFF mutation; shared Idempotency-Key enforcement is scheduled.
      requestBody: { $ref: '#/components/requestBodies/AdminMutation' }
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      operationId: archiveAdminCommerceProduct
      summary: Archive a product without damaging order history
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Archive operation is resource-state idempotent.
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /admin/categories:
    get:
      operationId: listAdminCommerceCategories
      summary: List store categories
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      responses:
        '200': { $ref: '#/components/responses/AdminCollection' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      operationId: createAdminCommerceCategory
      summary: Create a store category
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility BFF mutation.
      requestBody: { $ref: '#/components/requestBodies/AdminMutation' }
      responses:
        '201': { $ref: '#/components/responses/AdminResource' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /admin/categories/{id}:
    parameters: [{ $ref: '#/components/parameters/ResourceId' }]
    patch:
      operationId: updateAdminCommerceCategory
      summary: Update a store category
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility BFF mutation.
      requestBody: { $ref: '#/components/requestBodies/AdminMutation' }
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      operationId: archiveAdminCommerceCategory
      summary: Archive a store category
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Archive operation is resource-state idempotent.
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /admin/brands:
    get:
      operationId: listAdminCommerceBrands
      summary: List store brands
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      responses:
        '200': { $ref: '#/components/responses/AdminCollection' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      operationId: createAdminCommerceBrand
      summary: Create a store brand
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility BFF mutation.
      requestBody: { $ref: '#/components/requestBodies/AdminMutation' }
      responses:
        '201': { $ref: '#/components/responses/AdminResource' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /admin/brands/{id}:
    parameters: [{ $ref: '#/components/parameters/ResourceId' }]
    patch:
      operationId: updateAdminCommerceBrand
      summary: Update a store brand
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility BFF mutation.
      requestBody: { $ref: '#/components/requestBodies/AdminMutation' }
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      operationId: archiveAdminCommerceBrand
      summary: Archive a store brand
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Archive operation is resource-state idempotent.
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /admin/bundles:
    get:
      operationId: listAdminCommerceBundles
      summary: List store bundles
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      responses:
        '200': { $ref: '#/components/responses/AdminCollection' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      operationId: createAdminCommerceBundle
      summary: Create a store bundle
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility BFF mutation.
      requestBody: { $ref: '#/components/requestBodies/AdminMutation' }
      responses:
        '201': { $ref: '#/components/responses/AdminResource' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /admin/bundles/{id}:
    parameters: [{ $ref: '#/components/parameters/ResourceId' }]
    patch:
      operationId: updateAdminCommerceBundle
      summary: Update a store bundle
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility BFF mutation.
      requestBody: { $ref: '#/components/requestBodies/AdminMutation' }
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      operationId: archiveAdminCommerceBundle
      summary: Archive a store bundle
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Archive operation is resource-state idempotent.
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /admin/orders:
    get:
      operationId: listAdminCommerceOrders
      summary: Search and list orders for the current store
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      responses:
        '200': { $ref: '#/components/responses/AdminCollection' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /admin/orders/{id}:
    parameters: [{ $ref: '#/components/parameters/ResourceId' }]
    patch:
      operationId: updateAdminCommerceOrder
      summary: Update order status and shipping data
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility BFF mutation.
      requestBody: { $ref: '#/components/requestBodies/AdminMutation' }
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /admin/settings:
    get:
      operationId: getAdminCommerceSettings
      summary: Get safe store settings
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    patch:
      operationId: updateAdminCommerceSettings
      summary: Update store settings
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility BFF mutation.
      requestBody: { $ref: '#/components/requestBodies/AdminMutation' }
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /admin/analytics:
    get:
      operationId: getAdminCommerceAnalytics
      summary: Get store commerce analytics
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /admin/shipping-zones:
    get:
      operationId: listAdminCommerceShippingZones
      summary: List store shipping zones
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      responses:
        '200': { $ref: '#/components/responses/AdminCollection' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      operationId: createAdminCommerceShippingZone
      summary: Create a store shipping zone
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility BFF mutation.
      requestBody: { $ref: '#/components/requestBodies/AdminMutation' }
      responses:
        '201': { $ref: '#/components/responses/AdminResource' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /admin/delivery-slots:
    get:
      operationId: listAdminCommerceDeliverySlots
      summary: List store delivery slots
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      responses:
        '200': { $ref: '#/components/responses/AdminCollection' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      operationId: createAdminCommerceDeliverySlot
      summary: Create a store delivery slot
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility BFF mutation.
      requestBody: { $ref: '#/components/requestBodies/AdminMutation' }
      responses:
        '201': { $ref: '#/components/responses/AdminResource' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /admin/payment-gateways:
    get:
      operationId: listAdminCommercePaymentGateways
      summary: List safe payment gateway configuration
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      responses:
        '200': { $ref: '#/components/responses/AdminCollection' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      operationId: upsertAdminCommercePaymentGateway
      summary: Create or update an encrypted payment gateway configuration
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility BFF mutation; credentials are encrypted by the owning service.
      requestBody: { $ref: '#/components/requestBodies/AdminMutation' }
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '201': { $ref: '#/components/responses/AdminResource' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /admin/payment-gateways/{id}:
    parameters: [{ $ref: '#/components/parameters/ResourceId' }]
    delete:
      operationId: removeAdminCommercePaymentGateway
      summary: Remove a store payment gateway configuration
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Compatibility BFF mutation.
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /admin/payment-gateways/{id}/test:
    parameters: [{ $ref: '#/components/parameters/ResourceId' }]
    post:
      operationId: testAdminCommercePaymentGateway
      summary: Test an encrypted store payment gateway configuration
      tags: [Commerce Admin]
      security: [{ cookieSession: [] }]
      x-idempotency-exempt: true
      x-migration-note: Diagnostic action is rate-limited and audited by the owning service.
      responses:
        '200': { $ref: '#/components/responses/AdminResource' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
components:
  securitySchemes:
    cookieSession:
      type: apiKey
      in: cookie
      name: pdf_library_session
      description: Compatibility BFF session. OIDC bearer scopes replace this during the shared-auth phase.
  parameters:
    Store:
      name: store
      in: query
      required: false
      schema: { type: string, minLength: 1, maxLength: 60 }
      description: Store slug or custom-domain routing key.
    ProductSlug:
      name: slug
      in: path
      required: true
      schema: { type: string, minLength: 1, maxLength: 160 }
    BundleSlug:
      name: slug
      in: path
      required: true
      schema: { type: string, minLength: 1, maxLength: 160 }
    OrderId:
      name: id
      in: path
      required: true
      schema: { type: string, format: uuid }
    PaymentId:
      name: payment
      in: query
      required: true
      schema: { type: string, format: uuid }
    PaymentProvider:
      name: provider
      in: query
      required: true
      schema: { type: string, maxLength: 30 }
    ResourceId:
      name: id
      in: path
      required: true
      schema: { type: string, format: uuid }
  requestBodies:
    AdminMutation:
      required: true
      content:
        application/json:
          schema:
            type: object
            additionalProperties: true
  schemas:
    Health:
      type: object
      additionalProperties: false
      required: [status, service, version]
      properties:
        status: { type: string, const: ok }
        service: { type: string, const: commerce }
        version: { type: string, const: v1 }
    Catalog:
      type: object
      additionalProperties: true
      properties:
        store: { type: object, additionalProperties: true }
        categories:
          type: array
          items: { type: object, additionalProperties: true }
        brands:
          type: array
          items: { type: object, additionalProperties: true }
        products:
          type: array
          items:
            $ref: '#/components/schemas/Product'
        bundles:
          type: array
          items:
            $ref: '#/components/schemas/Bundle'
    Product:
      type: object
      additionalProperties: true
      required: [id, name, slug, price]
      properties:
        id: { type: string, format: uuid }
        name: { type: string }
        slug: { type: string }
        price: { type: integer, minimum: 0, description: Price in toman. }
        discountPrice: { type: [integer, 'null'], minimum: 0 }
        status: { type: string }
        isAvailable: { type: boolean }
        imageUrl: { type: [string, 'null'], format: uri }
    Bundle:
      type: object
      additionalProperties: true
      required: [id, name, slug, price]
      properties:
        id: { type: string, format: uuid }
        name: { type: string }
        slug: { type: string }
        price: { type: integer, minimum: 0 }
        isAvailable: { type: boolean }
        items:
          type: array
          items: { type: object, additionalProperties: true }
    CartItem:
      oneOf:
        - type: object
          additionalProperties: false
          required: [type, productId, quantity]
          properties:
            type: { type: string, const: PRODUCT }
            productId: { type: string, format: uuid }
            variantId: { type: [string, 'null'], format: uuid }
            quantity: { type: integer, minimum: 1, maximum: 999 }
        - type: object
          additionalProperties: false
          required: [type, bundleId, quantity]
          properties:
            type: { type: string, const: BUNDLE }
            bundleId: { type: string, format: uuid }
            quantity: { type: integer, minimum: 1, maximum: 999 }
    CheckoutRequest:
      type: object
      additionalProperties: false
      required: [items, fullName, phone, province, city, postalCode, address]
      properties:
        items:
          type: array
          minItems: 1
          maxItems: 40
          items:
            $ref: '#/components/schemas/CartItem'
        fullName: { type: string, minLength: 3, maxLength: 100 }
        phone: { type: string, maxLength: 30 }
        province: { type: string, minLength: 2, maxLength: 60 }
        city: { type: string, minLength: 2, maxLength: 60 }
        district: { type: integer, minimum: 1, maximum: 22 }
        postalCode: { type: string, pattern: '^[0-9]{10}$' }
        address: { type: string, minLength: 10, maxLength: 600 }
        customerNote: { type: string, maxLength: 500 }
        deliveryMethod: { type: string, enum: [POSTAL, COURIER, STORE], default: POSTAL }
        deliveryDate: { type: string, format: date }
        deliverySlotId: { type: string, format: uuid }
        cashOnDelivery: { type: boolean }
        discountCode: { type: string, maxLength: 40 }
        store: { type: string, maxLength: 60 }
    DiscountValidationRequest:
      type: object
      additionalProperties: false
      required: [code, subtotal]
      properties:
        code: { type: string, minLength: 1, maxLength: 40 }
        subtotal: { type: integer, minimum: 0, maximum: 1000000000 }
        shippingAmount: { type: integer, minimum: 0, maximum: 100000000, default: 0 }
        store: { type: string, maxLength: 60 }
    AddressInput:
      type: object
      additionalProperties: false
      required: [fullName, phone, province, city, postalCode, address]
      properties:
        label: { type: string, maxLength: 40 }
        fullName: { type: string, minLength: 3, maxLength: 100 }
        phone: { type: string, maxLength: 30 }
        province: { type: string, minLength: 2, maxLength: 60 }
        city: { type: string, minLength: 2, maxLength: 60 }
        postalCode: { type: string, pattern: '^[0-9]{10}$' }
        address: { type: string, minLength: 10, maxLength: 600 }
        isDefault: { type: boolean }
    Address:
      allOf:
        - $ref: '#/components/schemas/AddressInput'
        - type: object
          required: [id]
          properties:
            id: { type: string, format: uuid }
    Order:
      type: object
      additionalProperties: true
      required: [id, orderNumber, status, total]
      properties:
        id: { type: string, format: uuid }
        orderNumber: { type: string }
        status: { type: string }
        total: { type: integer, minimum: 0 }
        createdAt: { type: string, format: date-time }
    Error:
      type: object
      additionalProperties: true
      required: [error]
      properties:
        error: { type: string }
  responses:
    AdminCollection:
      description: Tenant-scoped administrative collection.
      content:
        application/json:
          schema:
            type: object
            additionalProperties: true
    AdminResource:
      description: Tenant-scoped administrative resource.
      content:
        application/json:
          schema:
            type: object
            additionalProperties: true
    BadRequest:
      description: Invalid request.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    Unauthorized:
      description: Authentication is required.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    Forbidden:
      description: The current principal cannot access this resource.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    Conflict:
      description: Resource state conflict.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    TooManyRequests:
      description: Rate limit exceeded.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
