# UTF-8. 若瀏覽器仍顯示亂碼，請改用 JSON 版：einvoice-api-openapi.json
openapi: 3.0.3
info:
  title: e首發票 API
  description: |
    e首發票電子發票加值中心 API：開立發票（發票版）、作廢發票、折讓電子發票。
    適用情境：電商、ERP、POS、機台／繳款設備。取號策略請參閱情境選型（發票版 vs 訂單版）。
  version: "2025.03"
  contact:
    name: e首發票服務
  license:
    name: 依服務合約

servers:
  - url: https://webapi.systemlead.com/terpapi
    description: 測試環境（實際正式環境依服務商管理後台取得）
  - url: https://webapi.systemlead.com/terpapi
    description: 正式環境（URL 依合約提供）

tags:
  - name: 開立
    description: 發票版開立（營業人系統取號後送交）
  - name: 作廢
    description: 作廢已開立發票
  - name: 折讓
    description: 折讓電子發票

# 簽章於請求體內：CompanyID + Timestamp + Signature（SHA256），見各 path 與 components.schemas
paths:
  /Append/Invoices:
    post:
      tags: [開立]
      summary: 開立發票（發票版）
      description: |
        發票版開立：由營業人系統取號後，帶發票號與開立資料送交。
        - 請求/回應：JSON
        - 簽章：SHA256（統編 + 加密KEY + 時間序），有過期時間請注意。
        - C 類 48 小時、B 類 7 天內需完成上傳，逾期將依稽核規定處理。
      operationId: appendInvoices
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InvoiceIssueRequest"
            examples:
              B2B:
                $ref: "#/components/examples/B2BInvoice"
              B2C:
                $ref: "#/components/examples/B2CInvoice"
      responses:
        "200":
          description: 開立結果（成功或失敗皆回傳）
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceIssueResponse"
        "4xx":
          description: 請求錯誤或簽章驗證失敗
        "5xx":
          description: 伺服器錯誤

  /Update/CancelInvoices:
    post:
      tags: [作廢]
      summary: 作廢發票
      description: |
        作廢已開立之發票。支援單筆或批次（Data 為陣列）。
        - 簽章：SHA256
      operationId: cancelInvoices
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CancelInvoiceRequest"
      responses:
        "200":
          description: 作廢結果（單筆或批次陣列）
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/CancelInvoiceResponse"
                  - type: array
                    items:
                      $ref: "#/components/schemas/CancelInvoiceResponse"

  /Update/AllowanceInvoice:
    post:
      tags: [折讓]
      summary: 折讓電子發票
      description: |
        對電子發票開立折讓。B2B/B2C、應稅／免稅／零稅／混稅（混稅僅 C 類，且僅允許 1 應稅、3 免稅）。
        - 稅額與總計需符合檢核（應稅 5%、單頭與單身稅額一致等）。
      operationId: allowanceInvoice
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AllowanceInvoiceRequest"
      responses:
        "200":
          description: 折讓結果
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AllowanceInvoiceResponse"

components:
  securitySchemes:
    SignatureInBody:
      type: apiKey
      in: header
      name: X-API-Key
      description: |
        實際驗證在請求體內：CompanyID、Timestamp、Signature（SHA256）。
        計算方式：統編與加密 KEY、時間序計算；過期時間請注意，勿重複使用同一 Timestamp 提交多筆。
        若僅檢視規格，可忽略此項；實作時請依服務商文件以 body 內簽章驗證為準。

  schemas:
    # ---- 共通：簽章包裝 ----
    SignedPayload:
      type: object
      required:
        - CompanyID
        - Timestamp
        - Signature
      properties:
        CompanyID:
          type: string
          maxLength: 8
          description: 賣方統編（統一編號）
        Timestamp:
          type: string
          description: 時間序（用於簽章計算，有過期時間）
        Signature:
          type: string
          description: SHA256 簽章

    # ---- 開立發票 ----
    InvoiceIssueRequest:
      allOf:
        - $ref: "#/components/schemas/SignedPayload"
        - type: object
          required: [Data]
          properties:
            Data:
              $ref: "#/components/schemas/InvoiceData"

    InvoiceData:
      type: object
      required:
        - InvoiceID
        - InvoiceDateTime
        - InvoiceFor
        - BuyerID
        - BuyerName
        - BuyerEmailAddress
        - TaxType
        - SalesAmount
        - TaxAmount
        - TotalAmount
        - Details
      properties:
        InvoiceID:
          type: string
          maxLength: 10
          description: 發票字軌號碼（字母+數字），分批傳入避免重複
        InvoiceDateTime:
          type: string
          format: date-time
          example: "2025-03-17T19:43:37"
          description: 開立日期時間，YYYY-MM-DDTHH:mm:ss
        BillingNo:
          type: string
          maxLength: 30
          description: 訂單編號，需唯一
        InvoiceFor:
          type: string
          enum: [B, C]
          description: "B=B2B, C=B2C"
        BuyerID:
          type: string
          maxLength: 10
          description: 買方統編，B2C 固定 0000000000
        BuyerInvoiceTitle:
          type: string
          maxLength: 60
          description: 買方抬頭（B2B 必填）
        BuyerName:
          type: string
          maxLength: 60
          description: 買受人名稱
        BuyerTelNo:
          type: string
          maxLength: 26
          description: 買方電話，簡訊通知用
        BuyerEmailAddress:
          type: string
          maxLength: 80
          description: 買方電子郵件，格式錯誤將清空並可能列入黑名單
        CheckNumber:
          type: string
          maxLength: 4
          description: 發票檢查碼，B2C 固定 9999
        RandomNumber:
          type: string
          maxLength: 4
          description: 發票隨機碼，B2C 固定 9999
        PrintMark:
          type: string
          enum: [Y, N]
          description: C 類證明聯已列印標記
        TaxType:
          type: string
          enum: ["1", "2", "3", "9"]
          description: "1=應稅, 2=零稅, 3=免稅, 9=混稅（僅 C 類）"
        SalesAmount:
          type: integer
          description: 應稅銷售額（四捨五入整數）
        FreeTaxSalesAmount:
          type: integer
          default: 0
        ZeroTaxSalesAmount:
          type: integer
          default: 0
        TaxAmount:
          type: integer
          description: 稅額，B 類為 SalesAmount*5% 四捨五入，C 類為 0
        TotalAmount:
          type: integer
          description: 含稅總金額
        CarrierType:
          type: string
          example: "3J0002"
          description: 載具類型，如手機條碼
        CarrierId:
          type: string
          maxLength: 64
          description: 載具編號，手機條碼為 / + 7 碼
        NPOBAN:
          type: string
          minLength: 3
          maxLength: 10
          description: 愛心碼（捐贈）
        Note:
          type: string
          maxLength: 250
          description: 系統紀錄，可含 cc*email*cc* 格式 CC 收件者
        Details:
          type: array
          items:
            $ref: "#/components/schemas/InvoiceDetailItem"

    InvoiceDetailItem:
      type: object
      required:
        - DetailID
        - ProductName
        - Quantity
        - UnitPrice
        - SubTotal
        - ItemTaxType
      properties:
        DetailID:
          type: string
          maxLength: 4
          example: "0001"
        ProductName:
          type: string
          maxLength: 256
        Quantity:
          type: number
          format: double
        UnitPrice:
          type: number
          format: double
        SubTotal:
          type: number
          format: double
          description: Quantity × UnitPrice
        ItemTaxType:
          type: string
          enum: ["1", "2", "3"]
          description: "1=應稅, 2=零稅, 3=免稅"

    InvoiceIssueResponse:
      type: object
      properties:
        InvoiceID:
          type: string
        InvoiceNumber:
          type: string
        InvoiceDateTime:
          type: string
          format: date-time
        StatusCode:
          type: integer
          description: "1=成功, 0=失敗"
        ResultMessage:
          type: string
          description: 成功或錯誤原因（開立失敗時請依此處理，避免漏開）

    # ---- 作廢發票 ----
    CancelInvoiceRequest:
      allOf:
        - $ref: "#/components/schemas/SignedPayload"
        - type: object
          required: [Data]
          properties:
            Data:
              oneOf:
                - $ref: "#/components/schemas/CancelInvoiceData"
                - type: array
                  items:
                    $ref: "#/components/schemas/CancelInvoiceData"

    CancelInvoiceData:
      type: object
      required:
        - SellerID
        - InvoiceNumber
        - CancelDate
        - CancelReason
      properties:
        SellerID:
          type: string
          maxLength: 8
        InvoiceNumber:
          type: string
          maxLength: 10
          description: 要作廢的發票號碼
        CancelDate:
          type: string
          format: date-time
          example: "2024-12-13T14:30:00"
        CancelReason:
          type: string
          maxLength: 20
          description: 作廢原因，如「退貨」「交易取消」

    CancelInvoiceResponse:
      type: object
      properties:
        SellerID:
          type: string
        InvoiceNumber:
          type: string
        StatusCode:
          type: integer
          description: "1=成功, 0=失敗"
        ResultMessage:
          type: string

    # ---- 折讓 ----
    AllowanceInvoiceRequest:
      allOf:
        - $ref: "#/components/schemas/SignedPayload"
        - type: object
          required: [Data]
          properties:
            Data:
              $ref: "#/components/schemas/AllowanceData"

    AllowanceData:
      type: object
      required:
        - AllowanceNumberPrefix
        - SellerID
        - BuyerID
        - AllowanceType
        - TaxAmount
        - TotalAmount
        - Details
      properties:
        AllowanceNumberPrefix:
          type: string
          maxLength: 16
          description: 折讓單號，不可重複，建議時間序流水號
        SellerID:
          type: string
          maxLength: 8
        BuyerID:
          type: string
          maxLength: 10
          description: B2B 為統編，B2C 為 0000000000
        AllowanceType:
          type: string
          enum: ["2"]
          description: 固定 2（賣方傳送）
        TaxAmount:
          type: number
          description: 折讓稅額，應為應稅明細 Tax 加總
        TotalAmount:
          type: number
          description: 折讓總額，應為所有明細 Amount 加總
        Details:
          type: array
          items:
            $ref: "#/components/schemas/AllowanceDetailItem"

    AllowanceDetailItem:
      type: object
      required:
        - InvoiceNumber
        - SequenceNumber
        - Amount
        - Quantity
        - UnitPrice
        - ItemTaxType
        - Tax
      properties:
        InvoiceNumber:
          type: string
          maxLength: 10
          description: 原發票號碼（字軌+8碼數字）
        SequenceNumber:
          type: string
          maxLength: 4
          example: "0001"
        Amount:
          type: number
        Quantity:
          type: number
        UnitPrice:
          type: number
        ItemTaxType:
          type: string
          enum: ["1", "2", "3"]
        Tax:
          type: number
          description: ItemTaxType=1 時為 Amount*0.05 四捨五入

    AllowanceInvoiceResponse:
      type: object
      properties:
        SellerID:
          type: string
        AllowanceNumber:
          type: string
        StatusCode:
          type: integer
          description: "1=成功, 0=失敗"
        ResultMessage:
          type: string

  examples:
    B2BInvoice:
      value:
        CompanyID: "12345678"
        Timestamp: "1677585655"
        Signature: "4754A5FE41749DE99E82FC5502348C69781E1026064ADF7AC"
        Data:
          InvoiceID: "NX25780488"
          InvoiceDateTime: "2025-03-17T19:43:37"
          InvoiceFor: "B"
          BuyerID: "87654321"
          BuyerName: "企業買家"
          BuyerEmailAddress: "einv@einv.tw"
          TaxType: "1"
          SalesAmount: 5000
          FreeTaxSalesAmount: 0
          ZeroTaxSalesAmount: 0
          TaxAmount: 250
          TotalAmount: 5250
          Details:
            - DetailID: "001"
              ProductName: "商品A"
              ItemTaxType: "1"
              Quantity: 1
              UnitPrice: 5000
              SubTotal: 5000
    B2CInvoice:
      value:
        CompanyID: "12345678"
        Timestamp: "1677585655"
        Signature: "4754A5FE41749DE99E82FC5502348C69781E1026064ADF7AC"
        Data:
          InvoiceID: "NX25780489"
          InvoiceDateTime: "2025-03-17T19:45:00"
          InvoiceFor: "C"
          BuyerID: "0000000000"
          BuyerEmailAddress: "einv@einv.tw"
          TaxType: "1"
          SalesAmount: 2000
          FreeTaxSalesAmount: 0
          ZeroTaxSalesAmount: 0
          TaxAmount: 0
          TotalAmount: 2000
          Details:
            - DetailID: "001"
              ProductName: "商品B"
              ItemTaxType: "1"
              Quantity: 1
              UnitPrice: 2000
              SubTotal: 2000
