openapi: 3.1.0
info:
  title: NFTPAYROLL Servis API'si
  version: "1.0.0"
  summary: B2B bordro aktarımı ve NFT mint hattı
  description: |
    NFTPAYROLL Servis API'si, bordro verilerini platforma aktarmanız ve her bordronun
    IPFS'e yüklenip Sepolia ağında NFT olarak mint edilmesini tetiklemeniz için
    tasarlanmış sunucudan-sunucuya (B2B) bir API'dir.

    ## Kimlik Doğrulama
    Her istek bir **servis API anahtarı** ile yetkilendirilir. Anahtarınızı her
    istekte `X-API-Key` header'ı ile gönderin:

    ```
    X-API-Key: sk_live_xxxxxxxxxxxxxxxxxxxx
    ```

    `Authorization: Bearer <key>` formatı da kabul edilir. Her anahtar tek bir
    şirkete (tenant) bağlıdır; gönderdiğiniz tüm kayıtlar o şirketin altında oluşturulur.

    ## Rate Limit
    Varsayılan limit **dakikada 60 istektir**. Limit aşıldığında `429 Too Many
    Requests` döner. Anahtar bazında limit yükseltmek için bizimle iletişime geçin.

    ## İş Akışı
    Bordro aktarmanın iki yolu vardır:

    1. **Direkt aktarım** (`POST /payrolls/bulk`) — kayıtlar doğrulanır, oluşturulur
       ve hemen mint kuyruğuna alınır.
    2. **Review → Submit** (`POST /payrolls/review-batches` → `.../submit`) —
       kayıtlar önce doğrulanıp önizlenir (henüz oluşturulmaz). Sonucu kontrol
       ettikten sonra `submit` ile kalıcı hale getirip mint kuyruğuna alırsınız.
       Yüksek hacimli veya onay gerektiren entegrasyonlar için önerilir.

    ## İdempotans (external_ref)
    Tekrarlı kayıtları engellemek için `payroll.external_ref` gönderin. Göndermezseniz
    sistem `PR-{companyId}-{tcKimlik}-{YYYYAA}-{belgeNo}` formatında otomatik bir
    referans üretir. Aynı `external_ref` ikinci kez gönderildiğinde kayıt atlanır
    (`skipped` / `duplicate`).
  contact:
    name: NFTPAYROLL Entegrasyonlar
    email: developers@nftpayroll.ai

servers:
  - url: http://127.0.0.1:8000/api/service/v1
    description: Yerel geliştirme
  - url: https://api.nftpayroll.ai/api/service/v1
    description: Production

security:
  - ApiKeyAuth: []

tags:
  - name: Bordrolar
    description: Bordro aktarımı ve batch durum sorgulama
  - name: Review Batch'leri
    description: Önizle → doğrula → submit akışı

paths:
  /payrolls/bulk:
    post:
      tags: [Bordrolar]
      operationId: bulkStorePayrolls
      summary: Bordroları toplu aktar
      description: |
        Bir bordro listesini doğrular, oluşturur ve her birini NFT mint kuyruğuna
        alır. Çalışan `national_id` ile eşleştirilir; bulunamazsa otomatik oluşturulur
        (bunun için `name`+`surname` ya da `full_name` zorunludur). Kayıt bazlı
        sonuçlar `created` ve `failed` dizilerinde döner; bir kaydın hatalı olması
        diğerlerini durdurmaz.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BulkPayrollRequest"
            examples:
              singleItem:
                summary: Tek bordro
                value:
                  external_batch_ref: ACME-2026-06
                  items:
                    - employee:
                        national_id: "11111111111"
                        name: Ayşe
                        surname: Yılmaz
                        employee_code: EMP-001
                        position: Yazılım Mühendisi
                        department: Mühendislik
                        wallet_address: "0x1111111111111111111111111111111111111111"
                      payroll:
                        period_start: "2026-06-01"
                        period_end: "2026-06-30"
                        payment_date: "2026-07-05"
                        currency: TRY
                        gross_salary: 95000
                        net_salary: 71250
                        bonus: 5000
                        deductions_total: 23750
                        payroll_document_no: BORDRO-2026-06-001
                        employer_sign_name: Mehmet Demir
                        employer_sign_title: İK Direktörü
                        external_ref: PR-ACME-001
      responses:
        "200":
          description: Aktarım tamamlandı (kısmi başarı olabilir)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BulkPayrollResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationError"
        "429":
          $ref: "#/components/responses/RateLimited"

  /payrolls/batches/{externalBatchRef}:
    get:
      tags: [Bordrolar]
      operationId: getBatchStatus
      summary: Batch durumunu sorgula
      description: |
        Bir `external_batch_ref` altındaki tüm bordroların mint durumunu döner. Her
        kaydın blockchain işlem hash'i (`tx_hash`), NFT `token_id` ve `ipfs_cid`
        değerleri mint tamamlandıkça doldurulur.
      parameters:
        - name: externalBatchRef
          in: path
          required: true
          description: Aktarım sırasında verdiğiniz (veya sistemin ürettiği) batch referansı
          schema:
            type: string
          example: ACME-2026-06
      responses:
        "200":
          description: Batch durumu
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchStatusResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"

  /payrolls/review-batches:
    post:
      tags: [Review Batch'leri]
      operationId: createReviewBatch
      summary: Review batch oluştur (önizleme)
      description: |
        Kayıtları doğrular ve bir önizleme (`review`) batch'i oluşturur — bordrolar
        henüz **oluşturulmaz**. Her kaydın durumu `ready`, `duplicate` veya `failed`
        olarak döner. Sonucu kontrol ettikten sonra `submit` endpoint'i ile kalıcı
        hale getirin.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BulkPayrollRequest"
      responses:
        "201":
          description: Review batch oluşturuldu
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReviewRun"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationError"
        "429":
          $ref: "#/components/responses/RateLimited"
    get:
      tags: [Review Batch'leri]
      operationId: listReviewBatches
      summary: Review batch'leri listele
      description: Şirketinize ait son 100 review batch'ini döner (öğe detayları olmadan).
      responses:
        "200":
          description: Review batch listesi
          content:
            application/json:
              schema:
                type: object
                properties:
                  company_id:
                    type: integer
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/ReviewRun"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/RateLimited"

  /payrolls/review-batches/{runId}:
    get:
      tags: [Review Batch'leri]
      operationId: getReviewBatch
      summary: Review batch detayını getir
      description: Tek bir review batch'i tüm kayıt detaylarıyla (`items`) birlikte döner.
      parameters:
        - name: runId
          in: path
          required: true
          schema:
            type: integer
          example: 4821
      responses:
        "200":
          description: Review batch detayı
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReviewRun"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"

  /payrolls/review-batches/{runId}/submit:
    post:
      tags: [Review Batch'leri]
      operationId: submitReviewBatch
      summary: Review batch'i submit et
      description: |
        Daha önce oluşturulmuş bir review batch'indeki kayıtları kalıcı bordrolara
        çevirir ve mint kuyruğuna alır. `ready` kayıtlar oluşturulur; `duplicate`
        olanlar atlanır. Aynı batch ikinci kez submit edilemez (`409`).
      parameters:
        - name: runId
          in: path
          required: true
          schema:
            type: integer
          example: 4821
      responses:
        "200":
          description: Submit tamamlandı
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReviewSubmitResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          description: Batch zaten submit edilmiş
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          $ref: "#/components/responses/ValidationError"
        "429":
          $ref: "#/components/responses/RateLimited"

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: "Servis API anahtarı. `Authorization: Bearer <key>` formatı da kabul edilir."

  schemas:
    Employee:
      type: object
      required: [national_id]
      properties:
        national_id:
          type: string
          description: 11 haneli TC kimlik numarası
          minLength: 11
          maxLength: 11
          example: "11111111111"
        name:
          type: string
          description: Ad (yeni çalışanda surname ile birlikte ya da full_name zorunlu)
          example: Ayşe
        surname:
          type: string
          example: Yılmaz
        full_name:
          type: string
          description: name+surname yerine tek alan olarak ad soyad
          example: Ayşe Yılmaz
        employee_code:
          type: string
          maxLength: 50
          example: EMP-001
        position:
          type: string
          maxLength: 100
          example: Yazılım Mühendisi
        department:
          type: string
          maxLength: 100
          example: Mühendislik
        start_date:
          type: string
          format: date
          example: "2024-01-15"
        status:
          type: string
          enum: [active, inactive]
          default: active
        wallet_address:
          type: string
          description: Sepolia/Ethereum cüzdan adresi. Boşsa varsayılan mint cüzdanı kullanılır.
          pattern: "^0x[0-9a-fA-F]{40}$"
          example: "0x1111111111111111111111111111111111111111"

    PayrollData:
      type: object
      required: [period_start, period_end, currency, gross_salary, net_salary, payroll_document_no]
      properties:
        period_start:
          type: string
          format: date
          example: "2026-06-01"
        period_end:
          type: string
          format: date
          description: period_start'tan büyük veya eşit olmalı
          example: "2026-06-30"
        payment_date:
          type: string
          format: date
          example: "2026-07-05"
        currency:
          type: string
          maxLength: 10
          default: TRY
          example: TRY
        gross_salary:
          type: number
          example: 95000
        net_salary:
          type: number
          example: 71250
        bonus:
          type: number
          example: 5000
        deductions_total:
          type: number
          example: 23750
        employer_sign_name:
          type: string
          maxLength: 255
          example: Mehmet Demir
        employer_sign_title:
          type: string
          maxLength: 255
          example: İK Direktörü
        payroll_document_no:
          type: string
          maxLength: 100
          example: BORDRO-2026-06-001
        batch_id:
          type: string
          maxLength: 100
        external_ref:
          type: string
          description: |
            İdempotans anahtarı. Format: büyük harf/rakam ile başlayıp `._:-`
            karakterlerini içerebilir (6-100 karakter). Boş bırakılırsa otomatik üretilir.
          maxLength: 100
          example: PR-ACME-001

    PayrollItem:
      type: object
      required: [employee, payroll]
      properties:
        employee:
          $ref: "#/components/schemas/Employee"
        payroll:
          $ref: "#/components/schemas/PayrollData"

    BulkPayrollRequest:
      type: object
      required: [items]
      properties:
        payroll_group_id:
          type: string
          format: uuid
          description: Opsiyonel grup kimliği. Verilmezse otomatik UUID üretilir.
        external_batch_ref:
          type: string
          description: Batch'i sonradan sorgulamak için referans. Verilmezse otomatik üretilir.
          example: ACME-2026-06
        items:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/PayrollItem"

    BulkPayrollResponse:
      type: object
      properties:
        bulk_operation_run_id:
          type: integer
        company_id:
          type: integer
        payroll_group_id:
          type: string
          format: uuid
        external_batch_ref:
          type: string
        created_count:
          type: integer
        created_employees_count:
          type: integer
        created_employees:
          type: array
          items:
            type: object
            properties:
              id: { type: integer }
              national_id: { type: string }
              name: { type: string }
              surname: { type: string }
        failed_count:
          type: integer
        created:
          type: array
          description: Oluşturulan bordro kayıtları
          items:
            type: object
            additionalProperties: true
        failed:
          type: array
          items:
            type: object
            properties:
              index: { type: integer }
              errors:
                type: array
                items: { type: string }
              data:
                type: object
                additionalProperties: true

    BatchStatusResponse:
      type: object
      properties:
        company_id:
          type: integer
        external_batch_ref:
          type: string
        summary:
          type: object
          properties:
            total: { type: integer }
            pending: { type: integer }
            queued: { type: integer }
            sent: { type: integer }
            failed: { type: integer }
        items:
          type: array
          items:
            type: object
            properties:
              id: { type: integer }
              employee_id: { type: integer }
              external_ref: { type: string }
              status:
                type: string
                description: Mint durumu
                enum: [pending, queued, sent, failed]
              tx_hash:
                type: [string, "null"]
                description: Sepolia işlem hash'i (mint tamamlanınca dolar)
              token_id:
                type: [string, "null"]
              ipfs_cid:
                type: [string, "null"]

    ReviewItem:
      type: object
      properties:
        index: { type: integer }
        status:
          type: string
          enum: [ready, duplicate, failed]
        reason:
          type: [string, "null"]
        external_ref:
          type: string
        employee:
          type: object
          properties:
            national_id: { type: string }
            full_name: { type: string }
            employee_code: { type: [string, "null"] }
        payroll:
          type: object
          properties:
            period_start: { type: string, format: date }
            period_end: { type: string, format: date }
            payment_date: { type: [string, "null"], format: date }
            currency: { type: string }
            gross_salary: { type: number }
            net_salary: { type: number }
            payroll_document_no: { type: string }
        submit_status:
          type: string
          description: Yalnızca submit sonrası dolar
          enum: [success, failed, skipped]

    ReviewRun:
      type: object
      properties:
        id: { type: integer }
        company_id: { type: integer }
        type: { type: string, example: service_payroll_review }
        name: { type: string }
        status:
          type: string
          example: completed
        stage:
          type: string
          enum: [review_pending, submitted]
        payroll_group_id: { type: [string, "null"] }
        external_batch_ref: { type: [string, "null"] }
        submitted_at: { type: [string, "null"], format: date-time }
        total_items: { type: integer }
        processed_items: { type: integer }
        failed_items: { type: integer }
        summary:
          type: object
          properties:
            total: { type: integer }
            ready: { type: integer }
            duplicate: { type: integer }
            failed: { type: integer }
            submitted_success: { type: integer }
            submitted_failed: { type: integer }
            submitted_skipped: { type: integer }
        started_at: { type: [string, "null"], format: date-time }
        finished_at: { type: [string, "null"], format: date-time }
        created_at: { type: [string, "null"], format: date-time }
        items:
          type: array
          description: Yalnızca detay endpoint'inde döner
          items:
            $ref: "#/components/schemas/ReviewItem"

    ReviewSubmitResponse:
      type: object
      properties:
        message: { type: string }
        created_count: { type: integer }
        failed_count: { type: integer }
        created_employees_count: { type: integer }
        created_employees:
          type: array
          items:
            type: object
            properties:
              id: { type: integer }
              national_id: { type: string }
              name: { type: string }
              surname: { type: string }
        run:
          $ref: "#/components/schemas/ReviewRun"

    Error:
      type: object
      properties:
        message:
          type: string
          example: Invalid service API key.

  responses:
    Unauthorized:
      description: API anahtarı eksik veya geçersiz
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            message: Invalid service API key.
    NotFound:
      description: Kayıt bulunamadı
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            message: Batch not found.
    ValidationError:
      description: Geçersiz istek gövdesi
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            message: items alanı zorunlu ve dizi olmalıdır.
    RateLimited:
      description: Rate limit aşıldı (dakikada 60 istek)
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            message: Too Many Attempts.
