# Entry document for the /swagger/profile spec; served after appending the shared
# _paths-*.yaml and _components.yaml fragments under openapi/parts (see config/Swagger.kt).
openapi: "3.0.3"
info:
  title: "MineSec (glue) Profile Management API"
  description: "API for merchant profile and acceptance management"
  version: "183-20261028-9a02ec5"
servers:
  - url: "https://glue-stage.mspayhub.com"
security:
  - OpenApiBearerAuth: []
tags:
  - name: "Profiles"
    description: "Profile management operations"
  - name: "Default Config"
    description: "Default configuration management operations"

paths:
  /api/v1/pos/profiles:
    get:
      tags:
        - "Profiles"
      summary: "List profiles"
      description: "Get a paginated list of profiles with optional filtering."
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
        - name: "startingAfter"
          in: "query"
          description: "The profileId to query the next page after. Mutually exclusive with endingBefore."
          schema:
            type: "string"
        - name: "endingBefore"
          in: "query"
          description: "The profileId to query the previous page before. Mutually exclusive with startingAfter."
          schema:
            type: "string"
        - name: "limit"
          in: "query"
          description: "Number of results to return per page. Default 10, maximum 100."
          schema:
            type: "integer"
            default: 10
            maximum: 100
        - name: "profileName"
          in: "query"
          description: "Profile name"
          schema:
            type: "string"
        - name: "providerReferences"
          in: "query"
          description: "Comma-separated list of provider references"
          style: "form"
          explode: false
          schema:
            type: "array"
            items:
              type: "string"
        - name: "merchantName"
          in: "query"
          description: "Merchant name"
          schema:
            type: "string"
        - name: "mccs"
          in: "query"
          description: "Comma-separated list of MCC codes"
          style: "form"
          explode: false
          schema:
            type: "array"
            items:
              type: "string"
        - name: "entityStatus"
          in: "query"
          description: "Entity status"
          schema:
            $ref: "#/components/schemas/EntityStatus"
      responses:
        "200":
          description: "List of profiles retrieved successfully"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CursorPaginatedProfile"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
    post:
      tags:
        - "Profiles"
      summary: "Create profile"
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
      requestBody:
        description: |
          Request body to create a new profile.
          - **Required fields**: profileName, merchantName, merchantAddr, mcc, countryCode, currency
          - **Optional fields**: providerReference, providerMchId, merchantPhone, merchantEmail, merchantCity, merchantPostalCode, acceptances
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProfileRequest"
            examples:
              profileWithAcceptances:
                summary: "Profile with multiple acceptances"
                description: "Profile with multiple acceptances for different payment providers and tags"
                value:
                  profileName: "string"
                  providerReference: "string"
                  merchantName: "string"
                  merchantAddr: "string"
                  merchantEmail: "string"
                  merchantPhone: "string"
                  mcc: "string"
                  countryCode: "string"
                  currency: "string"
                  acceptances:
                    - acptName: "string"
                      acptReference: "string"
                      hostMessageFormat: "MPGS_AP"
                      supportedPayments:
                        - "VISA"
                        - "MASTERCARD"
                      primaryMid: "string"
                      primaryTid: "string"
                      tags:
                        - "string"
                      extraParams:
                        subMchDba: "string"
                    - acptName: "string"
                      acptReference: "string"
                      hostMessageFormat: "GCAG"
                      supportedPayments:
                        - "AMEX"
                      primaryMid: "string"
                      primaryTid: "string"
                    - acptName: "string"
                      acptReference: "string"
                      hostMessageFormat: "IULINK"
                      supportedPayments:
                        - "UNIONPAY"
                      primaryMid: "string"
                      primaryTid: "string"
                      tags:
                        - "string"
        required: true
      responses:
        "201":
          description: "Profile created successfully"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProfileWithAcceptances"
        "400":
          description: "Bad request due to invalid input"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /api/v1/pos/profiles/providerReference/{providerReference}:
    get:
      tags:
        - "Profiles"
      summary: "Get profile by provider reference"
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
        - name: "providerReference"
          in: "path"
          required: true
          schema:
            type: "string"
      responses:
        "200":
          description: "Profile retrieved successfully"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProfileWithAcceptances"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: "Profile not found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/v1/pos/profiles/{profileId}:
    get:
      tags:
        - "Profiles"
      summary: "Get profile by ID"
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
        - name: "profileId"
          in: "path"
          required: true
          schema:
            type: "string"
      responses:
        "200":
          description: "Profile retrieved successfully"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProfileWithAcceptances"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: "Profile not found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    put:
      tags:
        - "Profiles"
      summary: "Update profile"
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
        - name: "profileId"
          in: "path"
          required: true
          schema:
            type: "string"
      requestBody:
        description: |
          Request body to update a profile.
          - For **acceptances**: include `acptId` to update existing acceptance, omit to create new acceptance
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProfileRequest"
            examples:
              updateWithAcceptances:
                summary: "Update profile with acceptances"
                description: "Update profile - include acptId to update existing acceptance, omit acptId to create new"
                value:
                  profileName: "string"
                  providerReference: "string"
                  merchantName: "string"
                  merchantAddr: "string"
                  merchantEmail: "string"
                  merchantPhone: "string"
                  mcc: "string"
                  countryCode: "string"
                  currency: "string"
                  acceptances:
                    - acptId: "string"
                      acptName: "string"
                      acptReference: "string"
                      hostMessageFormat: "MPGS_AP"
                      supportedPayments:
                        - "VISA"
                        - "MASTERCARD"
                      primaryMid: "string"
                      primaryTid: "string"
                      tags:
                        - "string"
                      extraParams:
                        subMchDba: "string"
                    - acptName: "string"
                      acptReference: "string"
                      hostMessageFormat: "GCAG"
                      supportedPayments:
                        - "AMEX"
                      primaryMid: "string"
                      primaryTid: "string"
        required: true
      responses:
        "200":
          description: "Profile updated successfully"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProfileWithAcceptances"
        "400":
          description: "Bad request due to invalid input"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: "Profile not found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/v1/pos/profiles/{profileId}/status:
    patch:
      tags:
        - "Profiles"
      summary: "Update profile status"
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
        - name: "profileId"
          in: "path"
          required: true
          schema:
            type: "string"
      requestBody:
        description: "Request body to update profile status"
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StatusRequest"
            examples:
              activate:
                summary: "Activate profile"
                value:
                  status: "ACTIVE"
              disable:
                summary: "Disable profile"
                value:
                  status: "DISABLED"
        required: true
      responses:
        "200":
          description: "Profile status updated successfully"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProfileWithAcceptances"
        "400":
          description: "Bad request due to invalid status input"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: "Profile not found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/v1/pos/profiles/{profileId}/deviceParam:
    get:
      tags:
        - "Profiles"
      summary: "Get device parameters for a profile"
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
        - name: "profileId"
          in: "path"
          required: true
          schema:
            type: "string"
      responses:
        "200":
          description: "Device parameters retrieved successfully"
          content:
            application/json:
              schema:
                type: "object"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: "Profile not found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/v1/pos/config/{profileId}/terminal:
    put:
      tags:
        - "Default Config"
      summary: "Update profile-level terminal configuration"
      description: "Saves a terminal configuration override for the specified profile. Does not affect the PBO-level default."
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
        - name: "profileId"
          in: "path"
          required: true
          description: "Profile ID to set the config override for."
          schema:
            type: "string"
      requestBody:
        description: "Terminal configuration parameters"
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TerminalParam"
        required: true
      responses:
        "200":
          description: "Terminal configuration updated successfully"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TerminalParam"
        "400":
          description: "Bad request — profileId does not exist or request body is invalid"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /api/v1/pos/config/{profileId}/emv:
    put:
      tags:
        - "Default Config"
      summary: "Update profile-level EMV configuration"
      description: "Saves an EMV configuration override for the specified profile. Does not affect the PBO-level default."
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
        - name: "profileId"
          in: "path"
          required: true
          description: "Profile ID to set the config override for."
          schema:
            type: "string"
      requestBody:
        description: "List of EMV parameters"
        content:
          application/json:
            schema:
              type: "array"
              items:
                $ref: "#/components/schemas/EmvParam"
        required: true
      responses:
        "200":
          description: "EMV configuration updated successfully"
          content:
            application/json:
              schema:
                type: "array"
                items:
                  $ref: "#/components/schemas/EmvParam"
        "400":
          description: "Bad request — profileId does not exist or request body is invalid"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /api/v1/pos/config/{profileId}/capk:
    put:
      tags:
        - "Default Config"
      summary: "Update profile-level CAPK configuration"
      description: "Saves a CAPK configuration override for the specified profile. Does not affect the PBO-level default."
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
        - name: "profileId"
          in: "path"
          required: true
          description: "Profile ID to set the config override for."
          schema:
            type: "string"
      requestBody:
        description: "List of CAPK parameters"
        content:
          application/json:
            schema:
              type: "array"
              items:
                $ref: "#/components/schemas/CapkParam"
        required: true
      responses:
        "200":
          description: "CAPK configuration updated successfully"
          content:
            application/json:
              schema:
                type: "array"
                items:
                  $ref: "#/components/schemas/CapkParam"
        "400":
          description: "Bad request — profileId does not exist or request body is invalid"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /api/v1/pos/config/terminal:
    get:
      tags:
        - "Default Config"
      summary: "Get PBO-level terminal configuration"
      description: "Returns the terminal configuration for the given profile if present, otherwise falls back to the PBO-level default. Enabler handles the fallback logic. When the caller token carries an `isvId` claim, resolution also goes through the ISV+currency and ISV levels."
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
        - name: "profileId"
          in: "query"
          required: false
          description: "Profile ID. When provided, returns the profile-level config if it exists, otherwise falls back through the ISV+currency and ISV levels (when the caller token carries an `isvId` claim) to the PBO-level default."
          schema:
            type: "string"
        - name: "currency"
          in: "query"
          required: false
          description: "ISO 4217 alpha-3 currency (case-insensitive). Scopes the ISV lookup (token `isvId` claim) to the ISV+currency level. Ignored without an ISV-scoped token."
          schema:
            type: "string"
      responses:
        "200":
          description: "Ok"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TerminalParam"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
    put:
      tags:
        - "Default Config"
      summary: "Update PBO-level terminal configuration"
      description: "Updates the PBO-level default terminal configuration. Applies to all profiles that have no profile-level override."
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
      requestBody:
        description: "Terminal configuration parameters"
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TerminalParam"
        required: true
      responses:
        "200":
          description: "Terminal configuration updated successfully"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TerminalParam"
        "400":
          description: "Bad request — request body is invalid"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /api/v1/pos/config/emv:
    get:
      tags:
        - "Default Config"
      summary: "Get PBO-level EMV configuration"
      description: "Returns the EMV parameters for the given profile if present, otherwise falls back to the PBO-level default. Enabler handles the fallback logic. When the caller token carries an `isvId` claim, resolution also goes through the ISV+currency and ISV levels."
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
        - name: "profileId"
          in: "query"
          required: false
          description: "Profile ID. When provided, returns the profile-level config if it exists, otherwise falls back through the ISV+currency and ISV levels (when the caller token carries an `isvId` claim) to the PBO-level default."
          schema:
            type: "string"
        - name: "currency"
          in: "query"
          required: false
          description: "ISO 4217 alpha-3 currency (case-insensitive). Scopes the ISV lookup (token `isvId` claim) to the ISV+currency level. Ignored without an ISV-scoped token."
          schema:
            type: "string"
      responses:
        "200":
          description: "Ok"
          content:
            application/json:
              schema:
                type: "array"
                items:
                  $ref: "#/components/schemas/EmvParam"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
    put:
      tags:
        - "Default Config"
      summary: "Update PBO-level EMV configuration"
      description: "Updates the PBO-level default EMV parameters. Applies to all profiles that have no profile-level override."
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
      requestBody:
        description: "List of EMV parameters"
        content:
          application/json:
            schema:
              type: "array"
              items:
                $ref: "#/components/schemas/EmvParam"
        required: true
      responses:
        "200":
          description: "EMV configuration updated successfully"
          content:
            application/json:
              schema:
                type: "array"
                items:
                  $ref: "#/components/schemas/EmvParam"
        "400":
          description: "Bad request — request body is invalid"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /api/v1/pos/config/capk:
    get:
      tags:
        - "Default Config"
      summary: "Get PBO-level CAPK configuration"
      description: "Returns the CAPK parameters for the given profile if present, otherwise falls back to the PBO-level default. Enabler handles the fallback logic. When the caller token carries an `isvId` claim, resolution also goes through the ISV+currency and ISV levels."
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
        - name: "profileId"
          in: "query"
          required: false
          description: "Profile ID. When provided, returns the profile-level config if it exists, otherwise falls back through the ISV+currency and ISV levels (when the caller token carries an `isvId` claim) to the PBO-level default."
          schema:
            type: "string"
        - name: "currency"
          in: "query"
          required: false
          description: "ISO 4217 alpha-3 currency (case-insensitive). Scopes the ISV lookup (token `isvId` claim) to the ISV+currency level. Ignored without an ISV-scoped token."
          schema:
            type: "string"
      responses:
        "200":
          description: "Ok"
          content:
            application/json:
              schema:
                type: "array"
                items:
                  $ref: "#/components/schemas/CapkParam"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
    put:
      tags:
        - "Default Config"
      summary: "Update PBO-level CAPK configuration"
      description: "Updates the PBO-level default CAPK parameters. Applies to all profiles that have no profile-level override."
      parameters:
        - $ref: "#/components/parameters/XMineSecRequestId"
      requestBody:
        description: "List of CAPK parameters"
        content:
          application/json:
            schema:
              type: "array"
              items:
                $ref: "#/components/schemas/CapkParam"
        required: true
      responses:
        "200":
          description: "CAPK configuration updated successfully"
          content:
            application/json:
              schema:
                type: "array"
                items:
                  $ref: "#/components/schemas/CapkParam"
        "400":
          description: "Bad request — request body is invalid"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

components:
  securitySchemes:
    OpenApiBearerAuth:
      type: "http"
      scheme: "bearer"
      bearerFormat: "JWT"
      description: "Bearer token authentication for POS API access"

  responses:
    Unauthorized:
      description: "Missing or invalid JWT token"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            status: 401
            path: "/api/v1/pos/profiles"
            requestId: "req_01KF12TA15CPFDW2NKKQDQRJ5E"
            title: "Unauthorized"
            message: "Token is not valid or has expired"
    Forbidden:
      description: "Insufficient scope in JWT token"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            status: 403
            path: "/api/v1/pos/profiles"
            requestId: "req_01KF12TA15CPFDW2NKKQDQRJ5E"
            title: "AuthorizationException"
            message: "Insufficient scope: requires pos:profiles or pos:configs"

  parameters:
    XMineSecRequestId:
      name: "x-minesec-request-id"
      in: "header"
      required: false
      description: "Request ID for tracing. Format: ULID or req_ULID (e.g., 01KF12TA15CPFDW2NKKQDQRJ5E or req_01KF12TA15CPFDW2NKKQDQRJ5E)"
      schema:
        type: "string"
      example: "req_01KF12TA15CPFDW2NKKQDQRJ5E"

  schemas:
    ErrorResponse:
      type: "object"
      properties:
        status:
          type: "integer"
          format: "int32"
        path:
          type: "string"
        requestId:
          type: "string"
        title:
          type: "string"
        message:
          type: "string"
      required:
        - "path"
        - "requestId"
        - "title"
        - "message"
        - "status"

    EntityStatus:
      type: "string"
      enum:
        - "ACTIVE"
        - "DISABLED"

    PaymentMethod:
      type: "string"
      enum:
        - "VISA"
        - "MASTERCARD"
        - "UNIONPAY"
        - "AMEX"
        - "JCB"
        - "DISCOVER"
        - "DINERS"
        - "MAESTRO"
        - "HIMYAN"
        - "NAPS"
        - "MY_DEBIT"
        - "ELO"
        - "CPACE"
        - "NAPAS"
        - "TROY"
        - "TAKAPAY"
        - "SADEEQ"
        - "ALIPAY"
        - "WECHAT"
        - "VISA_QR"
        - "MASTERCARD_QR"
        - "UNIONPAY_QR"
        - "SHOPEE_PAY_QR"
        - "GRAB_PAY_QR"
        - "ALIPAY_PLUS_QR"
        - "DUIT_NOW_QR"
        - "PAY_NOW"
        - "PH_QR"
        - "JCB_EMV_QR"
        - "LINEPAY_QR"
        - "EZ_PAY"
        - "PI_QR"
        - "ESUNWALLET_QR"
        - "TAIWANPAY_QR"
        - "EASYWALLET_QR"
        - "JKOPAY_QR"
        - "IPASSMONEY_PAY_QR"
        - "ICASH_PAY_QR"
        - "PLUSPAY_QR"
        - "PXPAYPLUS_QR"
        - "GAMA_PAY_QR"
        - "OPAY_QR"
        - "LINEPAYMONEY_QR"
        - "PAYPAY_QR"
        - "BCCARD_QR"
        - "NETS_QR"
        - "NTTDATA_QR"
        - "UCARD"
        - "CUP"
        - "SMARTPAY"
        - "BONUSPAY_QR"
        - "CASHLEZ_QR"
        - "UNKNOWN"

    HostMessageFormat:
      type: "string"
      enum:
        - "MOCK"
        - "MPGS_AP"
        - "CARD_NOW"
        - "TWOC2P"
        - "CARDWORKS"
        - "SHIFT4"
        - "SHIFT4_HD"
        - "PAY_STORE"
        - "POMELO"
        - "EFT_PAY"
        - "EZPAY"
        - "BANKNBOX"
        - "IULINK"
        - "RAZER"
        - "WALLYT"
        - "GCAG"
        - "POSTBRIDGE"
        - "RENDEZVOUS"
        - "STD_JSON"
        - "S2M_SELECT"
        - "VAULTSPAY"
        - "SMART_VISTA"
        - "SADAD"
        - "AIRWALLEX"
        - "AIRWALLEX_EU"
        - "WORLDPAY"
        - "CASHPLUS"
        - "PSS"
        - "RAWBANK"
        - "NEOCASH"
        - "CASHLEZ"
        - "QRIS"
        - "MASARAT"
        - "BONUSPAY"
        - "ALTO"
        - "AXS"
        - "CARDINFOLINK"
        - "BBL"
        - "ONERWAY"
        - "ACB"
        - "KEENU"
        - "SELECT_STANDARD"
        - "SELECT_MANUAL_MODE"
        - "SELECT_AUTO_MODE"
        - "SELECT_V24_MODE"
        - "STD_JSON_BSD"
        - "STD_JSON_YEDPAY"

    # ==================== Profile Schemas ====================
    ProfileRequest:
      type: "object"
      description: "Request to create or update a profile"
      properties:
        profileName:
          type: "string"
        providerReference:
          type: "string"
        providerMchId:
          type: "string"
        isvId:
          type: "string"
        isvName:
          type: "string"
        shopId:
          type: "string"
        shopName:
          type: "string"
        merchantName:
          type: "string"
        merchantAddr:
          type: "string"
        merchantPhone:
          type: "string"
        merchantEmail:
          type: "string"
        merchantCity:
          type: "string"
        merchantPostalCode:
          type: "string"
        mcc:
          type: "string"
          description: "Merchant Category Code"
        countryCode:
          type: "string"
          description: "ISO alpha-2 country code"
        currency:
          type: "string"
          description: "ISO alpha-3 currency code"
        acceptances:
          type: "array"
          items:
            $ref: "#/components/schemas/AcceptanceUpsertRequest"
      required:
        - "profileName"
        - "merchantName"
        - "merchantAddr"
        - "mcc"
        - "countryCode"
        - "currency"

    Profile:
      type: "object"
      properties:
        profileId:
          type: "string"
        profileName:
          type: "string"
        providerReference:
          type: "string"
        providerMchId:
          type: "string"
        isvId:
          type: "string"
        isvName:
          type: "string"
        shopId:
          type: "string"
        shopName:
          type: "string"
        merchantName:
          type: "string"
        merchantAddr:
          type: "string"
        merchantPhone:
          type: "string"
        merchantEmail:
          type: "string"
        merchantCity:
          type: "string"
        merchantPostalCode:
          type: "string"
        mcc:
          type: "string"
        countryCode:
          type: "string"
        currency:
          type: "string"
        entityStatus:
          $ref: "#/components/schemas/EntityStatus"
        createdAt:
          type: "string"
          format: "date-time"
          example: "2026-01-26T12:57:01.373+08:00"
        updatedAt:
          type: "string"
          format: "date-time"
          example: "2026-01-26T12:57:01.381+08:00"
      required:
        - "profileId"
        - "profileName"
        - "merchantName"
        - "mcc"
        - "countryCode"
        - "currency"
        - "entityStatus"
        - "createdAt"
        - "merchantAddr"
    CursorPaginatedProfile:
      type: "object"
      description: "Cursor-paginated list of profiles"
      properties:
        hasMore:
          type: "boolean"
          description: "Whether more results exist beyond this page"
        requestPath:
          type: "string"
          description: "The request path including query parameters, as echoed back by the server"
        requestSize:
          type: "integer"
          description: "The requested page size (limit)"
        dataSize:
          type: "integer"
          description: "The number of items actually returned in this page"
        totalSize:
          type: "integer"
          description: "Total number of matching profiles across all pages"
        cursorStarting:
          type: "string"
          nullable: true
          description: "The cursor (profileId) of the first item in this page. Pass as startingAfter to fetch the next page."
        cursorEnding:
          type: "string"
          nullable: true
          description: "The cursor (profileId) of the last item in this page. Pass as endingBefore to fetch the previous page."
        data:
          type: "array"
          items:
            $ref: "#/components/schemas/Profile"
      required:
        - "hasMore"
        - "requestPath"
        - "requestSize"
        - "dataSize"
        - "totalSize"
        - "data"

    ProfileWithAcceptances:
      type: "object"
      properties:
        profileId:
          type: "string"
        profileName:
          type: "string"
        providerReference:
          type: "string"
        providerMchId:
          type: "string"
        isvId:
          type: "string"
        isvName:
          type: "string"
        shopId:
          type: "string"
        shopName:
          type: "string"
        merchantName:
          type: "string"
        merchantAddr:
          type: "string"
        merchantPhone:
          type: "string"
        merchantEmail:
          type: "string"
        merchantCity:
          type: "string"
        merchantPostalCode:
          type: "string"
        mcc:
          type: "string"
        countryCode:
          type: "string"
        currency:
          type: "string"
        acceptances:
          type: "array"
          items:
            $ref: "#/components/schemas/Acceptance"
        acptSelection:
          type: "object"
          additionalProperties:
            type: "array"
            items:
              $ref: "#/components/schemas/PaymentMethod"
        entityStatus:
          $ref: "#/components/schemas/EntityStatus"
        createdAt:
          type: "string"
          format: "date-time"
          example: "2026-01-26T12:57:01.373+08:00"
        updatedAt:
          type: "string"
          format: "date-time"
          example: "2026-01-26T12:57:01.381+08:00"
      required:
        - "profileId"
        - "profileName"
        - "merchantName"
        - "mcc"
        - "countryCode"
        - "currency"
        - "acceptances"
        - "entityStatus"
        - "createdAt"
        - "merchantAddr"

    StatusRequest:
      type: "object"
      properties:
        status:
          $ref: "#/components/schemas/EntityStatus"
      required:
        - "status"

    # ==================== Acceptance Schemas ====================
    AcceptanceUpsertRequest:
      type: "object"
      description: "Request to create or update acceptance in a profile"
      properties:
        acptId:
          type: "string"
          description: "Optional acceptance ID. If present, will update instead of create"
        acptName:
          type: "string"
        acptReference:
          type: "string"
        hostMessageFormat:
          $ref: "#/components/schemas/HostMessageFormat"
        supportedPayments:
          type: "array"
          items:
            $ref: "#/components/schemas/PaymentMethod"
        primaryMid:
          type: "string"
        primaryTid:
          type: "string"
        acqMid:
          type: "string"
          deprecated: true
          description: "Deprecated. Use primaryMid instead"
        acqTid:
          type: "string"
          deprecated: true
          description: "Deprecated. Use primaryTid instead"
        extraParams:
          type: "object"
          additionalProperties:
            type: "string"
        tags:
          type: "array"
          items:
            type: "string"
      required:
        - "hostMessageFormat"
        - "supportedPayments"

    Acceptance:
      type: "object"
      properties:
        acptId:
          type: "string"
        acptName:
          type: "string"
        acptReference:
          type: "string"
        hostMessageFormat:
          $ref: "#/components/schemas/HostMessageFormat"
        supportedPayments:
          type: "array"
          items:
            $ref: "#/components/schemas/PaymentMethod"
        primaryMid:
          type: "string"
        primaryTid:
          type: "string"
        extraParams:
          type: "object"
          additionalProperties:
            type: "string"
        keyId:
          type: "string"
        tags:
          type: "array"
          items:
            type: "string"
        entityStatus:
          $ref: "#/components/schemas/EntityStatus"
        createdAt:
          type: "string"
          format: "date-time"
          example: "2026-01-26T12:57:01.373+08:00"
        updatedAt:
          type: "string"
          format: "date-time"
          example: "2026-01-26T12:57:01.381+08:00"
      required:
        - "acptId"
        - "hostMessageFormat"
        - "supportedPayments"
        - "primaryMid"
        - "primaryTid"
        - "entityStatus"
        - "createdAt"
        - "extraParams"

    TerminalParam:
      type: "object"
      description: "Terminal configuration parameters"
      properties:
        terminalType:
          type: "string"
          description: "Terminal type identifier"
        merchantCategoryCode:
          type: "string"
          description: "Merchant Category Code (MCC)"
        merchantName:
          type: "string"
          nullable: true
          description: "Merchant name"
        countryCode:
          type: "string"
          description: "ISO 3166-1 alpha-2 country code"
        tranCurrCode:
          type: "string"
          description: "Transaction currency code in ISO 4217 format"
        terminalCapability:
          type: "string"
          description: "Terminal capability indicator"
        ctlReaderCapability:
          type: "string"
          nullable: true
          description: "Contactless reader capability"
        ctlReaderCapabilityAmex:
          type: "string"
          nullable: true
          description: "Contactless reader capability for American Express"
        bMydebitPriority:
          type: "boolean"
          nullable: true
          description: "Whether MyDebit priority is enabled"
        extra:
          type: "object"
          nullable: true
          additionalProperties:
            type: "string"
      required:
        - "terminalType"
        - "merchantCategoryCode"
        - "countryCode"
        - "tranCurrCode"
        - "terminalCapability"

    EmvParam:
      type: "object"
      description: "EMV (Europay, Mastercard, Visa) processing parameters"
      properties:
        aid:
          type: "string"
          description: "Application ID (9F06)"
        appVersion:
          type: "string"
          description: "Application Version Number (9F09), hex string"
        tacDenial:
          type: "string"
          description: "Terminal Action Code for Denial (default / contactless)"
        tacOnline:
          type: "string"
          description: "Terminal Action Code for Online Authorization (default / contactless)"
        tacDefault:
          type: "string"
          description: "Terminal Action Code for Default Authorization (default / contactless)"
        contactTacDenial:
          type: "string"
          nullable: true
          description: "Contact-specific Terminal Action Code for Denial"
        contactTacOnline:
          type: "string"
          nullable: true
          description: "Contact-specific Terminal Action Code for Online Authorization"
        contactTacDefault:
          type: "string"
          nullable: true
          description: "Contact-specific Terminal Action Code for Default Authorization"
        defaultDdol:
          type: "string"
          description: "Default DDOL for constructing the internal authenticate command"
        defaultTdol:
          type: "string"
          description: "Default TDOL for generating the TC Hash Value"
        fullMatchSelect:
          type: "boolean"
          description: "Application Selection Indicator (0=exact, 1=partial)"
        selectionPriority:
          type: "integer"
          format: "int32"
          description: "Priority of application selection"
        enabledRandomSelect:
          type: "boolean"
          description: "Indicates if random selection is enabled"
        randomSelectTargetPercent:
          type: "integer"
          format: "int32"
          description: "Target percentage for random selection"
        randomSelectMaxTargetPercent:
          type: "integer"
          format: "int32"
          description: "Maximum target percentage for Biased Random Selection"
        randomSelectThreshold:
          type: "string"
          description: "Threshold value for Biased Random Selection"
        enabledVelocityCheck:
          type: "boolean"
          description: "Indicates if velocity check is enabled"
        enabledFloorLimitCheck:
          type: "boolean"
          description: "Indicates if floor limit check is enabled"
        ctlFloorLimit:
          type: "string"
          description: "Terminal contactless floor limit (9F1B), N12"
        ctlTranLimit:
          type: "string"
          description: "Amount above which contactless transactions are not allowed"
        ctlCvmRequiredLimit:
          type: "string"
          description: "Amount above which CVM is required for contactless transactions"
        ttq:
          type: "string"
          nullable: true
          description: "Terminal transaction qualifier (9F66)"
        riskMgmtData:
          type: "string"
          nullable: true
          description: "Terminal risk management data (9F1D)"
        mcKernelConfig:
          type: "string"
          nullable: true
          description: "Mastercard kernel configuration (DF811B)"
        enabledCtlStatusCheck:
          type: "boolean"
          nullable: true
          description: "Indicates if contactless status check is enabled"
        cvmCapabilityCVMRequired:
          type: "string"
          nullable: true
          description: "CVM capability when CVM is required"
        cvmCapabilityNoCVM:
          type: "string"
          nullable: true
          description: "CVM capability when no CVM is required"
        isNewCVMRuleForMC:
          type: "boolean"
          nullable: true
          description: "Whether the new Mastercard CVM rule is enabled"
        kernelId:
          type: "integer"
          format: "int32"
          nullable: true
          description: "Kernel ID"
        jcbTip:
          type: "string"
          nullable: true
          description: "JCB Terminal Interchange Profile"
        clbCvmLimit:
          type: "boolean"
          nullable: true
          description: "Contactless CVM limit flag"
      required:
        - "aid"
        - "appVersion"
        - "tacDenial"
        - "tacOnline"
        - "tacDefault"
        - "defaultDdol"
        - "defaultTdol"
        - "fullMatchSelect"
        - "selectionPriority"
        - "enabledRandomSelect"
        - "randomSelectTargetPercent"
        - "randomSelectMaxTargetPercent"
        - "randomSelectThreshold"
        - "enabledVelocityCheck"
        - "enabledFloorLimitCheck"
        - "ctlFloorLimit"
        - "ctlTranLimit"
        - "ctlCvmRequiredLimit"

    CapkParam:
      type: "object"
      description: "Certification Authority Public Key (CAPK) parameters"
      properties:
        scheme:
          $ref: "#/components/schemas/PaymentMethod"
        rid:
          type: "string"
          description: "Registered Application Provider Identifier (RID)"
        keyIndex:
          type: "string"
          description: "Key index"
        exponent:
          type: "string"
          description: "Public key exponent"
        modulus:
          type: "string"
          description: "Public key modulus"
        expiryYyMmDd:
          type: "string"
          description: "Key expiry date in YYMMDD format"
        checksum:
          type: "string"
          description: "Key checksum"
        capkType:
          type: "string"
          nullable: true
          enum:
            - "TEST"
            - "LIVE"
          description: "Key type (TEST or LIVE)"
      required:
        - "scheme"
        - "rid"
        - "keyIndex"
        - "exponent"
        - "modulus"
        - "expiryYyMmDd"
        - "checksum"
