openapi: 3.1.0
info:
  title: Stock Rush Provider API
  version: "1.0.0"
  description: |
    B2B integration API for the Stock Rush crash game. Operators authenticate
    via API key + HMAC, mint short-lived player sessions, and let the
    Stock Rush back-end handle round lifecycle, wallet debits/credits, and
    real-time event delivery.

    Base URLs:
      - Sandbox:    https://api.sandbox.stockrush.io
      - Production: https://api.stockrush.io

    All money values are decimal strings in minor units of the wallet
    currency (e.g. "100.00" EUR).
  contact:
    name: Stock Rush Integrations
    email: integrations@stockrush.io
servers:
  - url: https://api.sandbox.stockrush.io
    description: Sandbox
  - url: https://api.stockrush.io
    description: Production
security:
  - ApiKeyAuth: []
    HmacAuth: []

tags:
  - name: Sessions
  - name: Wallet
  - name: Rounds
  - name: Fairness
  - name: Webhooks

paths:
  /v1/sessions:
    post:
      tags: [Sessions]
      summary: Mint a player session
      operationId: createSession
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CreateSession" }
      responses:
        "201":
          description: Session created
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Session" }

  /v1/wallets/{walletId}/balance:
    get:
      tags: [Wallet]
      summary: Read wallet balance
      parameters:
        - { in: path, name: walletId, required: true, schema: { type: string } }
      responses:
        "200":
          description: ok
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Balance" }

  /v1/wallets/{walletId}/debit:
    post:
      tags: [Wallet]
      summary: Debit wallet (place bet)
      parameters:
        - { in: path, name: walletId, required: true, schema: { type: string } }
        - { in: header, name: Idempotency-Key, required: true, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/DebitRequest" }
      responses:
        "200": { description: ok, content: { application/json: { schema: { $ref: "#/components/schemas/LedgerEntry" } } } }
        "402": { description: insufficient_funds }
        "409": { description: idempotency_conflict }

  /v1/wallets/{walletId}/credit:
    post:
      tags: [Wallet]
      summary: Credit wallet (cashout / refund)
      parameters:
        - { in: path, name: walletId, required: true, schema: { type: string } }
        - { in: header, name: Idempotency-Key, required: true, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CreditRequest" }
      responses:
        "200": { description: ok, content: { application/json: { schema: { $ref: "#/components/schemas/LedgerEntry" } } } }

  /v1/rounds/current:
    get:
      tags: [Rounds]
      summary: Current round snapshot
      responses:
        "200":
          description: ok
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Round" }

  /v1/rounds/{roundId}:
    get:
      tags: [Rounds]
      parameters:
        - { in: path, name: roundId, required: true, schema: { type: string } }
      responses:
        "200":
          description: ok
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Round" }

  /v1/bets:
    post:
      tags: [Rounds]
      summary: Place a bet
      parameters:
        - { in: header, name: Idempotency-Key, required: true, schema: { type: string } }
        - { in: header, name: X-Session-Id,    required: true, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/PlaceBet" }
      responses:
        "201": { description: bet placed, content: { application/json: { schema: { $ref: "#/components/schemas/Bet" } } } }
        "403": { description: round_locked | self_excluded | kyc_required }
        "402": { description: insufficient_funds }

  /v1/cashout:
    post:
      tags: [Rounds]
      summary: Cash out an active bet
      parameters:
        - { in: header, name: Idempotency-Key, required: true, schema: { type: string } }
        - { in: header, name: X-Session-Id,    required: true, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/Cashout" }
      responses:
        "200":
          description: cashout settled
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CashoutResult" }
        "410": { description: too_late_already_crashed }

  /v1/fairness/{roundId}/verify:
    get:
      tags: [Fairness]
      summary: Verify a round's crash point
      parameters:
        - { in: path, name: roundId, required: true, schema: { type: string } }
      responses:
        "200":
          description: ok
          content:
            application/json:
              schema: { $ref: "#/components/schemas/FairnessProof" }

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-Operator-Api-Key
    HmacAuth:
      type: apiKey
      in: header
      name: X-Signature
      description: |
        `hex(HMAC_SHA256(secret, `${ts}.${method}.${path}.${sha256(body)}`))`
        Header `X-Timestamp` required. ±60s clock skew allowed.

  schemas:
    CreateSession:
      type: object
      required: [playerIdHash, walletId, currency, kycTier, jurisdiction]
      properties:
        playerIdHash: { type: string }
        walletId:     { type: string }
        currency:     { type: string, example: USD }
        kycTier:      { type: string, enum: [none, basic, enhanced, corporate] }
        jurisdiction: { type: string, example: MT }
        selfExcluded: { type: boolean, default: false }
        ttlSeconds:   { type: integer, default: 3600 }
        realityCheckIntervalMs: { type: integer, default: 1800000 }
        lossLimit:    { type: string, nullable: true }
    Session:
      type: object
      properties:
        sessionId: { type: string }
        expiresAt: { type: string, format: date-time }
        wsUrl:     { type: string }
    Balance:
      type: object
      properties:
        walletId: { type: string }
        currency: { type: string }
        available: { type: string }
        locked:   { type: string }
    DebitRequest:
      type: object
      required: [amount, currency, refId]
      properties:
        amount: { type: string }
        currency: { type: string }
        refId:  { type: string }
        meta:   { type: object, additionalProperties: true }
    CreditRequest:
      allOf:
        - $ref: "#/components/schemas/DebitRequest"
    LedgerEntry:
      type: object
      properties:
        id: { type: string }
        walletId: { type: string }
        direction: { type: string, enum: [debit, credit] }
        amount: { type: string }
        balanceAfter: { type: string }
        idempotencyKey: { type: string }
        createdAt: { type: string, format: date-time }
    Round:
      type: object
      properties:
        roundId: { type: string }
        phase: { type: string, enum: [betting, running, crashed, settled] }
        bettingClosesAt: { type: string, format: date-time }
        startedAt: { type: string, format: date-time, nullable: true }
        crashPoint: { type: number, nullable: true }
        serverSeedHash: { type: string }
        clientSeed: { type: string }
        nonce: { type: integer }
    PlaceBet:
      type: object
      required: [roundId, amount, currency, walletId, operatorId, playerIdHash]
      properties:
        roundId: { type: string }
        amount: { type: string }
        currency: { type: string }
        walletId: { type: string }
        operatorId: { type: string }
        playerIdHash: { type: string }
        autoCashoutAt: { type: number, nullable: true }
    Bet:
      type: object
      properties:
        betId: { type: string }
        roundId: { type: string }
        amount: { type: string }
        autoCashoutAt: { type: number, nullable: true }
        status: { type: string, enum: [open, won, lost, refunded] }
    Cashout:
      type: object
      required: [betId]
      properties:
        betId: { type: string }
        atMultiplier: { type: number }
    CashoutResult:
      type: object
      properties:
        betId: { type: string }
        multiplier: { type: number }
        payout: { type: string }
        ledgerId: { type: string }
    FairnessProof:
      type: object
      properties:
        roundId: { type: string }
        serverSeed: { type: string }
        serverSeedHash: { type: string }
        clientSeed: { type: string }
        nonce: { type: integer }
        hmac: { type: string }
        crashPoint: { type: number }
        houseEdge: { type: number }
        maxMultiplier: { type: number }
        verifierReference:
          type: string
          example: "services/fairness-svc/src/fairness/crypto.util.ts#deriveCrashPoint"
