openapi: 3.0.3
info:
  title: PUP - Funding Voucher API v1
  description: >
    PUP is the universal player profile for padel. This API provides funding voucher management.
  version: 1.0.0

servers:
  - url: https://api.gopup.app
    description: Development server

x-tagGroups:
  - name: Voucher
    tags:
      - funding

tags:
  - name: funding
    x-displayName: "Funding"
    description: Issue, search, claim and revoke funding vouchers that grant clubs PUP coins

security:
  - OAuth2:
      - urn:pup:funding-voucher:read
  - PAT: []

paths:
  /voucher/v1/funding-vouchers:
    post:
      tags:
        - funding
      operationId: issueFundingVoucher
      summary: Issue a new funding voucher
      description: Creates a new funding voucher code that can be claimed by club owners. Requires voucher.manage.
      security:
        - OAuth2:
            - urn:pup:funding-voucher:manage
        - PAT: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IssueFundingVoucherRequest'
      responses:
        '201':
          description: Funding voucher issued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IssueFundingVoucherCreated'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Voucher code already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    get:
      tags:
        - funding
      operationId: searchFundingVouchers
      summary: Search funding vouchers
      description: Lists funding vouchers with optional filters. Requires voucher.manage.
      security:
        - OAuth2:
            - urn:pup:funding-voucher:read
        - PAT: []
      parameters:
        - name: status
          in: query
          required: false
          schema:
            type: string
            enum:
              - active
              - revoked
        - name: voucher_type
          in: query
          required: false
          schema:
            type: string
            enum:
              - single_use
              - multi_use
        - name: offset
          in: query
          required: false
          schema:
            type: integer
            format: int32
            minimum: 0
            default: 0
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            format: int32
            minimum: 1
            maximum: 100
            default: 20
      responses:
        '200':
          description: Search results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchFundingVouchersResponse'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /voucher/v1/funding-vouchers/{code}:
    get:
      tags:
        - funding
      operationId: getFundingVoucherByCode
      summary: Get funding voucher details
      description: Returns a single funding voucher with its redemption history. Requires voucher.manage.
      security:
        - OAuth2:
            - urn:pup:funding-voucher:read
        - PAT: []
      parameters:
        - name: code
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Funding voucher details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FundingVoucherResponse'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Voucher not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /voucher/v1/funding-vouchers/{code}/revoke:
    post:
      tags:
        - funding
      operationId: revokeFundingVoucher
      summary: Revoke a funding voucher
      description: Revokes a funding voucher so it can no longer be claimed. Requires voucher.manage.
      security:
        - OAuth2:
            - urn:pup:funding-voucher:manage
        - PAT: []
      parameters:
        - name: code
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Voucher revoked
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Voucher not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Voucher already revoked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /voucher/v1/claim:
    post:
      tags:
        - funding
      operationId: claimFundingVoucher
      summary: Claim a funding voucher for a club
      description: Claims a funding voucher by code, crediting the specified club's account. The caller must be the club owner.
      security:
        - OAuth2:
            - urn:pup:funding-voucher:claim
        - PAT: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ClaimFundingVoucherRequest'
      responses:
        '200':
          description: Voucher claimed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClaimFundingVoucherOK'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Voucher not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Already claimed by this club or voucher exhausted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Voucher expired or revoked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

components:
  securitySchemes:
    OAuth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://oauth2.gopup.app/oauth2/auth
          tokenUrl: https://oauth2.gopup.app/oauth2/token
          scopes:
            urn:pup:funding-voucher:read: Read funding vouchers
            urn:pup:funding-voucher:manage: Manage funding vouchers
            urn:pup:funding-voucher:claim: Claim a funding voucher
    PAT:
      type: http
      scheme: bearer
      bearerFormat: PAT
      description: Personal Access Token

  schemas:
    IssueFundingVoucherRequest:
      type: object
      required:
        - amount
        - voucherType
      properties:
        code:
          type: string
          description: Custom voucher code. If omitted, a code is auto-generated.
          example: BETA500
          minLength: 3
          maxLength: 50
        amount:
          type: integer
          format: int32
          description: Amount of PUP coins the voucher grants.
          minimum: 1
          example: 500
        voucherType:
          type: string
          description: Whether the voucher can be used once or multiple times.
          enum:
            - single_use
            - multi_use
        maxRedemptions:
          type: integer
          format: int32
          description: Maximum number of times a multi-use voucher can be claimed. Omit for unlimited.
          minimum: 1
          example: 100
        expiresAt:
          type: string
          format: date-time
          description: Expiry date-time. Omit for no expiry.

    ClaimFundingVoucherRequest:
      type: object
      required:
        - code
        - clubId
      properties:
        code:
          type: string
          description: The voucher code to claim.
          example: BETA500
        clubId:
          type: string
          description: The club to credit with the voucher amount.

    IssueFundingVoucherCreated:
      type: object
      required:
        - code
      properties:
        code:
          type: string
          description: The voucher code (useful when auto-generated).

    ClaimFundingVoucherOK:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          example: Voucher claimed successfully

    FundingVoucher:
      type: object
      required:
        - code
        - amount
        - voucherType
        - status
        - redemptionCount
        - createdBy
        - createdAt
      properties:
        code:
          type: string
        amount:
          type: integer
          format: int32
        voucherType:
          type: string
          enum:
            - single_use
            - multi_use
        status:
          type: string
          enum:
            - active
            - revoked
        maxRedemptions:
          type: integer
          format: int32
          nullable: true
        redemptionCount:
          type: integer
          format: int32
        expiresAt:
          type: string
          format: date-time
          nullable: true
        createdBy:
          type: string
        createdAt:
          type: string
          format: date-time
        revokedAt:
          type: string
          format: date-time
          nullable: true

    FundingVoucherDetail:
      type: object
      required:
        - code
        - amount
        - voucherType
        - status
        - redemptionCount
        - createdBy
        - createdAt
        - redemptions
      properties:
        code:
          type: string
        amount:
          type: integer
          format: int32
        voucherType:
          type: string
          enum:
            - single_use
            - multi_use
        status:
          type: string
          enum:
            - active
            - revoked
        maxRedemptions:
          type: integer
          format: int32
          nullable: true
        redemptionCount:
          type: integer
          format: int32
        expiresAt:
          type: string
          format: date-time
          nullable: true
        createdBy:
          type: string
        createdAt:
          type: string
          format: date-time
        revokedAt:
          type: string
          format: date-time
          nullable: true
        redemptions:
          type: array
          items:
            $ref: '#/components/schemas/Redemption'

    Redemption:
      type: object
      required:
        - id
        - clubId
        - redeemedBy
        - claimedAt
      properties:
        id:
          type: string
        clubId:
          type: string
        redeemedBy:
          type: string
        claimedAt:
          type: string
          format: date-time

    FundingVoucherResponse:
      type: object
      required:
        - fundingVoucher
      properties:
        fundingVoucher:
          $ref: '#/components/schemas/FundingVoucherDetail'

    SearchFundingVouchersResponse:
      type: object
      required:
        - hits
        - meta
      properties:
        hits:
          type: array
          items:
            $ref: '#/components/schemas/FundingVoucher'
        meta:
          $ref: '#/components/schemas/ResponseMetaMany'

    ResponseMetaMany:
      type: object
      required:
        - pagination
      properties:
        pagination:
          type: object
          required:
            - total
            - returned
            - offset
            - limit
          properties:
            total:
              type: integer
              format: int64
              description: Total number of items
            returned:
              type: integer
              format: int32
              description: Number of items in this response
            offset:
              type: integer
              format: int32
              description: Offset used in the request
            limit:
              type: integer
              format: int32
              description: Limit used in the request
            next:
              type: string
              nullable: true
              description: Relative path to next page
            previous:
              type: string
              nullable: true
              description: Relative path to previous page

    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
      description: Generic error response
