openapi: 3.0.3
info:
  title: PSP Gateway Query API
  version: 1.0.0
  description: |
    Query service surface for merchant read operations. Query accepts invoice or
    transfer account secrets for reads, but it does not expose write routes.
    A missing X-API-ID header returns 400 INVALID_REQUEST. A missing X-API-Key
    header or invalid credentials return 401 UNAUTHORIZED. A credential store
    failure returns 500 INTERNAL_ERROR before the request handler runs.
servers:
  - url: https://query-api.psp.example.com
    description: Example host; obtain the actual Query host from PSP
tags:
  - name: Query Payments
  - name: Query Accounts
  - name: Query Cards
  - name: Query Tokenization
security:
  - AccountSecretEd25519: []
paths:
  /api/v1/payments/intents:
    get:
      tags: [Query Payments]
      summary: List invoice payment intents
      parameters:
        - $ref: '#/components/parameters/PaymentFilters'
        - $ref: '#/components/parameters/MerchantOrderID'
        - $ref: '#/components/parameters/StatusFilter'
        - $ref: '#/components/parameters/StateFilter'
        - $ref: '#/components/parameters/CreatedAfter'
        - $ref: '#/components/parameters/CreatedBefore'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Offset'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Payment intent page
          content:
            application/json:
              schema: { $ref: '#/components/schemas/DualModeInvoicePage' }
  /api/v1/payments/intents/{invoice_number}:
    get:
      tags: [Query Payments]
      summary: Get a invoice payment intent
      parameters:
        - $ref: '#/components/parameters/InvoiceNumber'
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Payment intent detail
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Invoice' }
  /api/v1/payments/transfers:
    get:
      tags: [Query Payments]
      summary: List transfer intents
      parameters:
        - $ref: '#/components/parameters/PaymentFilters'
        - $ref: '#/components/parameters/MerchantOrderID'
        - $ref: '#/components/parameters/StatusFilter'
        - $ref: '#/components/parameters/StateFilter'
        - $ref: '#/components/parameters/CreatedAfter'
        - $ref: '#/components/parameters/CreatedBefore'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Offset'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Transfer intent page
          content:
            application/json:
              schema: { $ref: '#/components/schemas/DualModeTransferPage' }
  /api/v1/payments/transfers/{intent_id}:
    get:
      tags: [Query Payments]
      summary: Get a transfer intent
      parameters:
        - $ref: '#/components/parameters/IntentID'
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Transfer intent detail
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Transfer' }
  /api/v1/payments/refunds:
    get:
      tags: [Query Payments]
      summary: List refunds
      parameters:
        - $ref: '#/components/parameters/OriginalIntentID'
        - $ref: '#/components/parameters/PaymentFilters'
        - $ref: '#/components/parameters/StatusFilter'
        - $ref: '#/components/parameters/StateFilter'
        - $ref: '#/components/parameters/CreatedAfter'
        - $ref: '#/components/parameters/CreatedBefore'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Offset'
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Refund page
          content:
            application/json:
              schema: { $ref: '#/components/schemas/RefundPage' }
  /api/v1/payments/refunds/{refund_id}:
    get:
      tags: [Query Payments]
      summary: Get a refund
      parameters:
        - $ref: '#/components/parameters/RefundID'
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Refund detail
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Refund' }
  /api/v1/merchant-accounts:
    get:
      tags: [Query Accounts]
      summary: List merchant accounts
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Merchant account page
  /api/v1/merchant-accounts/{account_id}:
    get:
      tags: [Query Accounts]
      summary: Get a merchant account
      parameters:
        - $ref: '#/components/parameters/AccountID'
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Merchant account detail
  /api/v1/merchant-accounts/{account_id}/balance:
    get:
      tags: [Query Accounts]
      summary: Get merchant account balance
      parameters:
        - $ref: '#/components/parameters/AccountID'
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Balance detail
  /api/v1/merchant-accounts/{account_id}/transactions:
    get:
      tags: [Query Accounts]
      summary: List account transactions
      parameters:
        - $ref: '#/components/parameters/AccountID'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Offset'
      responses:
        '200':
          description: Account transaction page
  /api/v1/merchants/balance:
    get:
      tags: [Query Accounts]
      summary: Get aggregate merchant balance
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Merchant balance list
  /api/v1/cards/authorizations/{authorization_id}:
    get:
      tags: [Query Cards]
      summary: Get credit card authorization
      parameters:
        - $ref: '#/components/parameters/AuthorizationID'
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Authorization detail
  /api/v1/cards/captures/{invoice_id}:
    get:
      tags: [Query Cards]
      summary: Get credit card capture
      parameters:
        - $ref: '#/components/parameters/InvoiceID'
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Capture detail
  /api/v1/cards/refunds:
    get:
      tags: [Query Cards]
      summary: List credit card refunds
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Card refund page
  /api/v1/cards/refunds/{refund_id}:
    get:
      tags: [Query Cards]
      summary: Get credit card refund
      parameters:
        - $ref: '#/components/parameters/RefundID'
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Card refund detail
  /api/v1/tokenization/tokens/{token_id}:
    get:
      tags: [Query Tokenization]
      summary: Get token metadata
      parameters:
        - $ref: '#/components/parameters/TokenID'
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Token detail
  /api/v1/tokenization/customers/{customer_id}/tokens:
    get:
      tags: [Query Tokenization]
      summary: List customer tokens
      parameters:
        - $ref: '#/components/parameters/CustomerID'
      responses:
        '500':
          $ref: '#/components/responses/AuthenticationDependencyFailure'
        '200':
          description: Customer token page
  /api/v1/orders/{order_id}/events:
    get:
      tags: [Query Payments]
      summary: List order events
      parameters:
        - $ref: '#/components/parameters/OrderID'
      responses:
        '200':
          description: Order event list
  /api/v1/orders/{order_id}/notifications:
    get:
      tags: [Query Payments]
      summary: List order notifications
      parameters:
        - $ref: '#/components/parameters/OrderID'
      responses:
        '200':
          description: Order notification list
components:
  securitySchemes:
    AccountSecretEd25519:
      type: apiKey
      in: header
      name: X-API-ID
      description: Query Ed25519 authentication. Invoice and transfer account secrets are accepted for reads only; PSP stores only their public keys.
  parameters:
    PaymentFilters:
      name: payment_method
      in: query
      schema: { type: string }
    OriginalIntentID:
      name: original_intent_id
      in: query
      schema: { type: string, format: uuid }
    MerchantOrderID:
      name: merchant_order_id
      in: query
      schema: { type: string }
    StatusFilter:
      name: status
      in: query
      schema: { type: string }
    StateFilter:
      name: state
      in: query
      schema: { type: string }
    CreatedAfter:
      name: created_after
      in: query
      schema: { type: string, format: date-time }
    CreatedBefore:
      name: created_before
      in: query
      schema: { type: string, format: date-time }
    Limit:
      name: limit
      in: query
      schema: { type: integer, minimum: 1, maximum: 100 }
    Offset:
      name: offset
      in: query
      schema: { type: integer, minimum: 0 }
    Cursor:
      name: cursor
      in: query
      description: Presence selects cursor pagination, including an empty value for the first page. Response contains items, next_cursor, has_more and page_size instead of total.
      schema: { type: string }
    OrderID:
      name: order_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    IntentID:
      name: intent_id
      in: path
      required: true
      schema: { type: string }
    InvoiceNumber:
      name: invoice_number
      in: path
      required: true
      schema: { type: string }
    RefundID:
      name: refund_id
      in: path
      required: true
      schema: { type: string }
    AccountID:
      name: account_id
      in: path
      required: true
      schema: { type: string }
    AuthorizationID:
      name: authorization_id
      in: path
      required: true
      schema: { type: string }
    InvoiceID:
      name: invoice_id
      in: path
      required: true
      schema: { type: string }
    TokenID:
      name: token_id
      in: path
      required: true
      schema: { type: string }
    CustomerID:
      name: customer_id
      in: path
      required: true
      schema: { type: string }
  schemas:
    Invoice:
      type: object
      required: [id, invoice_number, payment_method, amount, currency, status]
      properties:
        id: { type: string, format: uuid }
        invoice_number: { type: string }
        payment_method: { type: string }
        amount: { type: integer, format: int64 }
        currency: { type: string }
        status: { type: string }
        state: { type: string }
        merchant_order_id: { type: string }
        paid_amount: { type: integer, format: int64 }
        refunded_amount: { type: integer, format: int64 }
        payment_url: { type: string }
        psp_checkout_url: { type: string }
        provider_checkout_url: { type: string }
        instruction: { type: object, additionalProperties: true }
        expected_payer_info: { type: object, additionalProperties: true }
        payer_info: { type: object, additionalProperties: true }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
        metadata: { type: object, additionalProperties: true }
    Transfer:
      type: object
      required: [id, transfer_number, payment_method, amount, currency, status]
      properties:
        id: { type: string, format: uuid }
        transfer_number: { type: string }
        payment_method: { type: string }
        amount: { type: integer, format: int64 }
        currency: { type: string }
        status: { type: string }
        state: { type: string }
        merchant_order_id: { type: string }
        beneficiary: { type: object, additionalProperties: true }
        actual_beneficiary: { type: object, additionalProperties: true }
        transferred_amount: { type: integer, format: int64 }
        returned_amount: { type: integer, format: int64 }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
        metadata: { type: object, additionalProperties: true }
    Refund:
      type: object
      required: [id, refund_number, original_intent_id, amount, currency, status]
      properties:
        id: { type: string, format: uuid }
        refund_number: { type: string }
        original_intent_id: { type: string, format: uuid }
        invoice_number: { type: string }
        amount: { type: integer, format: int64 }
        currency: { type: string }
        status: { type: string }
        state: { type: string }
        reason: { type: string }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    DualModeInvoicePage:
      oneOf:
        - $ref: '#/components/schemas/InvoiceOffsetPage'
        - $ref: '#/components/schemas/InvoiceCursorPage'
    InvoiceOffsetPage:
      type: object
      required: [items, total, limit, offset]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Invoice' } }
        total: { type: integer, format: int64 }
        limit: { type: integer }
        offset: { type: integer }
    InvoiceCursorPage:
      type: object
      required: [items, next_cursor, has_more, page_size]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Invoice' } }
        next_cursor: { type: string, nullable: true }
        has_more: { type: boolean }
        page_size: { type: integer }
    DualModeTransferPage:
      oneOf:
        - $ref: '#/components/schemas/TransferOffsetPage'
        - $ref: '#/components/schemas/TransferCursorPage'
    TransferOffsetPage:
      type: object
      required: [items, total, limit, offset]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Transfer' } }
        total: { type: integer, format: int64 }
        limit: { type: integer }
        offset: { type: integer }
    TransferCursorPage:
      type: object
      required: [items, next_cursor, has_more, page_size]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Transfer' } }
        next_cursor: { type: string, nullable: true }
        has_more: { type: boolean }
        page_size: { type: integer }
    RefundPage:
      type: object
      required: [items, total, limit, offset]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Refund' } }
        total: { type: integer, format: int64 }
        limit: { type: integer }
        offset: { type: integer }
  responses:
    AuthenticationDependencyFailure:
      description: Credential lookup failed (INTERNAL_ERROR). The request handler was not executed.
