openapi: 3.1.0
info:
  title: BulkMail API
  description: |
    REST API for BulkMail - Email Account Marketplace Platform.
    
    ## Authentication
    All authenticated endpoints require an API key passed in the `X-API-Key` header.
    
    ```
    X-API-Key: your_api_key_here
    ```
    
    ## Rate Limiting
    - 100 requests per minute for authenticated users
    - 20 requests per minute for unauthenticated endpoints
    
    ## Response Format
    All responses follow this format:
    ```json
    {
      "success": true|false,
      "data": {...},
      "message": "Optional message",
      "error": "Error message if success=false",
      "errors": {"field": ["error1", "error2"]}
    }
    ```
  version: 2.0.0
  contact:
    name: BulkMail Support
    email: support@bulkmail.shop
    url: https://bulkmail.shop
  license:
    name: Proprietary
    url: https://bulkmail.shop/terms

servers:
  - url: https://bulkmail.shop/api/v2
    description: Production server

tags:
  - name: User
    description: User profile and account management
  - name: Products
    description: Product catalog (public endpoints)
  - name: Orders
    description: Order management and purchases
  - name: Wallet
    description: Balance and payment operations
  - name: Stock
    description: Real-time stock information (public)
  - name: API Keys
    description: API key management

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: API key for authentication. Get your key from the dashboard.

  schemas:
    Error:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
          example: "Error message"
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string

    PaginationMeta:
      type: object
      properties:
        current_page:
          type: integer
          example: 1
        per_page:
          type: integer
          example: 20
        total:
          type: integer
          example: 100
        total_pages:
          type: integer
          example: 5

    User:
      type: object
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: "John Doe"
        email:
          type: string
          format: email
          example: "john@example.com"
        balance:
          type: number
          format: float
          example: 150.50
        created_at:
          type: string
          format: date-time

    Product:
      type: object
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: "Hotmail PVA"
        sku:
          type: string
          example: "HOTMAIL-PVA-001"
        description:
          type: string
        price:
          type: number
          format: float
          example: 0.50
        stock_quantity:
          type: integer
          example: 1500
        in_stock:
          type: boolean
          example: true
        bulk_pricing_enabled:
          type: boolean
          example: true
        bulk_tiers:
          type: array
          items:
            type: object
            properties:
              min_quantity:
                type: integer
              price:
                type: number
                format: float

    Order:
      type: object
      properties:
        id:
          type: integer
        order_number:
          type: string
          example: "ORD-20240101-001"
        product_name:
          type: string
        quantity:
          type: integer
        unit_price:
          type: number
          format: float
        total_amount:
          type: number
          format: float
        status:
          type: string
          enum: [pending, processing, completed, cancelled]
        created_at:
          type: string
          format: date-time

    Transaction:
      type: object
      properties:
        id:
          type: integer
        type:
          type: string
          enum: [deposit, purchase, refund, admin_adjustment]
        amount:
          type: number
          format: float
        balance_after:
          type: number
          format: float
        description:
          type: string
        created_at:
          type: string
          format: date-time

    Deposit:
      type: object
      properties:
        id:
          type: integer
        amount:
          type: number
          format: float
        status:
          type: string
          enum: [pending, completed, failed, cancelled]
        payment_method:
          type: string
          enum: [crypto, cryptomus, piprapay]
        payment_url:
          type: string
          format: uri
        created_at:
          type: string
          format: date-time

    CustomMailRequest:
      type: object
      properties:
        id:
          type: integer
        quantity:
          type: integer
        fulfilled_quantity:
          type: integer
        total_cost:
          type: number
          format: float
        status:
          type: string
          enum: [pending, processing, completed, cancelled]
        pattern:
          type: string
        custom_domain:
          type: string
        created_at:
          type: string
          format: date-time

    ApiKey:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        key_preview:
          type: string
          example: "bm_****abc123"
        last_used_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time

    Stock:
      type: object
      properties:
        product_id:
          type: integer
        product_name:
          type: string
        sku:
          type: string
        stock_count:
          type: integer
        in_stock:
          type: boolean
        price:
          type: number
          format: float

  responses:
    Unauthorized:
      description: Authentication required or invalid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            success: false
            error: "Invalid or missing authentication"

    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            success: false
            error: "Resource not found"

    ValidationError:
      description: Validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            success: false
            error: "Validation failed"
            errors:
              field_name: ["Error message"]

security:
  - ApiKeyAuth: []

paths:
  # ============ USER ============
  /user/profile:
    get:
      tags: [User]
      summary: Get user profile
      responses:
        '200':
          description: User profile
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    $ref: '#/components/schemas/User'
        '401':
          $ref: '#/components/responses/Unauthorized'

    put:
      tags: [User]
      summary: Update user profile
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                email:
                  type: string
                  format: email
      responses:
        '200':
          description: Profile updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: "Profile updated successfully"
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'

  /user/password:
    post:
      tags: [User]
      summary: Change password
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [current_password, new_password, new_password_confirmation]
              properties:
                current_password:
                  type: string
                new_password:
                  type: string
                  minLength: 8
                new_password_confirmation:
                  type: string
      responses:
        '200':
          description: Password changed
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'

  /user/dashboard:
    get:
      tags: [User]
      summary: Get dashboard statistics
      responses:
        '200':
          description: Dashboard data
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: object
                    properties:
                      balance:
                        type: number
                        format: float
                      total_orders:
                        type: integer
                      total_spent:
                        type: number
                        format: float
                      pending_orders:
                        type: integer
                      recent_orders:
                        type: array
                        items:
                          $ref: '#/components/schemas/Order'
                      recent_transactions:
                        type: array
                        items:
                          $ref: '#/components/schemas/Transaction'
        '401':
          $ref: '#/components/responses/Unauthorized'

  # ============ PRODUCTS ============
  /products:
    get:
      tags: [Products]
      summary: List all products
      security: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: per_page
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
        - name: search
          in: query
          schema:
            type: string
        - name: in_stock
          in: query
          schema:
            type: boolean
        - name: sort
          in: query
          schema:
            type: string
            enum: [name, price, stock_quantity, created_at]
        - name: order
          in: query
          schema:
            type: string
            enum: [asc, desc]
            default: asc
      responses:
        '200':
          description: List of products
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Product'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'

  /products/{id}:
    get:
      tags: [Products]
      summary: Get product details
      security: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Product details
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    $ref: '#/components/schemas/Product'
        '404':
          $ref: '#/components/responses/NotFound'

  /products/{id}/calculate-price:
    post:
      tags: [Products]
      summary: Calculate price with bulk discount
      security: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [quantity]
              properties:
                quantity:
                  type: integer
                  minimum: 1
      responses:
        '200':
          description: Price calculation
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: object
                    properties:
                      quantity:
                        type: integer
                      unit_price:
                        type: number
                        format: float
                      total_price:
                        type: number
                        format: float
                      discount_applied:
                        type: boolean
                      savings:
                        type: number
                        format: float

  # ============ ORDERS ============
  /orders:
    get:
      tags: [Orders]
      summary: List user orders
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: per_page
          in: query
          schema:
            type: integer
            default: 20
        - name: status
          in: query
          schema:
            type: string
            enum: [pending, processing, completed, cancelled]
      responses:
        '200':
          description: List of orders
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Order'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'

    post:
      tags: [Orders]
      summary: Create new order (purchase)
      description: |
        Purchase products using wallet balance. Stock is delivered immediately upon successful purchase.
        
        **Error Codes:**
        - `INSUFFICIENT_BALANCE` - Not enough balance
        - `INSUFFICIENT_STOCK` - Not enough stock available
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [product_id, quantity]
              properties:
                product_id:
                  type: integer
                quantity:
                  type: integer
                  minimum: 1
      responses:
        '201':
          description: Order created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: "Order placed successfully"
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      order_number:
                        type: string
                      quantity:
                        type: integer
                      total_amount:
                        type: number
                        format: float
                      status:
                        type: string
                      stock_items:
                        type: array
                        items:
                          type: string
                        description: "Array of email:password strings"
        '400':
          description: Bad request (insufficient balance/stock)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /orders/{id}:
    get:
      tags: [Orders]
      summary: Get order details
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Order details with stock items
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: object
                    allOf:
                      - $ref: '#/components/schemas/Order'
                      - type: object
                        properties:
                          stock_items:
                            type: array
                            items:
                              type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /orders/{id}/cancel:
    post:
      tags: [Orders]
      summary: Cancel order
      description: Only pending orders can be cancelled. Refund will be credited to wallet.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Order cancelled
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: "Order cancelled successfully"
                  data:
                    type: object
                    properties:
                      refunded_amount:
                        type: number
                        format: float
        '400':
          description: Order cannot be cancelled
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /orders/{id}/export:
    get:
      tags: [Orders]
      summary: Export order stock
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
        - name: format
          in: query
          schema:
            type: string
            enum: [txt, csv, json]
            default: txt
      responses:
        '200':
          description: Exported data
          content:
            text/plain:
              schema:
                type: string
            text/csv:
              schema:
                type: string
            application/json:
              schema:
                type: object
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  # ============ WALLET ============
  /wallet/balance:
    get:
      tags: [Wallet]
      summary: Get wallet balance
      responses:
        '200':
          description: Current balance
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: object
                    properties:
                      balance:
                        type: number
                        format: float
                        example: 150.50
                      currency:
                        type: string
                        example: "USD"
        '401':
          $ref: '#/components/responses/Unauthorized'

  /wallet/transactions:
    get:
      tags: [Wallet]
      summary: List transactions
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: per_page
          in: query
          schema:
            type: integer
            default: 20
        - name: type
          in: query
          schema:
            type: string
            enum: [deposit, purchase, refund, admin_adjustment]
        - name: date_from
          in: query
          schema:
            type: string
            format: date
        - name: date_to
          in: query
          schema:
            type: string
            format: date
      responses:
        '200':
          description: Transaction list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Transaction'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /wallet/deposit:
    post:
      tags: [Wallet]
      summary: Create deposit
      description: |
        Create a deposit using available payment gateways.
        
        **Payment Methods:**
        - `heleket` or `crypto` - Heleket crypto gateway
        - `cryptomus` - Cryptomus gateway
        - `piprapay` - PipraPay USD gateway
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [amount, gateway]
              properties:
                amount:
                  type: number
                  format: float
                  minimum: 0.40
                  description: Amount in USD
                gateway:
                  type: string
                  enum: [heleket, crypto, cryptomus, piprapay]
                  description: Payment gateway
                currency:
                  type: string
                  default: "USDT"
                  description: Crypto currency (for crypto gateways)
            examples:
              heleket:
                summary: Heleket Crypto
                value:
                  amount: 50
                  gateway: "heleket"
              cryptomus:
                summary: Cryptomus
                value:
                  amount: 100
                  gateway: "cryptomus"
              piprapay:
                summary: PipraPay USD
                value:
                  amount: 25
                  gateway: "piprapay"
      responses:
        '200':
          description: Deposit created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: object
                    properties:
                      deposit_id:
                        type: integer
                      amount:
                        type: number
                        format: float
                      payment_method:
                        type: string
                      status:
                        type: string
                      payment_url:
                        type: string
                        format: uri
                        description: Redirect user to this URL to complete payment
                      expires_at:
                        type: string
                        format: date-time
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'

  /wallet/deposits:
    get:
      tags: [Wallet]
      summary: List deposits
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: per_page
          in: query
          schema:
            type: integer
            default: 20
        - name: status
          in: query
          schema:
            type: string
            enum: [pending, completed, failed, cancelled]
      responses:
        '200':
          description: Deposit list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Deposit'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /wallet/deposits/{id}/status:
    get:
      tags: [Wallet]
      summary: Check deposit status
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Deposit status
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      status:
                        type: string
                      amount:
                        type: number
                        format: float
                      payment_method:
                        type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  # ============ STOCK ============
  /stock:
    get:
      tags: [Stock]
      summary: Get all stock
      description: Real-time stock for all products
      security: []
      responses:
        '200':
          description: Stock list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Stock'
                  timestamp:
                    type: string
                    format: date-time

  /stock/{product_id}:
    get:
      tags: [Stock]
      summary: Get product stock
      security: []
      parameters:
        - name: product_id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Product stock
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    $ref: '#/components/schemas/Stock'
        '404':
          $ref: '#/components/responses/NotFound'

  /stock/check:
    get:
      tags: [Stock]
      summary: Bulk check stock
      description: Check stock for multiple products at once
      security: []
      parameters:
        - name: ids
          in: query
          required: true
          schema:
            type: string
          description: Comma-separated product IDs (max 50)
          example: "1,2,3,4,5"
      responses:
        '200':
          description: Stock for requested products
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        product_id:
                          type: integer
                        stock_count:
                          type: integer
                        in_stock:
                          type: boolean

  /stock/categories:
    get:
      tags: [Stock]
      summary: Stock by categories
      security: []
      responses:
        '200':
          description: Stock grouped by category
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        category:
                          type: string
                        total_products:
                          type: integer
                        total_stock:
                          type: integer

  # ============ API KEYS ============
  /api-keys:
    get:
      tags: [API Keys]
      summary: List API keys
      responses:
        '200':
          description: API key list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ApiKey'
        '401':
          $ref: '#/components/responses/Unauthorized'

    post:
      tags: [API Keys]
      summary: Create API key
      description: Create a new API key. Maximum 5 keys per user. **The key is only shown once!**
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  maxLength: 100
      responses:
        '201':
          description: API key created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: "Save this API key securely. It won't be shown again."
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      name:
                        type: string
                      api_key:
                        type: string
                        description: Full API key (shown only once)
        '400':
          description: Maximum keys reached
        '401':
          $ref: '#/components/responses/Unauthorized'

  /api-keys/{id}:
    delete:
      tags: [API Keys]
      summary: Revoke API key
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: API key revoked
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: "API key revoked successfully"
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
