openapi: 3.1.0
info:
  title: MuPag Public Merchant API
  version: 1.0.0
  description: Contrato público canônico para capacidades aprovadas e voltadas ao lojista.
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary
servers:
  - url: https://api.sandbox.mupag.com.br
    description: Sandbox disponível
  - url: https://api.mupag.com.br
    description: Live reservado; ambiente ainda não disponibilizado
security: []
paths:
  /v1/_test/charges/{id}/simulate-paid:
    post:
      tags: [Sandbox]
      operationId: simulateSandboxChargePaid
      summary: Confirma uma cobrança sandbox pelo fluxo canônico
      description: |
        Operação auxiliar autenticada, isolada por lojista e exclusiva de sandbox. A mesma
        `Idempotency-Key` retorna o resultado original; reutilizar a chave em outra operação falha com conflito.
      security:
        - merchantApiKey: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          required: true
          example: sandbox-charge-paid-550e8400
          schema:
            type: string
            minLength: 16
            maxLength: 128
            pattern: "^[\\x21-\\x7E]+$"
      responses:
        "200":
          description: Transição canônica concluída ou resposta original retornada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxTestResult"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/_test/charges/{id}/simulate-failed:
    post:
      tags: [Sandbox]
      operationId: simulateSandboxChargeFailed
      summary: Marca uma cobrança sandbox como falha pelo fluxo canônico
      description: |
        Operação auxiliar autenticada, isolada por lojista e exclusiva de sandbox. Produz os mesmos
        efeitos duráveis da transição canônica de falha e preserva a resposta original em novas tentativas.
      security:
        - merchantApiKey: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          required: true
          example: sandbox-charge-failed-550e8400
          schema:
            type: string
            minLength: 16
            maxLength: 128
            pattern: "^[\\x21-\\x7E]+$"
      responses:
        "200":
          description: Transição canônica concluída ou resposta original retornada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxTestResult"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/_test/subscriptions/{id}/advance-billing:
    post:
      tags: [Sandbox]
      operationId: advanceSandboxSubscriptionBilling
      summary: Antecipa a elegibilidade do próximo ciclo de uma assinatura sandbox
      description: |
        Executado de forma síncrona pelo serviço responsável pela assinatura. Nunca retrocede um período
        já processado, e novas tentativas com a mesma `Idempotency-Key` retornam o resultado persistido.
      security:
        - merchantApiKey: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          required: true
          example: sandbox-billing-advance-550e8400
          schema:
            type: string
            minLength: 16
            maxLength: 128
            pattern: "^[\\x21-\\x7E]+$"
      responses:
        "200":
          description: Avanço aplicado ou resposta original retornada sem alterar um ciclo já processado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxTestResult"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/_test/data/seed:
    post:
      tags: [Sandbox]
      operationId: seedSandboxData
      summary: Cria dados mínimos de teste do lojista no sandbox
      description: |
        Cria clientes e produtos de forma determinística no escopo autenticado. Novas tentativas retornam
        os mesmos identificadores persistidos; a rota exige simultaneamente os escopos de clientes e produtos.
      security:
        - merchantApiKey: []
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          example: sandbox-seed-data-550e8400
          schema:
            type: string
            minLength: 16
            maxLength: 128
            pattern: "^[\\x21-\\x7E]+$"
      responses:
        "200":
          description: Dados de teste criados ou resposta original retornada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxSeedResult"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"

  /v1/_test/psp-subaccounts/{provider}/decision:
    post:
      tags: [Sandbox]
      operationId: simulateSandboxPspSubaccountDecision
      summary: Simula uma decisão do PSP sobre a subconta do lojista
      description: |
        Helper autenticado e tenant-scoped disponível somente em sandbox. A rota não existe
        no perfil live. Reutilize a mesma `Idempotency-Key` em retries; `event_id` também é
        deduplicado, `event_sequence` ordena eventos e decisões finais não podem ser revertidas.
      security:
        - merchantApiKey: []
      parameters:
        - name: provider
          in: path
          required: true
          schema:
            type: string
            enum: [asaas, woovi]
        - name: Idempotency-Key
          in: header
          required: true
          example: sandbox-decision-550e8400
          schema:
            type: string
            minLength: 16
            maxLength: 128
            pattern: "^[\\x21-\\x7E]+$"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SandboxSubaccountDecisionRequest"
      responses:
        "200":
          description: Estado corrente do PSP sandbox após aplicar ou ignorar replay/reorder.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxSubaccountDecisionResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          description: Credencial sem escopo ou pertencente ao ambiente live.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/Problem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"

  /v1/charges:
    get:
      tags: [Charges]
      operationId: listCharges
      summary: Lista cobranças do lojista autenticado
      description: |
        Retorna uma projeção sem PII, ordenada por `created_at DESC, charge_id DESC`.
        `created_at_from` é inclusivo; `created_at_to` é exclusivo. Use `next_cursor`
        sem interpretá-lo para continuar a paginação keyset.
      security:
        - merchantApiKey: []
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [created, pending, authorized, paid, partially_refunded, refunded, failed, expired, cancelled, disputed, chargeback]
        - name: customer_id
          in: query
          schema:
            type: string
            format: uuid
        - name: payment_method
          in: query
          schema:
            type: string
            enum: [pix, credit_card]
        - name: created_at_from
          in: query
          schema:
            type: string
            format: date-time
        - name: created_at_to
          in: query
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
        - name: cursor
          in: query
          description: Cursor opaco retornado por `next_cursor`.
          schema:
            type: string
            maxLength: 256
      responses:
        "200":
          description: Página de cobranças do lojista e ambiente autenticados
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChargeListPage"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
        "429":
          $ref: "#/components/responses/RateLimitProblem"
    post:
      tags: [Charges]
      operationId: createCharge
      summary: Cria uma cobrança PIX ou cartão B2B direto
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateChargeRequest"
      responses:
        "201":
          description: Cobrança criada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChargeResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
        "429":
          $ref: "#/components/responses/RateLimitProblem"
        "502":
          $ref: "#/components/responses/UpstreamProblem"
  /v1/charges/{chargeId}/cancel:
    post:
      tags: [Charges]
      operationId: cancelCharge
      summary: Cancela uma cobrança pendente
      description: |
        Cancela somente cobranças no estado `pending`. Um `202` indica que o resultado ainda
        não é autoritativo. Não há reconciliação em background: faça replay da mesma
        `Idempotency-Key` e do mesmo payload para continuar a reconciliação segura. Antes do
        envio mutável ao PSP, ausência no provedor mantém a intenção pendente; depois do fence
        durável de envio, ausência em consulta confirma o cancelamento sem repetir o DELETE.
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/ChargeId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChargeCancellationRequest"
      responses:
        "200":
          description: Cancelamento confirmado de forma autoritativa.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChargeCancellationResponse"
        "202":
          description: Resultado pendente; faça replay da mesma Idempotency-Key e do mesmo payload.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChargeCancellationResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
        "429":
          $ref: "#/components/responses/RateLimitProblem"
        "502":
          $ref: "#/components/responses/UpstreamProblem"
  /v1/charges/{chargeId}/refunds:
    get:
      tags: [Refunds]
      operationId: listChargeRefunds
      summary: Lista reembolsos de uma cobrança
      description: |
        Mantém o envelope histórico `{refunds: [...]}` e pagina por
        `created_at DESC, refund_id DESC`. Reenvie `next_cursor` sem interpretá-lo.
      security:
        - merchantApiKey: []
      parameters:
        - name: chargeId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 100
        - name: cursor
          in: query
          schema:
            type: string
            maxLength: 256
      responses:
        "200":
          description: Página limitada de reembolsos no escopo autenticado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RefundListPage"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
        "429":
          $ref: "#/components/responses/RateLimitProblem"
    post:
      tags: [Refunds]
      operationId: requestRefund
      summary: Solicita reembolso total ou parcial de uma cobrança
      security:
        - merchantApiKey: []
      parameters:
        - name: chargeId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RefundRequest"
      responses:
        "202":
          description: Reembolso aceito para processamento idempotente
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RefundResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
        "429":
          $ref: "#/components/responses/RateLimitProblem"
        "502":
          $ref: "#/components/responses/UpstreamProblem"
  /v1/refunds/{refundId}:
    get:
      tags: [Refunds]
      operationId: getRefund
      summary: Retorna um reembolso no escopo autenticado
      security:
        - merchantApiKey: []
      parameters:
        - name: refundId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Reembolso encontrado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RefundResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "429":
          $ref: "#/components/responses/RateLimitProblem"
  /v1/customers:
    post:
      tags: [Customers]
      operationId: createCustomer
      summary: Cria um cliente
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomerRequest"
      responses:
        "201":
          description: Cliente criado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
        "429":
          $ref: "#/components/responses/RateLimitProblem"
    get:
      tags: [Customers]
      operationId: listCustomers
      summary: Lista clientes do lojista autenticado
      security:
        - merchantApiKey: []
      parameters:
        - name: email
          in: query
          schema:
            type: string
            format: email
        - name: tax_id
          in: query
          schema:
            type: string
        - name: created_at_from
          in: query
          schema:
            type: string
            format: date-time
        - name: created_at_to
          in: query
          schema:
            type: string
            format: date-time
      responses:
        "200":
          description: Clientes encontrados
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/CustomerSummary"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "429":
          $ref: "#/components/responses/RateLimitProblem"
  /v1/customers/{id}:
    get:
      tags: [Customers]
      operationId: getCustomer
      summary: Retorna detalhes de um cliente
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/CustomerId"
      responses:
        "200":
          description: Cliente encontrado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
    patch:
      tags: [Customers]
      operationId: updateCustomer
      summary: Atualiza dados permitidos de um cliente
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/CustomerId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomerRequest"
      responses:
        "200":
          description: Cliente atualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/customers/{id}/addresses:
    post:
      tags: [Customers]
      operationId: addCustomerAddress
      summary: Adiciona endereço a um cliente
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/CustomerId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomerAddressRequest"
      responses:
        "201":
          description: Endereço criado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerAddressDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/customers/{id}/addresses/{addressId}:
    patch:
      tags: [Customers]
      operationId: updateCustomerAddress
      summary: Atualiza endereço de um cliente
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/CustomerId"
        - $ref: "#/components/parameters/AddressId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomerAddressRequest"
      responses:
        "200":
          description: Endereço atualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerAddressDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/customers/{id}/cards:
    get:
      tags: [Customers]
      operationId: listCustomerCards
      summary: Lista cartões tokenizados de um cliente
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/CustomerId"
      responses:
        "200":
          description: Cartões encontrados
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/CardTokenDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
  /v1/customers/{id}/cards/{cardId}:
    delete:
      tags: [Customers]
      operationId: deleteCustomerCard
      summary: Remove um cartão tokenizado
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/CustomerId"
        - $ref: "#/components/parameters/CardId"
      responses:
        "204":
          description: Cartão removido
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
  /v1/products:
    post:
      tags: [Products]
      operationId: createProduct
      summary: Cria um produto
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductRequest"
      responses:
        "201":
          description: Produto criado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
    get:
      tags: [Products]
      operationId: listProducts
      summary: Lista produtos do lojista autenticado
      security:
        - merchantApiKey: []
      parameters:
        - name: type
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            type: string
        - name: category
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Produtos encontrados
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ProductSummary"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
  /v1/products/{id}:
    get:
      tags: [Products]
      operationId: getProduct
      summary: Retorna detalhe de um produto
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/ProductId"
      responses:
        "200":
          description: Produto encontrado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
    patch:
      tags: [Products]
      operationId: updateProduct
      summary: Atualiza um produto
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/ProductId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductRequest"
      responses:
        "200":
          description: Produto atualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/products/{id}/archive:
    post:
      tags: [Products]
      operationId: archiveProduct
      summary: Arquiva um produto
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/ProductId"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "204":
          description: Produto arquivado
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
  /v1/products/{id}/upload-image-url:
    post:
      tags: [Products]
      operationId: createProductImageUploadUrl
      summary: Gera URL de upload de imagem do produto
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/ProductId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductUploadRequest"
      responses:
        "200":
          description: URL gerada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductUploadUrl"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/products/{id}/upload-digital-file-url:
    post:
      tags: [Products]
      operationId: createProductDigitalFileUploadUrl
      summary: Gera URL de upload de arquivo digital do produto
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/ProductId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductUploadRequest"
      responses:
        "200":
          description: URL gerada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductUploadUrl"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/products/{productId}/plans:
    post:
      tags: [Plans]
      operationId: createPlan
      summary: Cria um plano de assinatura para um produto
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/ProductParentId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePlanRequest"
      responses:
        "201":
          description: Plano criado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanResponse"
        "400":
          $ref: "#/components/responses/ValidationProblem"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
    get:
      tags: [Plans]
      operationId: listPlans
      summary: Lista planos de um produto
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/ProductParentId"
      responses:
        "200":
          description: Planos encontrados
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/PlanResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
  /v1/plans/{id}:
    get:
      tags: [Plans]
      operationId: getPlan
      summary: Retorna detalhe de um plano
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/PlanId"
      responses:
        "200":
          description: Plano encontrado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
    patch:
      tags: [Plans]
      operationId: updatePlan
      summary: Atualiza campos mutáveis de um plano
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/PlanId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdatePlanRequest"
      responses:
        "200":
          description: Plano atualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanResponse"
        "400":
          $ref: "#/components/responses/ValidationProblem"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/plans/{id}/archive:
    post:
      tags: [Plans]
      operationId: archivePlan
      summary: Arquiva um plano sem encerrar assinaturas existentes
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/PlanId"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "200":
          description: Plano arquivado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
  /v1/coupons:
    post:
      tags: [Coupons]
      operationId: createCoupon
      summary: Cria um cupom
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CouponRequest"
      responses:
        "201":
          description: Cupom criado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CouponDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
    get:
      tags: [Coupons]
      operationId: listCoupons
      summary: Lista cupons do lojista autenticado
      security:
        - merchantApiKey: []
      parameters:
        - name: type
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            type: string
        - name: code
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Cupons encontrados
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/CouponSummary"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
  /v1/coupons/{id}:
    get:
      tags: [Coupons]
      operationId: getCoupon
      summary: Retorna detalhe de um cupom
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/CouponId"
      responses:
        "200":
          description: Cupom encontrado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CouponDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
    patch:
      tags: [Coupons]
      operationId: updateCoupon
      summary: Atualiza campos mutáveis de um cupom
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/CouponId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CouponPatchRequest"
      responses:
        "200":
          description: Cupom atualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CouponDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/coupons/{id}/disable:
    post:
      tags: [Coupons]
      operationId: disableCoupon
      summary: Desabilita um cupom
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/CouponId"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "200":
          description: Cupom desabilitado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CouponDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
  /v1/coupons/validate:
    post:
      tags: [Coupons]
      operationId: validateCoupon
      summary: Valida um cupom
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CouponValidationRequest"
      responses:
        "200":
          description: Resultado da validação
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CouponValidationResult"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/checkout-sessions:
    post:
      tags: [Checkout Sessions]
      operationId: createCheckoutSession
      summary: Cria uma sessão de checkout hospedado
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateCheckoutSessionRequest"
      responses:
        "201":
          description: Sessão criada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateCheckoutSessionResponse"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/checkout-sessions/{id}/public:
    get:
      tags: [Checkout Sessions]
      operationId: getCheckoutSessionPublicView
      summary: Retorna o payload público sanitizado de uma sessão
      security: []
      parameters:
        - $ref: "#/components/parameters/CheckoutSessionId"
      responses:
        "200":
          description: Sessão pública encontrada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CheckoutPublicResponse"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
  /v1/checkout-sessions/{id}/select-method:
    post:
      tags: [Checkout Sessions]
      operationId: selectCheckoutSessionMethod
      summary: Seleciona o método de pagamento da sessão
      security: []
      parameters:
        - $ref: "#/components/parameters/CheckoutSessionId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SelectCheckoutMethodRequest"
      responses:
        "204":
          description: Método selecionado
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/checkout-sessions/{id}/coupon:
    post:
      tags: [Checkout Sessions]
      operationId: applyCheckoutSessionCoupon
      summary: Aplica cupom a uma sessão
      security: []
      parameters:
        - $ref: "#/components/parameters/CheckoutSessionId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ApplyCheckoutCouponRequest"
      responses:
        "200":
          description: Resumo atualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CheckoutSummaryResponse"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
    delete:
      tags: [Checkout Sessions]
      operationId: removeCheckoutSessionCoupon
      summary: Remove cupom de uma sessão
      security: []
      parameters:
        - $ref: "#/components/parameters/CheckoutSessionId"
      responses:
        "200":
          description: Resumo sem desconto
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CheckoutSummaryResponse"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
  /v1/checkout-sessions/{id}/pay:
    post:
      tags: [Checkout Sessions]
      operationId: payCheckoutSession
      summary: Cria a cobrança da sessão
      security: []
      parameters:
        - $ref: "#/components/parameters/CheckoutSessionId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PayCheckoutSessionRequest"
      responses:
        "200":
          description: Resultado do pagamento
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CheckoutPaymentResponse"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "410":
          $ref: "#/components/responses/GoneProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/checkout-sessions/{id}/status:
    get:
      tags: [Checkout Sessions]
      operationId: getCheckoutSessionStatus
      summary: Consulta o status público de uma sessão
      security: []
      parameters:
        - $ref: "#/components/parameters/CheckoutSessionId"
      responses:
        "200":
          description: Status da sessão
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CheckoutStatusResponse"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
  /v1/checkout-sessions/{id}/renew:
    post:
      tags: [Checkout Sessions]
      operationId: renewCheckoutSession
      summary: Renova uma sessão expirada ou com falha terminal confirmada em um novo PIX
      security: []
      parameters:
        - $ref: "#/components/parameters/CheckoutSessionId"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "201":
          description: Sessão renovada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateCheckoutSessionResponse"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "410":
          $ref: "#/components/responses/GoneProblem"
  /v1/subscriptions:
    post:
      tags: [Subscriptions]
      operationId: createSubscription
      summary: Cria uma assinatura
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSubscriptionRequest"
      responses:
        "201":
          description: Assinatura criada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
    get:
      tags: [Subscriptions]
      operationId: listSubscriptions
      summary: Lista assinaturas do lojista autenticado
      security:
        - merchantApiKey: []
      parameters:
        - name: status
          in: query
          schema:
            type: string
        - name: customer_id
          in: query
          schema:
            type: string
            format: uuid
        - name: plan_id
          in: query
          schema:
            type: string
            format: uuid
        - name: created_from
          in: query
          schema:
            type: string
            format: date
        - name: created_to
          in: query
          schema:
            type: string
            format: date
        - name: cursor
          in: query
          description: Cursor opaco e autenticado retornado em `X-Next-Cursor`.
          schema:
            type: string
            maxLength: 512
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
      responses:
        "200":
          description: Assinaturas encontradas
          headers:
            X-Next-Cursor:
              description: Presente apenas quando existe uma próxima página.
              schema:
                type: string
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/SubscriptionDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
  /v1/subscriptions/{id}:
    get:
      tags: [Subscriptions]
      operationId: getSubscription
      summary: Retorna detalhe de uma assinatura
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
      responses:
        "200":
          description: Assinatura encontrada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
    patch:
      tags: [Subscriptions]
      operationId: patchSubscription
      summary: Atualiza metadados e referência externa da assinatura
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchSubscriptionRequest"
      responses:
        "200":
          description: Assinatura atualizada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
  /v1/subscriptions/{id}/pause:
    post:
      tags: [Subscriptions]
      operationId: pauseSubscription
      summary: Pausa uma assinatura
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PauseSubscriptionRequest"
      responses:
        "200":
          description: Assinatura pausada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
  /v1/subscriptions/{id}/resume:
    post:
      tags: [Subscriptions]
      operationId: resumeSubscription
      summary: Retoma uma assinatura pausada
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "200":
          description: Assinatura retomada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
  /v1/subscriptions/{id}/cancel:
    post:
      tags: [Subscriptions]
      operationId: cancelSubscription
      summary: Cancela uma assinatura
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CancelSubscriptionRequest"
      responses:
        "200":
          description: Assinatura cancelada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/subscriptions/{id}/reactivate:
    post:
      tags: [Subscriptions]
      operationId: reactivateSubscription
      summary: Reativa uma assinatura cancelada
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "200":
          description: Assinatura reativada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
  /v1/subscriptions/{id}/change-plan:
    post:
      tags: [Subscriptions]
      operationId: changeSubscriptionPlan
      summary: Troca o plano de uma assinatura
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChangeSubscriptionPlanRequest"
      responses:
        "200":
          description: Plano alterado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
  /v1/subscriptions/{id}/update-card:
    post:
      tags: [Subscriptions]
      operationId: updateSubscriptionCard
      summary: Atualiza o cartão de uma assinatura
      security:
        - merchantApiKey: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateSubscriptionCardRequest"
      responses:
        "200":
          description: Cartão atualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionDetail"
        "401":
          $ref: "#/components/responses/AuthProblem"
        "403":
          $ref: "#/components/responses/ForbiddenProblem"
        "404":
          $ref: "#/components/responses/NotFoundProblem"
        "409":
          $ref: "#/components/responses/ConflictProblem"
        "422":
          $ref: "#/components/responses/ValidationProblem"
components:
  securitySchemes:
    merchantApiKey:
      type: http
      scheme: bearer
      bearerFormat: "Secret API key (sk_*)"
      description: "Use `Authorization: Bearer sk_*` ou `X-API-Key` na borda pública; o gateway valida e normaliza o bearer secreto para o downstream."
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      example: 550e8400-e29b-41d4-a716-446655440000
      schema:
        type: string
        minLength: 1
        maxLength: 128
    ChargeId:
      name: chargeId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    CustomerId:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    AddressId:
      name: addressId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    CardId:
      name: cardId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    ProductId:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    ProductParentId:
      name: productId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    PlanId:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    CouponId:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    CheckoutSessionId:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    SubscriptionId:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
  responses:
    AuthProblem:
      description: Falha de autenticação
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    ForbiddenProblem:
      description: Permissão insuficiente
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    NotFoundProblem:
      description: Recurso não encontrado
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    ConflictProblem:
      description: Conflito de idempotência ou estado
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    GoneProblem:
      description: Recurso expirado
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    ValidationProblem:
      description: Erro de validação
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    RateLimitProblem:
      description: Limite de requisições excedido
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    UpstreamProblem:
      description: Falha em integração externa
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
  schemas:
    SandboxTestResult:
      type: object
      additionalProperties: false
      required: [id, status, updated_at]
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          minLength: 1
          maxLength: 64
        updated_at:
          type: string
          format: date-time
    SandboxSeedResult:
      type: object
      additionalProperties: false
      required: [environment, customers, products]
      properties:
        environment:
          type: string
          enum: [sandbox]
        customers:
          type: array
          maxItems: 100
          items:
            type: string
            format: uuid
        products:
          type: array
          maxItems: 100
          items:
            type: string
            format: uuid

    SandboxSubaccountDecisionRequest:
      type: object
      additionalProperties: false
      required: [event_id, event_sequence, status]
      properties:
        event_id:
          type: string
          format: uuid
        event_sequence:
          type: integer
          format: int64
          minimum: 1
        status:
          type: string
          enum: [pending, approved, rejected]
        rejection_reason:
          type: string
          maxLength: 500
      allOf:
        - if:
            properties:
              status:
                const: rejected
          then:
            required: [rejection_reason]
    SandboxSubaccountDecisionResponse:
      type: object
      additionalProperties: false
      required: [provider, provider_reference, status, event_sequence]
      properties:
        provider:
          type: string
          enum: [asaas, woovi]
        provider_reference:
          type: string
        status:
          type: string
          enum: [pending, approved, rejected]
        event_sequence:
          type: integer
          format: int64
          minimum: 0
        rejection_reason:
          type: [string, "null"]
          maxLength: 500

    Problem:
      type: object
      required: [title, status, detail]
      properties:
        type:
          type: string
          format: uri
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
        code:
          type: string
        suggestion:
          type: string
        documentation_url:
          type: string
          format: uri
        request_id:
          type: string
    CreateChargeRequest:
      type: object
      required: [amount_cents, payment_method, customer]
      example:
        amount_cents: 1290
        payment_method: pix
        customer:
          name: Ana Silva
          email: ana@example.com
          tax_id: "12345678909"
      properties:
        amount_cents:
          type: integer
          format: int64
          minimum: 0
          maximum: 9000000000000000
        payment_method:
          type: string
          enum: [pix, credit_card]
        installments:
          type: integer
          minimum: 1
          maximum: 1
        card_token:
          type: string
          description: Token Asaas obtido pelo lojista fora do checkout público MuPag.
        card_token_id:
          type: string
          format: uuid
        save_card:
          type: boolean
        customer:
          $ref: "#/components/schemas/ChargeCustomer"
        description:
          type: string
          maxLength: 500
        soft_descriptor:
          type: string
          deprecated: true
          description: Não suportado pelo PSP Asaas; requisições com valor são rejeitadas.
        payer_ip:
          type: string
          description: IP literal do pagador, atestado pelo lojista; obrigatório para credit_card.
        product_max_installments:
          type: integer
          minimum: 1
          maximum: 1
        external_reference:
          type: string
        expires_in_seconds:
          type: integer
        metadata:
          type: object
          additionalProperties: true
        affiliate_code:
          type: string
        coupon_code:
          type: string
        split_rules:
          type: array
          maxItems: 50
          items:
            $ref: "#/components/schemas/SplitRuleRequest"
        is_mit:
          type: boolean
        initial_mit_reference_id:
          type: string
          format: uuid
    ChargeCustomer:
      oneOf:
        - $ref: "#/components/schemas/ExistingChargeCustomer"
        - $ref: "#/components/schemas/NewChargeCustomer"
    ExistingChargeCustomer:
      type: object
      required: [id, name, email, tax_id]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        email:
          type: string
          format: email
        tax_id:
          type: string
    NewChargeCustomer:
      type: object
      required: [name, email, tax_id]
      properties:
        name:
          type: string
        email:
          type: string
          format: email
        tax_id:
          type: string
    SplitRuleRequest:
      type: object
      properties:
        recipient_id:
          type: string
          format: uuid
        value_type:
          type: string
        value_bps:
          type: integer
        value_cents:
          type: integer
    ChargeResponse:
      type: object
      required: [charge_id, status, amount_cents, psp_charge_id]
      properties:
        charge_id:
          type: string
          format: uuid
        status:
          type: string
        amount_cents:
          type: integer
        psp_charge_id:
          type: string
        card_token_id:
          type: string
          format: uuid
        card_brand:
          type: string
        card_last4:
          type: string
        three_ds_acs_url:
          type: string
          deprecated: true
          description: Reservado; o fluxo direto atual do Asaas não oferece 3DS exigível.
        failure_classification:
          type: string
        pix_qr_code_base64:
          type: string
        pix_emv_code:
          type: string
        expires_at:
          type: string
          format: date-time
    ChargeCancellationRequest:
      type: object
      additionalProperties: false
      properties:
        reason:
          type: string
          enum: [payment_attempt_cancelled]
    ChargeCancellationResponse:
      type: object
      additionalProperties: false
      required: [charge_id, status, reason, cancelled_at]
      properties:
        charge_id:
          type: string
          format: uuid
        status:
          type: string
          enum: [cancelled, cancellation_pending]
        reason:
          type: string
          enum: [payment_attempt_cancelled]
        cancelled_at:
          type: [string, "null"]
          format: date-time
    ChargeListPage:
      type: object
      additionalProperties: false
      required: [data]
      properties:
        data:
          type: array
          maxItems: 100
          items:
            $ref: "#/components/schemas/ChargeListItem"
        next_cursor:
          type: string
          maxLength: 256
    ChargeListItem:
      type: object
      additionalProperties: false
      required: [charge_id, amount_cents, status, created_at]
      properties:
        charge_id:
          type: string
          format: uuid
        amount_cents:
          type: integer
          minimum: 1
        status:
          type: string
          enum: [created, pending, authorized, paid, partially_refunded, refunded, failed, expired, cancelled, disputed, chargeback]
        psp_charge_id:
          type: string
        card_token_id:
          type: string
          format: uuid
        pix_qr_code:
          type: string
        pix_copy_paste:
          type: string
        expires_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
    RefundRequest:
      type: object
      additionalProperties: false
      properties:
        amount_cents:
          type: integer
          minimum: 1
        full:
          type: boolean
          default: false
        reason:
          type: string
      oneOf:
        - required: [amount_cents]
        - required: [full]
          properties:
            full:
              const: true
    RefundListPage:
      type: object
      additionalProperties: false
      required: [refunds]
      properties:
        refunds:
          type: array
          maxItems: 100
          items:
            $ref: "#/components/schemas/RefundResponse"
        next_cursor:
          type: string
          maxLength: 256
    RefundResponse:
      type: object
      additionalProperties: false
      required: [refund_id, charge_id, amount_cents, status, requested_at]
      properties:
        refund_id:
          type: string
          format: uuid
        charge_id:
          type: string
          format: uuid
        amount_cents:
          type: integer
          minimum: 1
        status:
          type: string
          enum: [requested, processing, completed, failed, cancelled, unknown]
        psp_refund_id:
          type: string
        reason:
          type: string
        requested_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
        failure_reason:
          type: string
    CustomerRequest:
      type: object
      required: [name, email]
      properties:
        name:
          type: string
        email:
          type: string
          format: email
        tax_id:
          type: string
        phone:
          type: string
        birth_date:
          type: string
          format: date
        external_reference:
          type: string
        marketing_consent:
          type: boolean
    CustomerSummary:
      type: object
      required: [id, name, email, marketingConsent, createdAt]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        email:
          type: string
          format: email
        document:
          type: string
        phone:
          type: string
        marketingConsent:
          type: boolean
        anonymizedAt:
          type: string
          format: date-time
        createdAt:
          type: string
          format: date-time
    CustomerResponse:
      type: object
      required: [id, merchant_id, environment, name, email, marketing_consent, created_at, updated_at]
      properties:
        id:
          type: string
          format: uuid
        merchant_id:
          type: string
          format: uuid
        environment:
          type: string
        name:
          type: string
        email:
          type: string
          format: email
        tax_id:
          type: string
        phone:
          type: string
        birth_date:
          type: string
          format: date
        external_reference:
          type: string
        marketing_consent:
          type: boolean
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    CustomerAddressRequest:
      type: object
      required: [type, cep, street, number, city, state]
      properties:
        type:
          type: string
        cep:
          type: string
        street:
          type: string
        number:
          type: string
        complement:
          type: string
        neighborhood:
          type: string
        city:
          type: string
        state:
          type: string
        country:
          type: string
        is_default:
          type: boolean
    CustomerAddressDetail:
      type: object
      required: [id, customerId, type, cep, street, number, city, state, isDefault]
      properties:
        id:
          type: string
          format: uuid
        customerId:
          type: string
          format: uuid
        type:
          type: string
        cep:
          type: string
        street:
          type: string
        number:
          type: string
        complement:
          type: string
        neighborhood:
          type: string
        city:
          type: string
        state:
          type: string
        country:
          type: string
        isDefault:
          type: boolean
    CardTokenDetail:
      type: object
      required: [id, customerId, psp, last4, brand, holderName, expMonth, expYear, fingerprint, isDefault, consentRecurring, createdAt]
      properties:
        id:
          type: string
          format: uuid
        customerId:
          type: string
          format: uuid
        psp:
          type: string
        last4:
          type: string
        brand:
          type: string
        holderName:
          type: string
        expMonth:
          type: integer
        expYear:
          type: integer
        fingerprint:
          type: string
        isDefault:
          type: boolean
        consentRecurring:
          type: boolean
        createdAt:
          type: string
          format: date-time
    ProductRequest:
      type: object
      required: [name, type, base_amount_cents, currency]
      properties:
        name:
          type: string
        description:
          type: string
        sku:
          type: string
        category:
          type: string
        image_urls:
          type: array
          items:
            type: string
        type:
          type: string
        base_amount_cents:
          type: integer
          format: int64
          minimum: 0
          maximum: 9000000000000000
        currency:
          type: string
          enum: [BRL]
        delivery_type:
          type: string
        digital_file_url:
          type: string
        digital_file_name:
          type: string
        digital_file_size_bytes:
          type: integer
        digital_file_mime_type:
          type: string
        download_link_ttl_hours:
          type: integer
        max_downloads_per_purchase:
          type: integer
        max_installments:
          type: integer
          minimum: 1
          maximum: 1
        allowed_methods:
          type: array
          items:
            type: string
    ProductSummary:
      type: object
      required: [id, name, type, deliveryType, baseAmountCents, currency, status, createdAt]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        sku:
          type: string
        category:
          type: string
        type:
          type: string
        deliveryType:
          type: string
        baseAmountCents:
          type: integer
        currency:
          type: string
        status:
          type: string
        firstPaidChargeAt:
          type: string
          format: date-time
        createdAt:
          type: string
          format: date-time
    ProductResponse:
      type: object
      required:
        [id, merchant_id, environment, name, type, base_amount_cents, download_link_ttl_hours, max_downloads_per_purchase, max_installments, allowed_methods, status, created_at, updated_at]
      properties:
        id:
          type: string
          format: uuid
        merchant_id:
          type: string
          format: uuid
        environment:
          type: string
        name:
          type: string
        description:
          type: string
        sku:
          type: string
        category:
          type: string
        image_urls:
          type: array
          items:
            type: string
        type:
          type: string
        base_amount_cents:
          type: integer
        currency:
          type: string
        delivery_type:
          type: string
        digital_file_url:
          type: string
        digital_file_name:
          type: string
        digital_file_size_bytes:
          type: integer
        digital_file_mime_type:
          type: string
        download_link_ttl_hours:
          type: integer
        max_downloads_per_purchase:
          type: integer
        max_installments:
          type: integer
        allowed_methods:
          type: array
          items:
            type: string
        soft_descriptor:
          type: string
        status:
          type: string
        first_paid_charge_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        archived_at:
          type: string
          format: date-time
    ProductUploadRequest:
      type: object
      required: [file_name, mime_type, file_size_bytes]
      properties:
        file_name:
          type: string
        mime_type:
          type: string
        file_size_bytes:
          type: integer
    ProductUploadUrl:
      type: object
      required: [uploadId, uploadUrl, objectKey, blobUri, expiresAt]
      properties:
        uploadId:
          type: string
          format: uuid
        uploadUrl:
          type: string
        objectKey:
          type: string
        blobUri:
          type: string
        expiresAt:
          type: string
          format: date-time
    CreatePlanRequest:
      type: object
      required:
        [name, amount_cents, billing_period_unit, billing_period_interval]
      properties:
        name:
          type: string
        description:
          type: string
        amount_cents:
          type: integer
          format: int64
          minimum: 0
          maximum: 9000000000000000
        billing_period_unit:
          type: string
        billing_period_interval:
          type: integer
        trial_days:
          type: integer
        setup_fee_cents:
          type: integer
          format: int64
          minimum: 0
          maximum: 9000000000000000
    UpdatePlanRequest:
      type: object
      required: [name]
      properties:
        name:
          type: string
        description:
          type: string
        amount_cents:
          type: integer
          format: int64
          minimum: 0
          maximum: 9000000000000000
        billing_period_unit:
          type: string
        billing_period_interval:
          type: integer
        trial_days:
          type: integer
        setup_fee_cents:
          type: integer
          format: int64
          minimum: 0
          maximum: 9000000000000000
    PlanResponse:
      type: object
      required:
        [id, product_id, merchant_id, environment, name, amount_cents, currency, billing_period_unit, billing_period_interval, trial_days, setup_fee_cents, status, created_at, updated_at]
      properties:
        id:
          type: string
          format: uuid
        product_id:
          type: string
          format: uuid
        merchant_id:
          type: string
          format: uuid
        environment:
          type: string
        name:
          type: string
        description:
          type: string
        amount_cents:
          type: integer
          format: int64
          minimum: 0
          maximum: 9000000000000000
        currency:
          type: string
        billing_period_unit:
          type: string
        billing_period_interval:
          type: integer
        trial_days:
          type: integer
        setup_fee_cents:
          type: integer
          format: int64
          minimum: 0
          maximum: 9000000000000000
        status:
          type: string
        first_subscription_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        archived_at:
          type: string
          format: date-time
    CouponRequest:
      type: object
      required: [type, code, discount_type, discount_value]
      properties:
        type:
          type: string
        code:
          type: string
        description:
          type: string
        membership_id:
          type: string
          format: uuid
        discount_type:
          type: string
        discount_value:
          type: integer
        max_uses_total:
          type: integer
        max_uses_per_customer:
          type: integer
        min_purchase_amount_cents:
          type: integer
        first_purchase_only:
          type: boolean
        applicable_products:
          type: array
          items:
            type: string
            format: uuid
        applicable_plans:
          type: array
          items:
            type: string
            format: uuid
        valid_from:
          type: string
          format: date-time
        valid_until:
          type: string
          format: date-time
        created_by_account_id:
          type: string
          format: uuid
    CouponPatchRequest:
      type: object
      properties:
        max_uses_total:
          type: integer
        valid_until:
          type: string
          format: date-time
        status:
          type: string
    CouponSummary:
      type: object
      required: [id, type, code, discountType, discountValue, status, timesRedeemed]
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
        code:
          type: string
        discountType:
          type: string
        discountValue:
          type: integer
        status:
          type: string
        timesRedeemed:
          type: integer
        maxUsesTotal:
          type: integer
        validUntil:
          type: string
          format: date-time
    CouponDetail:
      type: object
      required:
        [id, merchantId, environment, type, code, discountType, discountValue, maxUsesPerCustomer, firstPurchaseOnly, applicableProducts, applicablePlans, status, timesRedeemed, createdAt, updatedAt]
      properties:
        id:
          type: string
          format: uuid
        merchantId:
          type: string
          format: uuid
        environment:
          type: string
        type:
          type: string
        code:
          type: string
        description:
          type: string
        membershipId:
          type: string
          format: uuid
        discountType:
          type: string
        discountValue:
          type: integer
        maxUsesTotal:
          type: integer
        maxUsesPerCustomer:
          type: integer
        minPurchaseAmountCents:
          type: integer
        firstPurchaseOnly:
          type: boolean
        applicableProducts:
          type: array
          items:
            type: string
            format: uuid
        applicablePlans:
          type: array
          items:
            type: string
            format: uuid
        validFrom:
          type: string
          format: date-time
        validUntil:
          type: string
          format: date-time
        status:
          type: string
        timesRedeemed:
          type: integer
        createdByAccountId:
          type: string
          format: uuid
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        disabledAt:
          type: string
          format: date-time
    CouponValidationRequest:
      type: object
      required: [code, amount_cents]
      properties:
        code:
          type: string
        customer_id:
          type: string
          format: uuid
        products:
          type: array
          items:
            type: string
            format: uuid
        plans:
          type: array
          items:
            type: string
            format: uuid
        amount_cents:
          type: integer
    CouponValidationResult:
      type: object
      required: [valid, discount_amount_cents]
      properties:
        valid:
          type: boolean
        coupon_id:
          type: string
          format: uuid
        discount_amount_cents:
          type: integer
        reason:
          type: string
        membership_id:
          type: string
          format: uuid
    CreateCheckoutSessionRequest:
      type: object
      required: [items, success_url, cancel_url]
      properties:
        items:
          type: array
          minItems: 1
          maxItems: 100
          items:
            $ref: "#/components/schemas/CheckoutItemRequest"
        success_url:
          type: string
          format: uri
          maxLength: 2048
        cancel_url:
          type: string
          format: uri
          maxLength: 2048
        customer_id:
          type: string
          format: uuid
        customer_data:
          $ref: "#/components/schemas/CheckoutCustomerDataRequest"
        allowed_payment_methods:
          type: array
          minItems: 1
          maxItems: 1
          uniqueItems: true
          items:
            type: string
            enum: [pix]
        affiliate_code:
          type: string
        coupon_id:
          type: string
          format: uuid
        utm_params:
          type: object
          maxProperties: 20
          propertyNames:
            minLength: 1
            maxLength: 64
          additionalProperties:
            type: string
            maxLength: 256
        expires_in_minutes:
          type: integer
          minimum: 1
          maximum: 1440
          default: 30
        metadata:
          type: object
          maxProperties: 50
          propertyNames:
            minLength: 1
            maxLength: 64
          additionalProperties: true
          description: >
            JSON limitado a 4 níveis, 200 nós, strings de até 1000 caracteres e peso
            estrutural total de 16 KiB.
        collect_shipping_address:
          type: boolean
        delivery_type:
          type: string
        allow_coupons:
          type: boolean
          default: true
          description: Omissão ou null preserva o comportamento legado e permite cupons.
    CheckoutItemRequest:
      type: object
      required: [name, quantity, unit_amount_cents]
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 200
        quantity:
          type: integer
          minimum: 1
          maximum: 10000
        unit_amount_cents:
          type: integer
          format: int64
          minimum: 100
    CheckoutCustomerDataRequest:
      type: object
      properties:
        name:
          type: string
          maxLength: 200
        email:
          type: string
          format: email
          maxLength: 254
        document:
          type: string
          maxLength: 32
        phone:
          type: string
          maxLength: 32
    SelectCheckoutMethodRequest:
      type: object
      additionalProperties: false
      required: [payment_method]
      properties:
        payment_method:
          type: string
          enum: [pix]
        customer_data:
          $ref: "#/components/schemas/CheckoutCustomerDataRequest"
    ApplyCheckoutCouponRequest:
      type: object
      required: [code]
      properties:
        code:
          type: string
    PayCheckoutSessionRequest:
      type: object
      additionalProperties: false
      required: [payment_method]
      properties:
        payment_method:
          type: string
          enum: [pix]
        customer_data:
          $ref: "#/components/schemas/CheckoutCustomerDataRequest"
    CreateCheckoutSessionResponse:
      type: object
      required: [id, url, expires_at]
      properties:
        id:
          type: string
          format: uuid
        url:
          type: string
        expires_at:
          type: string
          format: date-time
    CheckoutBrandingResponse:
      type: object
      properties:
        display_name:
          type: string
        logo_url:
          type: string
        primary_color:
          type: string
        accent_color:
          type: string
        footer_text:
          type: string
        custom_domain:
          type: string
    CheckoutInstallmentResponse:
      type: object
      required: [number, installment_amount_cents, total_amount_cents, interest_free]
      properties:
        number:
          type: integer
        installment_amount_cents:
          type: integer
        total_amount_cents:
          type: integer
        interest_free:
          type: boolean
    CheckoutCardTokenizationResponse:
      type: object
      properties:
        provider:
          type: string
        public_key:
          type: string
        environment:
          type: string
        tokenization_url:
          type: string
    CheckoutCustomerPrefillResponse:
      type: object
      properties:
        name:
          type: string
        email:
          type: string
        document:
          type: string
        phone:
          type: string
    CheckoutPublicItem:
      type: object
      required: [name, quantity, unitAmountCents]
      properties:
        name:
          type: string
        quantity:
          type: integer
        unitAmountCents:
          type: integer
    CheckoutPublicResponse:
      type: object
      required:
        [id, status, items, amount_subtotal_cents, amount_discount_cents, amount_total_cents, currency, allowed_payment_methods, allow_coupons, collect_shipping_address, customer_data_provided, max_installments, installments, expires_at, metadata]
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
        items:
          type: array
          items:
            $ref: "#/components/schemas/CheckoutPublicItem"
        amount_subtotal_cents:
          type: integer
        amount_discount_cents:
          type: integer
        amount_total_cents:
          type: integer
        currency:
          type: string
        allowed_payment_methods:
          type: array
          minItems: 1
          maxItems: 1
          items:
            type: string
            enum: [pix]
        allow_coupons:
          type: boolean
        merchant_display_name:
          type: string
        merchant_branding:
          $ref: "#/components/schemas/CheckoutBrandingResponse"
        collect_shipping_address:
          type: boolean
        delivery_type:
          type: string
        customer_data_provided:
          type: boolean
        customer_prefill:
          $ref: "#/components/schemas/CheckoutCustomerPrefillResponse"
        max_installments:
          type: integer
        installments:
          type: array
          items:
            $ref: "#/components/schemas/CheckoutInstallmentResponse"
        card_tokenization:
          $ref: "#/components/schemas/CheckoutCardTokenizationResponse"
        expires_at:
          type: string
          format: date-time
        metadata:
          type: object
          additionalProperties: true
    CheckoutSummaryResponse:
      type: object
      required: [amount_subtotal_cents, amount_discount_cents, amount_total_cents]
      properties:
        amount_subtotal_cents:
          type: integer
        amount_discount_cents:
          type: integer
        amount_total_cents:
          type: integer
        coupon_id:
          type: string
          format: uuid
        coupon_description:
          type: string
    CheckoutPaymentResponse:
      type: object
      required: [charge_id, status]
      properties:
        charge_id:
          type: string
          format: uuid
        status:
          type: string
        pix_emv_code:
          type: string
        pix_qr_code_base64:
          type: string
        three_ds_acs_url:
          type: string
        expires_at:
          type: string
          format: date-time
    CheckoutStatusResponse:
      type: object
      required:
        [session_id, session_status, amount_subtotal_cents, amount_discount_cents, amount_total_cents, expires_at]
      properties:
        session_id:
          type: string
          format: uuid
        session_status:
          type: string
        charge_id:
          type: string
          format: uuid
        charge_status:
          type: string
        pix_emv_code:
          type: string
        pix_qr_code_base64:
          type: string
        three_ds_acs_url:
          type: string
        decline_reason:
          type: string
        amount_subtotal_cents:
          type: integer
        amount_discount_cents:
          type: integer
        amount_total_cents:
          type: integer
        delivery_type:
          type: string
        digital_delivery_url:
          type: string
        magic_link:
          type: string
          nullable: true
          deprecated: true
          description: Sempre nulo. O acesso ao portal é entregue por canal privado ao comprador.
        next_charge_at:
          type: string
          format: date-time
        expires_at:
          type: string
          format: date-time
        payment_method:
          type: string
        card_brand:
          type: string
        card_last4:
          type: string
        failure_classification:
          type: string
    CreateSubscriptionRequest:
      type: object
      required: [customer_id, plan_id, payment_method]
      properties:
        customer_id:
          type: string
          format: uuid
        plan_id:
          type: string
          format: uuid
        payment_method:
          type: string
        card_token_id:
          type: string
          format: uuid
        coupon_id:
          type: string
          format: uuid
        trial_days:
          type: integer
        affiliate_code:
          type: string
        metadata:
          type: object
          additionalProperties: true
        external_reference:
          type: string
    PauseSubscriptionRequest:
      type: object
      properties:
        resume_at:
          type: string
          format: date-time
    CancelSubscriptionRequest:
      type: object
      required: [mode]
      properties:
        mode:
          type: string
        reason:
          type: string
    ChangeSubscriptionPlanRequest:
      type: object
      required: [new_plan_id, prorate_mode, effective]
      properties:
        new_plan_id:
          type: string
          format: uuid
        prorate_mode:
          type: string
        effective:
          type: string
    UpdateSubscriptionCardRequest:
      type: object
      required: [card_token_id]
      properties:
        card_token_id:
          type: string
          format: uuid
    PatchSubscriptionRequest:
      type: object
      properties:
        metadata:
          type: object
          additionalProperties: true
        external_reference:
          type: string
    SubscriptionDetail:
      type: object
      required:
        [id, customer_id, plan_id, payment_method, status, cancel_at_period_end, metadata]
      properties:
        id:
          type: string
          format: uuid
        customer_id:
          type: string
          format: uuid
        plan_id:
          type: string
          format: uuid
        payment_method:
          type: string
        card_token_id:
          type: string
          format: uuid
        status:
          type: string
        current_period_start:
          type: string
          format: date-time
        current_period_end:
          type: string
          format: date-time
        next_billing_date:
          type: string
          format: date-time
        trialing_until:
          type: string
          format: date-time
        paused_at:
          type: string
          format: date-time
        paused_resume_at:
          type: string
          format: date-time
        cancel_at_period_end:
          type: boolean
        cancel_at:
          type: string
          format: date-time
        canceled_at:
          type: string
          format: date-time
        cancellation_reason:
          type: string
        metadata:
          type: object
          additionalProperties: true
        external_reference:
          type: string
