{
  "components": {
    "schemas": {
      "ApiError": {
        "properties": {
          "code": {
            "description": "문제를 구분하는 오류 코드를 알려줘요.",
            "example": "VALIDATION_FAILED",
            "type": "string"
          },
          "details": {
            "description": "검증에 실패한 필드나 추가 원인을 설명해요.",
            "example": {
              "fields": [
                {
                  "field": "productId",
                  "message": "필수 값이에요."
                }
              ]
            },
            "oneOf": [
              {
                "additionalProperties": true,
                "description": "VALIDATION_FAILED 오류에서 필드별 검증 문제를 알려줘요.",
                "example": {
                  "fields": [
                    {
                      "field": "productId",
                      "message": "필수 값이에요."
                    }
                  ]
                },
                "not": {
                  "required": [
                    "required"
                  ]
                },
                "properties": {
                  "fields": {
                    "description": "문제가 있는 필드와 원인이에요.",
                    "items": {
                      "properties": {
                        "field": {
                          "description": "검증에 실패한 필드 이름이에요.",
                          "type": "string"
                        },
                        "message": {
                          "description": "필드 값이 올바르지 않은 이유예요.",
                          "type": "string"
                        }
                      },
                      "required": [
                        "field",
                        "message"
                      ],
                      "type": "object"
                    },
                    "type": "array"
                  }
                },
                "required": [
                  "fields"
                ],
                "type": "object"
              },
              {
                "additionalProperties": true,
                "description": "API_KEY_SCOPE_MISSING 오류에서 필요한 권한을 알려줘요.",
                "example": {
                  "required": [
                    "orders:read"
                  ]
                },
                "not": {
                  "required": [
                    "fields"
                  ]
                },
                "properties": {
                  "required": {
                    "description": "요청에 필요한 API 키 권한 목록이에요.",
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  }
                },
                "required": [
                  "required"
                ],
                "type": "object"
              },
              {
                "additionalProperties": true,
                "description": "그 밖의 오류가 제공하는 추가 정보를 담아요.",
                "example": {
                  "retryable": true
                },
                "not": {
                  "anyOf": [
                    {
                      "required": [
                        "fields"
                      ]
                    },
                    {
                      "required": [
                        "required"
                      ]
                    }
                  ]
                },
                "type": "object"
              }
            ]
          },
          "message": {
            "description": "오류 원인과 해결 방법을 한국어로 설명해요.",
            "example": "요청 필드를 확인해 주세요.",
            "type": "string"
          },
          "statusCode": {
            "description": "HTTP 상태 코드를 알려줘요.",
            "example": 400,
            "type": "number"
          }
        },
        "required": [
          "statusCode",
          "code",
          "message"
        ],
        "type": "object"
      },
      "CreatePortalSessionV1Dto": {
        "properties": {
          "buyerEmail": {
            "description": "구매자 이메일로 활성 구독을 찾아요.",
            "example": "buyer@example.com",
            "format": "email",
            "type": "string"
          },
          "returnUrl": {
            "description": "포털에서 돌아갈 때 사용할 URL이에요.",
            "example": "https://shop.example.com/account",
            "type": "string"
          },
          "subscriptionId": {
            "description": "포털 세션을 만들 구독 식별자예요.",
            "example": "sub_123",
            "type": "string"
          }
        },
        "type": "object"
      },
      "V1BlockedReason": {
        "properties": {
          "code": {
            "description": "즉시 해지가 차단된 이유 코드예요.",
            "enum": [
              "SUBSCRIPTION_REFUND_SETTLED",
              "REFUND_REQUEST_PENDING",
              "ALREADY_ENDED"
            ],
            "example": "REFUND_REQUEST_PENDING",
            "type": "string"
          },
          "message": {
            "description": "즉시 해지가 차단된 이유예요.",
            "example": "처리 중인 환불 요청이 있어요.",
            "type": "string"
          }
        },
        "required": [
          "code",
          "message"
        ],
        "type": "object"
      },
      "V1CancelBreakdownItem": {
        "properties": {
          "amount": {
            "description": "해당 주문에서 환불할 금액이에요.",
            "example": 5000,
            "type": "number"
          },
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "orderId": {
            "description": "환불 계산 대상 주문 식별자예요.",
            "example": "ord_123",
            "type": "string"
          }
        },
        "required": [
          "orderId",
          "amount",
          "currency"
        ],
        "type": "object"
      },
      "V1CancelPeriodEndPreview": {
        "properties": {
          "effectiveAt": {
            "description": "기간 종료 해지 효력이 발생할 시각이에요.",
            "example": "2026-11-01T00:00:00.000Z",
            "type": "string"
          }
        },
        "required": [
          "effectiveAt"
        ],
        "type": "object"
      },
      "V1CancelPreviewResponse": {
        "properties": {
          "immediate": {
            "allOf": [
              {
                "$ref": "#/components/schemas/V1ImmediateCancelPreview"
              }
            ],
            "description": "즉시 해지 미리보기예요."
          },
          "mode": {
            "description": "test 또는 live 구독 환경을 알려줘요.",
            "enum": [
              "live",
              "test"
            ],
            "example": "live",
            "type": "string"
          },
          "pendingRefundRequest": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1RefundRequestResponse"
                  }
                ],
                "description": "처리 중인 환불 요청 정보예요."
              },
              {
                "type": "null"
              }
            ],
            "description": "처리 중인 환불 요청 정보예요."
          },
          "periodEnd": {
            "allOf": [
              {
                "$ref": "#/components/schemas/V1CancelPeriodEndPreview"
              }
            ],
            "description": "기간 종료 해지 미리보기예요."
          }
        },
        "required": [
          "mode",
          "periodEnd",
          "immediate",
          "pendingRefundRequest"
        ],
        "type": "object"
      },
      "V1CancelResultResponse": {
        "properties": {
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "refundAmount": {
            "description": "즉시 환불한 금액이에요.",
            "example": 0,
            "type": "number"
          },
          "refundOrderId": {
            "description": "즉시 환불한 주문 식별자예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "refundedOrderIds": {
            "description": "해지 처리 중 환불한 주문 식별자 목록이에요.",
            "example": [
              "ord_123"
            ],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "subscription": {
            "allOf": [
              {
                "$ref": "#/components/schemas/V1SubscriptionResponse"
              }
            ],
            "description": "해지 후 구독 정보예요."
          },
          "timing": {
            "description": "해지 시점이에요.",
            "enum": [
              "period_end",
              "immediate"
            ],
            "example": "period_end",
            "type": "string"
          }
        },
        "required": [
          "subscription",
          "refundOrderId",
          "refundAmount",
          "currency",
          "refundedOrderIds",
          "timing"
        ],
        "type": "object"
      },
      "V1CancelSubscriptionDto": {
        "properties": {
          "reason": {
            "description": "해지 사유예요.",
            "example": "서비스 이용을 중단해요.",
            "type": "string"
          },
          "timing": {
            "description": "기간 종료 시점 또는 즉시 해지 시점을 지정해요.",
            "enum": [
              "period_end",
              "immediate"
            ],
            "example": "period_end",
            "type": "string"
          }
        },
        "required": [
          "timing"
        ],
        "type": "object"
      },
      "V1CheckoutCustomer": {
        "properties": {
          "email": {
            "description": "결제 구매자의 이메일 주소예요.",
            "example": "buyer@example.com",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "description": "결제 구매자의 이름이에요.",
            "example": "홍길동",
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "description": "결제 구매자의 연락처예요.",
            "example": "01012345678",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "name",
          "email",
          "phone"
        ],
        "type": "object"
      },
      "V1CheckoutCustomerDto": {
        "properties": {
          "email": {
            "description": "결제 구매자의 이메일 주소예요.",
            "example": "buyer@example.com",
            "type": "string"
          },
          "name": {
            "description": "결제 구매자의 이름이에요.",
            "example": "홍길동",
            "type": "string"
          },
          "phone": {
            "description": "결제 구매자의 연락처예요.",
            "example": "01012345678",
            "type": "string"
          }
        },
        "type": "object"
      },
      "V1CheckoutSessionResponse": {
        "properties": {
          "cancelUrl": {
            "description": "결제 취소 후 이동할 URL이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "description": "세션이 생성된 시각이에요.",
            "example": "2026-10-15T00:00:00.000Z",
            "type": "string"
          },
          "customer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/V1CheckoutCustomer"
              }
            ],
            "description": "세션에 사전 입력한 구매자 정보예요."
          },
          "expiresAt": {
            "description": "세션 생성 후 30분이 되는 만료 시각이에요.",
            "example": "2026-10-15T00:30:00.000Z",
            "type": "string"
          },
          "id": {
            "description": "결제 세션 식별자예요.",
            "example": "cs_123",
            "type": "string"
          },
          "metadata": {
            "description": "요청 때 전달한 메타데이터예요.",
            "example": {
              "campaign": "fall"
            },
            "type": [
              "object",
              "null"
            ]
          },
          "mode": {
            "description": "test 또는 live 결제 환경을 알려줘요.",
            "enum": [
              "live",
              "test"
            ],
            "example": "test",
            "type": "string"
          },
          "orderId": {
            "description": "결제가 완료된 주문 식별자예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "productId": {
            "description": "상품 식별자예요.",
            "example": "prod_123",
            "type": "string"
          },
          "status": {
            "description": "결제 세션 상태예요.",
            "enum": [
              "open",
              "completed",
              "expired"
            ],
            "example": "open",
            "type": "string"
          },
          "successUrl": {
            "description": "결제 완료 후 이동할 URL이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "description": "구매자가 열 결제 페이지 URL이에요.",
            "example": "https://payri.kr/checkout/cs_123",
            "type": "string"
          }
        },
        "required": [
          "id",
          "mode",
          "productId",
          "status",
          "orderId",
          "customer",
          "metadata",
          "successUrl",
          "cancelUrl",
          "expiresAt",
          "createdAt",
          "url"
        ],
        "type": "object"
      },
      "V1CreateCheckoutSessionDto": {
        "properties": {
          "cancelUrl": {
            "description": "결제가 취소된 뒤 구매자를 돌려보낼 URL이에요.",
            "example": "https://shop.example.com/cancel",
            "type": "string"
          },
          "customer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/V1CheckoutCustomerDto"
              }
            ],
            "description": "구매자 사전 입력 정보예요."
          },
          "interval": {
            "description": "구독 상품의 결제 주기예요.",
            "enum": [
              "monthly",
              "yearly"
            ],
            "example": "monthly",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "결제 후 웹훅과 조회 응답에서 받을 문자열 메타데이터예요.",
            "example": {
              "campaign": "fall"
            },
            "type": "object"
          },
          "planId": {
            "description": "구독 상품의 플랜 식별자예요.",
            "example": "plan_basic",
            "type": "string"
          },
          "productId": {
            "description": "결제할 상품 식별자예요.",
            "example": "prod_123",
            "type": "string"
          },
          "successUrl": {
            "description": "결제 완료 후 구매자를 돌려보낼 URL이에요.",
            "example": "https://shop.example.com/complete",
            "type": "string"
          }
        },
        "required": [
          "productId"
        ],
        "type": "object"
      },
      "V1ImmediateCancelPreview": {
        "properties": {
          "blockedReason": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1BlockedReason"
                  }
                ],
                "description": "즉시 해지가 불가능한 이유예요."
              },
              {
                "type": "null"
              }
            ],
            "description": "즉시 해지가 불가능한 이유예요."
          },
          "breakdown": {
            "description": "주문별 환불 금액 내역이에요.",
            "items": {
              "$ref": "#/components/schemas/V1CancelBreakdownItem"
            },
            "type": "array"
          },
          "clawback": {
            "description": "연간 할인 환수 금액이에요.",
            "example": 0,
            "type": "number"
          },
          "clawbackCurrency": {
            "description": "환수 금액 통화예요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "deduction": {
            "description": "환불액에서 차감할 금액이에요.",
            "example": 0,
            "type": "number"
          },
          "deductionCap": {
            "description": "차감할 수 있는 최대 금액이에요.",
            "example": 0,
            "type": "number"
          },
          "deductionCapCurrency": {
            "description": "차감 한도 통화예요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "deductionCurrency": {
            "description": "차감 금액 통화예요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "fullRefund": {
            "description": "전액 환불인지 알려줘요.",
            "example": false,
            "type": "boolean"
          },
          "penalty": {
            "description": "위약금 금액이에요.",
            "example": 0,
            "type": "number"
          },
          "penaltyCurrency": {
            "description": "위약금 통화예요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "reason": {
            "description": "환불 계산 사유예요.",
            "enum": [
              "initial_full",
              "statutory_full",
              "renewal_full",
              "prorated",
              "past_due",
              "nothing_refundable"
            ],
            "example": "prorated",
            "type": "string"
          },
          "refundAmount": {
            "description": "즉시 해지 시 환불 예정 금액이에요.",
            "example": 5000,
            "type": "number"
          },
          "requiresApproval": {
            "description": "환불 요청의 판매자 승인이 필요한지 알려줘요.",
            "example": false,
            "type": "boolean"
          },
          "statutory": {
            "description": "법정 환불 기준 적용 여부예요.",
            "example": false,
            "type": "boolean"
          },
          "totalDays": {
            "description": "환불 계산에 사용하는 전체 일수예요.",
            "example": 30,
            "type": "number"
          },
          "usedDays": {
            "description": "구독 시작 후 사용한 일수예요.",
            "example": 5,
            "type": "number"
          }
        },
        "required": [
          "refundAmount",
          "currency",
          "fullRefund",
          "reason",
          "breakdown",
          "deduction",
          "deductionCurrency",
          "penalty",
          "penaltyCurrency",
          "clawback",
          "clawbackCurrency",
          "deductionCap",
          "deductionCapCurrency",
          "usedDays",
          "totalDays",
          "statutory",
          "requiresApproval",
          "blockedReason"
        ],
        "type": "object"
      },
      "V1LicenseActivateRequestDto": {
        "properties": {
          "key": {
            "description": "활성화할 라이선스 키예요.",
            "example": "PAYRI-ABCDE-FGHJK-MNPQR-STUVW",
            "type": "string"
          },
          "label": {
            "description": "기기 이름이에요.",
            "example": "내 컴퓨터",
            "type": "string"
          },
          "nonce": {
            "description": "응답 위조와 재전송을 확인할 nonce예요. hex 또는 base64로 보내고, 디코딩한 값은 16바이트 이상이어야 해요. 32바이트 hex를 권장해요.",
            "example": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
            "minLength": 22,
            "pattern": "^(?:(?:[A-Fa-f0-9]{2}){16,}|[A-Za-z0-9+/_-]{22,}={0,2})$",
            "type": "string"
          },
          "productId": {
            "description": "라이선스가 연결된 상품 식별자예요.",
            "example": "prod_123",
            "type": "string"
          }
        },
        "required": [
          "key",
          "productId",
          "nonce"
        ],
        "type": "object"
      },
      "V1LicenseActivation": {
        "properties": {
          "activatedAt": {
            "description": "활성화가 시작된 시각이에요.",
            "example": "2026-10-15T00:00:00.000Z",
            "type": "string"
          },
          "deactivatedAt": {
            "description": "활성화가 해제된 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "활성화 식별자예요.",
            "example": "act_123",
            "type": "string"
          },
          "label": {
            "description": "앱이 지정한 기기 이름이에요.",
            "example": "내 컴퓨터",
            "type": [
              "string",
              "null"
            ]
          },
          "lastVerifiedAt": {
            "description": "마지막 검증 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "activatedAt",
          "lastVerifiedAt",
          "deactivatedAt",
          "label"
        ],
        "type": "object"
      },
      "V1LicenseAppActivation": {
        "properties": {
          "activatedAt": {
            "description": "활성화가 시작된 시각이에요.",
            "example": "2026-10-15T00:00:00.000Z",
            "type": "string"
          },
          "deactivatedAt": {
            "description": "비활성화 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "서버가 발급한 활성화 식별자예요.",
            "example": "act_123",
            "type": "string"
          },
          "label": {
            "description": "활성화할 기기의 이름이에요.",
            "example": "내 컴퓨터",
            "type": [
              "string",
              "null"
            ]
          },
          "lastVerifiedAt": {
            "description": "마지막 검증 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "label",
          "activatedAt",
          "lastVerifiedAt",
          "deactivatedAt"
        ],
        "type": "object"
      },
      "V1LicenseAppResponse": {
        "properties": {
          "activation": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1LicenseAppActivation"
                  }
                ],
                "description": "이번 요청과 연결된 활성화 정보예요."
              },
              {
                "type": "null"
              }
            ],
            "description": "이번 요청과 연결된 활성화 정보예요."
          },
          "activationCount": {
            "description": "현재 활성화 수예요.",
            "example": 1,
            "type": "number"
          },
          "expiresAt": {
            "description": "라이선스 만료 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "issuedAt": {
            "description": "응답을 만든(서명한) 시각이에요. 앱은 이 값이 현재 시각에서 5분 이내인지 확인해 오래된 응답 재사용을 막아요.",
            "example": "2026-10-15T00:01:00.000Z",
            "type": "string"
          },
          "maxActivations": {
            "description": "허용된 최대 활성화 수예요. 무제한이면 null이에요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "mode": {
            "description": "test 또는 live 라이선스 환경을 알려줘요.",
            "enum": [
              "live",
              "test"
            ],
            "example": "live",
            "type": "string"
          },
          "nonce": {
            "description": "요청 때 보낸 16바이트 이상 nonce를 그대로 돌려줘요. hex 또는 base64로 보내며 32바이트 hex를 권장해요.",
            "example": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
            "type": "string"
          },
          "status": {
            "description": "라이선스 검증 결과예요.",
            "enum": [
              "active",
              "expired",
              "revoked",
              "pending",
              "not_found"
            ],
            "example": "active",
            "type": "string"
          }
        },
        "required": [
          "status",
          "expiresAt",
          "maxActivations",
          "activationCount",
          "mode",
          "activation",
          "nonce",
          "issuedAt"
        ],
        "type": "object"
      },
      "V1LicenseDeactivateRequestDto": {
        "properties": {
          "activationId": {
            "description": "해제할 활성화 식별자예요.",
            "example": "act_123",
            "type": "string"
          },
          "key": {
            "description": "비활성화할 라이선스 키예요.",
            "example": "PAYRI-ABCDE-FGHJK-MNPQR-STUVW",
            "type": "string"
          },
          "nonce": {
            "description": "응답 위조와 재전송을 확인할 nonce예요. hex 또는 base64로 보내고, 디코딩한 값은 16바이트 이상이어야 해요. 32바이트 hex를 권장해요.",
            "example": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
            "minLength": 22,
            "pattern": "^(?:(?:[A-Fa-f0-9]{2}){16,}|[A-Za-z0-9+/_-]{22,}={0,2})$",
            "type": "string"
          },
          "productId": {
            "description": "라이선스가 연결된 상품 식별자예요.",
            "example": "prod_123",
            "type": "string"
          }
        },
        "required": [
          "key",
          "productId",
          "nonce",
          "activationId"
        ],
        "type": "object"
      },
      "V1LicenseIssueBuyer": {
        "properties": {
          "email": {
            "description": "구매자 이메일 주소예요.",
            "example": "buyer@example.com",
            "type": "string"
          },
          "name": {
            "description": "구매자 이름이에요.",
            "example": "홍길동",
            "type": "string"
          }
        },
        "required": [
          "email",
          "name"
        ],
        "type": "object"
      },
      "V1LicenseIssueRequestData": {
        "properties": {
          "buyer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/V1LicenseIssueBuyer"
              }
            ],
            "description": "라이선스 구매자 정보예요."
          },
          "expiresAt": {
            "description": "라이선스 만료 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "interval": {
            "description": "구독 라이선스의 결제 주기예요.",
            "enum": [
              "monthly",
              "yearly",
              null
            ],
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "licenseId": {
            "description": "발급 대기 중인 라이선스 식별자예요.",
            "example": "lic_123",
            "type": "string"
          },
          "maxActivations": {
            "description": "라이선스의 최대 활성화 수예요. 무제한이면 null이에요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "mode": {
            "description": "test 또는 live 이벤트 환경을 알려줘요.",
            "enum": [
              "live",
              "test"
            ],
            "example": "live",
            "type": "string"
          },
          "orderId": {
            "description": "라이선스 발급을 요청한 주문 식별자예요.",
            "example": "ord_123",
            "type": "string"
          },
          "planId": {
            "description": "구독 라이선스의 플랜 식별자예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "productId": {
            "description": "라이선스 발급 대상 상품 식별자예요.",
            "example": "prod_123",
            "type": "string"
          }
        },
        "required": [
          "mode",
          "licenseId",
          "orderId",
          "productId",
          "planId",
          "interval",
          "buyer",
          "maxActivations",
          "expiresAt"
        ],
        "type": "object"
      },
      "V1LicenseIssueResponse": {
        "properties": {
          "expiresAt": {
            "description": "발급한 키의 만료 시각이에요. 생략하거나 null이면 요청 data.expiresAt을 그대로 써요. 구독 라이선스는 갱신 때 주기 말로 다시 연장돼요.",
            "example": "2027-10-15T00:00:00.000Z",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "key": {
            "description": "발급한 라이선스 키예요. 1자 이상 256자 이하 문자열이어야 해요. 요청 data에 key를 더해 그대로 돌려줘도 돼요. key·expiresAt·maxActivations 외 필드는 무시해요.",
            "example": "VENDOR-12345-ABCDE",
            "maxLength": 256,
            "minLength": 1,
            "type": "string"
          },
          "maxActivations": {
            "description": "발급한 키의 최대 활성화 수예요. null이면 무제한이고, 생략하면 요청 data.maxActivations(상품 설정)를 그대로 써요. 0도 무제한으로 받지만 null을 권장해요.",
            "example": null,
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "key"
        ],
        "type": "object"
      },
      "V1LicensePageResponse": {
        "properties": {
          "data": {
            "description": "페이지에 포함된 라이선스 목록이에요.",
            "items": {
              "$ref": "#/components/schemas/V1LicenseResponse"
            },
            "type": "array"
          },
          "limit": {
            "description": "페이지 크기예요.",
            "example": 20,
            "type": "number"
          },
          "page": {
            "description": "현재 페이지 번호예요.",
            "example": 1,
            "type": "number"
          },
          "total": {
            "description": "조건에 맞는 전체 항목 수예요.",
            "example": 1,
            "type": "number"
          }
        },
        "required": [
          "page",
          "limit",
          "total",
          "data"
        ],
        "type": "object"
      },
      "V1LicenseResponse": {
        "properties": {
          "activationCount": {
            "description": "현재 활성화 수예요.",
            "example": 1,
            "type": "number"
          },
          "activations": {
            "description": "이 라이선스의 활성화 정보예요.",
            "items": {
              "$ref": "#/components/schemas/V1LicenseActivation"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "buyerEmail": {
            "description": "구매자 이메일 주소예요.",
            "example": "buyer@example.com",
            "type": "string"
          },
          "buyerName": {
            "description": "구매자 이름이에요.",
            "example": "홍길동",
            "type": "string"
          },
          "expiresAt": {
            "description": "라이선스 만료 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "라이선스 식별자예요.",
            "example": "lic_123",
            "type": "string"
          },
          "issuedAt": {
            "description": "라이선스 발급 시각이에요.",
            "example": "2026-10-15T00:00:00.000Z",
            "type": "string"
          },
          "key": {
            "description": "발급된 라이선스 키예요.",
            "example": "PAYRI-ABCDE-FGHJK-MNPQR-STUVW",
            "type": [
              "string",
              "null"
            ]
          },
          "maxActivations": {
            "description": "허용된 최대 활성화 수예요. 무제한이면 null이에요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "mode": {
            "description": "test 또는 live 라이선스 환경을 알려줘요.",
            "enum": [
              "live",
              "test"
            ],
            "example": "live",
            "type": "string"
          },
          "orderId": {
            "description": "주문 식별자예요.",
            "example": "ord_123",
            "type": "string"
          },
          "orderItemId": {
            "description": "주문 항목 식별자예요.",
            "example": "item_123",
            "type": [
              "string",
              "null"
            ]
          },
          "productId": {
            "description": "상품 식별자예요.",
            "example": "prod_123",
            "type": "string"
          },
          "productName": {
            "description": "상품 이름이에요.",
            "example": "디지털 가이드",
            "type": "string"
          },
          "revokeReason": {
            "description": "라이선스 취소 사유예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "revokedAt": {
            "description": "라이선스가 취소된 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "rotatedFrom": {
            "description": "교체 전 라이선스 식별자예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "description": "라이선스 상태예요.",
            "enum": [
              "active",
              "pending",
              "expired",
              "revoked"
            ],
            "example": "active",
            "type": "string"
          }
        },
        "required": [
          "id",
          "mode",
          "key",
          "productId",
          "productName",
          "orderId",
          "buyerEmail",
          "buyerName",
          "status",
          "maxActivations",
          "issuedAt",
          "orderItemId",
          "expiresAt",
          "revokedAt",
          "revokeReason",
          "rotatedFrom",
          "activationCount",
          "activations"
        ],
        "type": "object"
      },
      "V1LicenseSigningKey": {
        "properties": {
          "createdAt": {
            "description": "공개키가 유효해진 시각이에요.",
            "example": "2026-10-01T00:00:00.000Z",
            "type": "string"
          },
          "kid": {
            "description": "공개키 식별자예요.",
            "example": "2026-10",
            "type": "string"
          },
          "publicKey": {
            "description": "Ed25519 원시 32바이트 공개키의 Base64 값이에요.",
            "example": "AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA=",
            "type": "string"
          }
        },
        "required": [
          "kid",
          "publicKey",
          "createdAt"
        ],
        "type": "object"
      },
      "V1LicenseSigningKeysResponse": {
        "properties": {
          "keys": {
            "description": "라이선스 응답 서명 검증에 사용할 공개키 목록이에요.",
            "items": {
              "$ref": "#/components/schemas/V1LicenseSigningKey"
            },
            "type": "array"
          }
        },
        "required": [
          "keys"
        ],
        "type": "object"
      },
      "V1LicenseVerifyRequestDto": {
        "properties": {
          "activationId": {
            "description": "상태를 확인할 활성화 식별자예요.",
            "example": "act_123",
            "type": "string"
          },
          "key": {
            "description": "검증할 라이선스 키예요.",
            "example": "PAYRI-ABCDE-FGHJK-MNPQR-STUVW",
            "type": "string"
          },
          "nonce": {
            "description": "응답 위조와 재전송을 확인할 nonce예요. hex 또는 base64로 보내고, 디코딩한 값은 16바이트 이상이어야 해요. 32바이트 hex를 권장해요.",
            "example": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
            "minLength": 22,
            "pattern": "^(?:(?:[A-Fa-f0-9]{2}){16,}|[A-Za-z0-9+/_-]{22,}={0,2})$",
            "type": "string"
          },
          "productId": {
            "description": "라이선스가 연결된 상품 식별자예요.",
            "example": "prod_123",
            "type": "string"
          }
        },
        "required": [
          "key",
          "productId",
          "nonce"
        ],
        "type": "object"
      },
      "V1OrderCoupon": {
        "properties": {
          "code": {
            "description": "쿠폰 코드를 알려줘요.",
            "example": "WELCOME10",
            "type": "string"
          },
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "discountAmount": {
            "description": "할인 금액을 원 단위로 알려줘요.",
            "example": 1000,
            "type": "number"
          },
          "name": {
            "description": "쿠폰 이름을 알려줘요.",
            "example": "신규 고객 할인",
            "type": "string"
          },
          "originalAmount": {
            "description": "할인 전 금액을 원 단위로 알려줘요.",
            "example": 10000,
            "type": "number"
          }
        },
        "required": [
          "code",
          "name",
          "discountAmount",
          "originalAmount",
          "currency"
        ],
        "type": "object"
      },
      "V1OrderPageResponse": {
        "properties": {
          "data": {
            "description": "페이지에 포함된 주문 목록이에요.",
            "items": {
              "$ref": "#/components/schemas/V1OrderResponse"
            },
            "type": "array"
          },
          "limit": {
            "description": "페이지 크기예요.",
            "example": 20,
            "type": "number"
          },
          "page": {
            "description": "현재 페이지 번호예요.",
            "example": 1,
            "type": "number"
          },
          "total": {
            "description": "조건에 맞는 전체 항목 수예요.",
            "example": 1,
            "type": "number"
          }
        },
        "required": [
          "page",
          "limit",
          "total",
          "data"
        ],
        "type": "object"
      },
      "V1OrderRefundDetail": {
        "properties": {
          "amount": {
            "description": "환불 금액을 원 단위로 알려줘요.",
            "example": 5000,
            "type": "number"
          },
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "id": {
            "description": "환불 식별자예요.",
            "example": "ref_123",
            "type": "string"
          },
          "reason": {
            "description": "환불 사유를 알려줘요.",
            "example": "고객 요청",
            "type": [
              "string",
              "null"
            ]
          },
          "refundedAt": {
            "description": "환불이 완료된 시각이에요.",
            "example": "2026-10-15T00:00:00.000Z",
            "type": "string"
          }
        },
        "required": [
          "id",
          "amount",
          "currency",
          "reason",
          "refundedAt"
        ],
        "type": "object"
      },
      "V1OrderRefundItem": {
        "properties": {
          "amount": {
            "description": "환불 금액을 원 단위로 알려줘요.",
            "example": 5000,
            "type": "number"
          },
          "at": {
            "description": "환불이 처리된 시각이에요.",
            "example": "2026-10-15T00:00:00.000Z",
            "type": "string"
          },
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "reason": {
            "description": "환불 사유를 알려줘요.",
            "example": "고객 요청",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "amount",
          "currency",
          "at",
          "reason"
        ],
        "type": "object"
      },
      "V1OrderResponse": {
        "properties": {
          "amount": {
            "description": "결제 금액을 원 단위 정수로 알려줘요.",
            "example": 10000,
            "type": "number"
          },
          "billingInterval": {
            "description": "구독 청구 주기예요.",
            "enum": [
              "monthly",
              "yearly",
              null
            ],
            "example": "monthly",
            "type": [
              "string",
              "null"
            ]
          },
          "buyerEmail": {
            "description": "구매자 이메일 주소예요.",
            "example": "buyer@example.com",
            "type": "string"
          },
          "buyerName": {
            "description": "구매자 이름이에요.",
            "example": "홍길동",
            "type": "string"
          },
          "buyerPhone": {
            "description": "구매자 연락처예요.",
            "example": "01012345678",
            "type": "string"
          },
          "checkoutSessionId": {
            "description": "연결된 결제 세션 식별자예요.",
            "example": "cs_123",
            "type": [
              "string",
              "null"
            ]
          },
          "confirmedAt": {
            "description": "구매 확정 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "coupon": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1OrderCoupon"
                  }
                ],
                "description": "적용한 쿠폰 정보예요."
              },
              {
                "type": "null"
              }
            ],
            "description": "적용한 쿠폰 정보예요."
          },
          "createdAt": {
            "description": "주문이 생성된 시각이에요.",
            "example": "2026-10-15T00:00:00.000Z",
            "type": "string"
          },
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "id": {
            "description": "주문 식별자예요.",
            "example": "ord_123",
            "type": "string"
          },
          "metadata": {
            "description": "요청 때 전달한 메타데이터예요.",
            "example": {
              "campaign": "fall"
            },
            "type": [
              "object",
              "null"
            ]
          },
          "method": {
            "description": "결제 수단이에요.",
            "enum": [
              "card",
              "kakao_pay",
              "naver_pay",
              "toss"
            ],
            "example": "card",
            "type": "string"
          },
          "mode": {
            "description": "test 또는 live 결제 환경을 알려줘요.",
            "enum": [
              "live",
              "test"
            ],
            "example": "test",
            "type": "string"
          },
          "paidAt": {
            "description": "결제가 완료된 시각이에요.",
            "example": "2026-10-15T00:01:00.000Z",
            "type": [
              "string",
              "null"
            ]
          },
          "periodEnd": {
            "description": "해당 결제 기간의 종료 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "periodStart": {
            "description": "해당 결제 기간의 시작 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "planId": {
            "description": "구독 플랜 식별자예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "productId": {
            "description": "상품 식별자예요.",
            "example": "prod_123",
            "type": "string"
          },
          "productName": {
            "description": "구매한 상품 이름이에요.",
            "example": "디지털 가이드",
            "type": "string"
          },
          "refundedAmount": {
            "description": "누적 환불 금액을 원 단위로 알려줘요.",
            "example": 0,
            "type": [
              "number",
              "null"
            ]
          },
          "refundedAt": {
            "description": "환불이 완료된 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "refunds": {
            "description": "주문에 연결된 환불 내역이에요.",
            "items": {
              "$ref": "#/components/schemas/V1OrderRefundItem"
            },
            "type": "array"
          },
          "status": {
            "description": "주문 상태예요.",
            "enum": [
              "pending",
              "paid",
              "failed",
              "refunded"
            ],
            "example": "paid",
            "type": "string"
          },
          "subscriptionEvent": {
            "description": "결제가 발생한 구독 이벤트예요.",
            "enum": [
              "initial",
              "renewal",
              "proration",
              null
            ],
            "example": "initial",
            "type": [
              "string",
              "null"
            ]
          },
          "subscriptionId": {
            "description": "연결된 구독 식별자예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "withdrawalNoticeAgreedAt": {
            "description": "청약 철회 안내 동의 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "mode",
          "productId",
          "productName",
          "amount",
          "currency",
          "buyerName",
          "buyerEmail",
          "buyerPhone",
          "method",
          "status",
          "createdAt",
          "paidAt",
          "refundedAt",
          "refundedAmount",
          "refunds",
          "confirmedAt",
          "withdrawalNoticeAgreedAt",
          "coupon",
          "subscriptionId",
          "planId",
          "billingInterval",
          "subscriptionEvent",
          "periodStart",
          "periodEnd",
          "checkoutSessionId",
          "metadata"
        ],
        "type": "object"
      },
      "V1PlanInterval": {
        "properties": {
          "billingInterval": {
            "description": "변경 전후 청구 주기예요.",
            "enum": [
              "monthly",
              "yearly"
            ],
            "example": "monthly",
            "type": "string"
          },
          "planId": {
            "description": "변경 전후 구독 플랜 식별자예요.",
            "example": "plan_basic",
            "type": "string"
          }
        },
        "required": [
          "planId",
          "billingInterval"
        ],
        "type": "object"
      },
      "V1PortalSessionResponse": {
        "properties": {
          "expiresAt": {
            "description": "토큰 만료 시각이에요.",
            "example": "2026-10-15T01:00:00.000Z",
            "type": "string"
          },
          "mode": {
            "description": "test 또는 live 포털 환경을 알려줘요.",
            "enum": [
              "live",
              "test"
            ],
            "example": "live",
            "type": "string"
          },
          "token": {
            "description": "포털 인증 토큰이에요.",
            "example": "eyJhbGciOi...",
            "type": "string"
          },
          "url": {
            "description": "구매자 포털에 접속할 URL이에요.",
            "example": "https://payri.kr/portal#token=...",
            "type": "string"
          }
        },
        "required": [
          "mode",
          "url",
          "token",
          "expiresAt"
        ],
        "type": "object"
      },
      "V1ProductContentItem": {
        "properties": {
          "label": {
            "description": "콘텐츠 항목의 표시 이름이에요.",
            "example": "강의 자료",
            "type": "string"
          },
          "type": {
            "description": "콘텐츠 항목의 유형이에요.",
            "enum": [
              "file",
              "license",
              "access",
              "service",
              "support",
              "update",
              "guide",
              "other"
            ],
            "example": "file",
            "type": "string"
          }
        },
        "required": [
          "type",
          "label"
        ],
        "type": "object"
      },
      "V1ProductDeliverable": {
        "properties": {
          "fileName": {
            "description": "전달 파일 이름이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "fileSize": {
            "description": "전달 파일 크기를 바이트로 알려줘요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "licenseIssuance": {
            "description": "라이선스 발급 방식이에요.",
            "enum": [
              "payri",
              "seller_server",
              "pool",
              "shared",
              "manual",
              null
            ],
            "example": "payri",
            "type": [
              "string",
              "null"
            ]
          },
          "maxActivations": {
            "description": "라이선스 최대 활성화 수예요. 무제한이면 null이에요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "note": {
            "description": "전달 방식에 대한 안내예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "description": "상품 전달 유형이에요.",
            "enum": [
              "file",
              "license",
              "link"
            ],
            "example": "file",
            "type": "string"
          }
        },
        "required": [
          "type",
          "note",
          "licenseIssuance",
          "maxActivations",
          "fileName",
          "fileSize"
        ],
        "type": "object"
      },
      "V1ProductPageResponse": {
        "properties": {
          "data": {
            "description": "페이지에 포함된 상품 목록이에요.",
            "items": {
              "$ref": "#/components/schemas/V1ProductResponse"
            },
            "type": "array"
          },
          "limit": {
            "description": "페이지 크기예요.",
            "example": 20,
            "type": "number"
          },
          "page": {
            "description": "현재 페이지 번호예요.",
            "example": 1,
            "type": "number"
          },
          "total": {
            "description": "조건에 맞는 전체 항목 수예요.",
            "example": 1,
            "type": "number"
          }
        },
        "required": [
          "page",
          "limit",
          "total",
          "data"
        ],
        "type": "object"
      },
      "V1ProductPenalty": {
        "properties": {
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "fixedAmount": {
            "description": "고정 위약금을 원 단위로 알려줘요.",
            "example": 0,
            "type": [
              "number",
              "null"
            ]
          },
          "rateBps": {
            "description": "위약금 비율을 1만분율로 알려줘요.",
            "example": 0,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "rateBps",
          "fixedAmount",
          "currency"
        ],
        "type": "object"
      },
      "V1ProductPlan": {
        "properties": {
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "features": {
            "description": "플랜에 포함된 기능 목록이에요.",
            "example": [
              "다운로드 가능"
            ],
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "id": {
            "description": "플랜 식별자예요.",
            "example": "plan_basic",
            "type": "string"
          },
          "monthlyAmount": {
            "description": "월간 결제 금액을 원 단위로 알려줘요.",
            "example": 9900,
            "type": "number"
          },
          "name": {
            "description": "플랜 이름이에요.",
            "example": "Basic",
            "type": "string"
          },
          "tagline": {
            "description": "플랜 요약 문구예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "tier": {
            "description": "플랜 표시 순서예요.",
            "example": 1,
            "type": "number"
          },
          "yearlyAmount": {
            "description": "연간 결제 금액을 원 단위로 알려줘요.",
            "example": 99000,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "name",
          "tagline",
          "features",
          "monthlyAmount",
          "yearlyAmount",
          "currency",
          "tier"
        ],
        "type": "object"
      },
      "V1ProductPricing": {
        "properties": {
          "amount": {
            "description": "단건 상품의 결제 금액이에요.",
            "example": 5000,
            "type": [
              "number",
              "null"
            ]
          },
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "interval": {
            "description": "단건 상품의 결제 주기예요.",
            "enum": [
              "monthly",
              "yearly",
              null
            ],
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "plans": {
            "description": "구독 상품의 플랜 목록이에요.",
            "items": {
              "$ref": "#/components/schemas/V1ProductPlan"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "type": {
            "description": "가격 유형이에요.",
            "enum": [
              "one_time",
              "subscription"
            ],
            "example": "subscription",
            "type": "string"
          }
        },
        "required": [
          "type",
          "currency",
          "amount",
          "interval",
          "plans"
        ],
        "type": "object"
      },
      "V1ProductRefundPolicy": {
        "properties": {
          "customText": {
            "description": "사용자에게 표시할 환불 안내예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "refundDays": {
            "description": "custom 정책의 결제 후 환불 요청 가능 일수예요. 이후 자동 구매확정돼요.",
            "example": null,
            "maximum": 90,
            "minimum": 7,
            "type": [
              "integer",
              "null"
            ]
          },
          "requireApproval": {
            "description": "환불 요청에 판매자 승인이 필요한지 알려줘요.",
            "example": false,
            "type": [
              "boolean",
              "null"
            ]
          },
          "subscription": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1ProductSubscriptionRefundTerms"
                  }
                ],
                "description": "구독 상품의 환불 조건이에요."
              },
              {
                "type": "null"
              }
            ],
            "description": "구독 상품의 환불 조건이에요."
          },
          "type": {
            "description": "환불 정책 유형이에요. before_delivery는 제공 개시 전·결제 후 7일 이내에만 환불 요청할 수 있고, 제공 개시 시 즉시 또는 미제공 상태로 7일이 지나면 자동 구매확정돼요.",
            "enum": [
              "no_refund",
              "before_delivery",
              "within_7days",
              "custom",
              null
            ],
            "example": "within_7days",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "type",
          "customText",
          "requireApproval",
          "refundDays",
          "subscription"
        ],
        "type": "object"
      },
      "V1ProductResponse": {
        "properties": {
          "contents": {
            "description": "상품에 포함된 콘텐츠예요.",
            "items": {
              "$ref": "#/components/schemas/V1ProductContentItem"
            },
            "type": "array"
          },
          "createdAt": {
            "description": "상품이 생성된 시각이에요.",
            "example": "2026-10-15T00:00:00.000Z",
            "type": "string"
          },
          "deliverable": {
            "allOf": [
              {
                "$ref": "#/components/schemas/V1ProductDeliverable"
              }
            ],
            "description": "상품 전달 정보예요."
          },
          "description": {
            "description": "상품 상세 설명이에요.",
            "example": "상품 상세 설명이에요.",
            "type": "string"
          },
          "displayOrder": {
            "description": "스토어에서 상품 표시 순서예요.",
            "example": 0,
            "type": [
              "number",
              "null"
            ]
          },
          "icon": {
            "description": "상품 아이콘 주소예요.",
            "example": "",
            "type": "string"
          },
          "iconColor": {
            "description": "아이콘 배경 색상 코드예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "상품 식별자예요.",
            "example": "prod_123",
            "type": "string"
          },
          "imageUrl": {
            "description": "상품 이미지 주소예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "linkId": {
            "description": "결제 페이지 링크 식별자예요.",
            "example": "link_123",
            "type": "string"
          },
          "listed": {
            "description": "상품이 공개 판매 중인지 알려줘요.",
            "example": true,
            "type": "boolean"
          },
          "mode": {
            "description": "test 또는 live 상품 환경을 알려줘요.",
            "enum": [
              "live",
              "test"
            ],
            "example": "live",
            "type": "string"
          },
          "name": {
            "description": "상품 이름이에요.",
            "example": "디지털 가이드",
            "type": "string"
          },
          "pricing": {
            "allOf": [
              {
                "$ref": "#/components/schemas/V1ProductPricing"
              }
            ],
            "description": "상품 가격과 구독 플랜 정보예요."
          },
          "refundPolicy": {
            "allOf": [
              {
                "$ref": "#/components/schemas/V1ProductRefundPolicy"
              }
            ],
            "description": "상품 환불 정책이에요."
          },
          "saleEndsAt": {
            "description": "판매 종료 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "saleStartsAt": {
            "description": "판매 시작 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "description": "상품 상태예요.",
            "enum": [
              "draft",
              "active",
              "archived"
            ],
            "example": "active",
            "type": "string"
          },
          "stockLimit": {
            "description": "재고 상한이에요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "summary": {
            "description": "상품 한 줄 소개예요.",
            "example": "바로 내려받는 가이드예요.",
            "type": "string"
          }
        },
        "required": [
          "id",
          "mode",
          "name",
          "summary",
          "description",
          "icon",
          "pricing",
          "linkId",
          "deliverable",
          "status",
          "listed",
          "contents",
          "refundPolicy",
          "createdAt",
          "imageUrl",
          "iconColor",
          "stockLimit",
          "saleStartsAt",
          "saleEndsAt",
          "displayOrder"
        ],
        "type": "object"
      },
      "V1ProductSubscriptionRefundTerms": {
        "properties": {
          "dayRounding": {
            "description": "환불 일수 계산 방식이에요.",
            "enum": [
              "ceil",
              "floor",
              null
            ],
            "example": "ceil",
            "type": [
              "string",
              "null"
            ]
          },
          "initialFullRefundDays": {
            "description": "최초 결제의 전액 환불 가능 일수예요.",
            "example": 7,
            "type": [
              "number",
              "null"
            ]
          },
          "penalty": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1ProductPenalty"
                  }
                ],
                "description": "환불 위약금 조건이에요."
              },
              {
                "type": "null"
              }
            ],
            "description": "환불 위약금 조건이에요."
          },
          "renewalFullRefundDays": {
            "description": "갱신 결제의 전액 환불 가능 일수예요.",
            "example": 7,
            "type": [
              "number",
              "null"
            ]
          },
          "restrictRepeatFullRefund": {
            "description": "반복 전액 환불 제한 여부예요.",
            "example": false,
            "type": [
              "boolean",
              "null"
            ]
          },
          "yearlyDiscountClawback": {
            "description": "연간 할인 환수 여부예요.",
            "example": false,
            "type": [
              "boolean",
              "null"
            ]
          }
        },
        "required": [
          "dayRounding",
          "initialFullRefundDays",
          "renewalFullRefundDays",
          "penalty",
          "yearlyDiscountClawback",
          "restrictRepeatFullRefund"
        ],
        "type": "object"
      },
      "V1RefundActionResponse": {
        "properties": {
          "refundRequest": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1RefundRequestResponse"
                  }
                ],
                "description": "환불 요청 처리 후의 정보예요."
              },
              {
                "type": "null"
              }
            ],
            "description": "환불 요청 처리 후의 정보예요."
          },
          "warnings": {
            "description": "처리 결과와 함께 전달하는 안내 목록이에요.",
            "items": {
              "$ref": "#/components/schemas/V1Warning"
            },
            "type": "array"
          }
        },
        "required": [
          "refundRequest",
          "warnings"
        ],
        "type": "object"
      },
      "V1RefundBreakdown": {
        "properties": {
          "orders": {
            "description": "환불 대상 주문별 금액이에요.",
            "items": {
              "$ref": "#/components/schemas/V1RefundBreakdownOrder"
            },
            "type": "array"
          }
        },
        "required": [
          "orders"
        ],
        "type": "object"
      },
      "V1RefundBreakdownOrder": {
        "properties": {
          "amount": {
            "description": "대상 주문의 환불 금액이에요.",
            "example": 5000,
            "type": "number"
          },
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "orderId": {
            "description": "환불 대상 주문 식별자예요.",
            "example": "ord_123",
            "type": "string"
          }
        },
        "required": [
          "orderId",
          "amount",
          "currency"
        ],
        "type": "object"
      },
      "V1RefundHold": {
        "properties": {
          "heldAt": {
            "description": "보류 처리 시각이에요.",
            "example": "2026-10-15T00:00:00.000Z",
            "type": "string"
          },
          "reason": {
            "description": "보류 사유예요.",
            "example": "추가 확인 중이에요.",
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "description": "보류 유형이에요.",
            "enum": [
              "in_review",
              "disputed"
            ],
            "example": "in_review",
            "type": "string"
          }
        },
        "required": [
          "type",
          "reason",
          "heldAt"
        ],
        "type": "object"
      },
      "V1RefundOrderDto": {
        "properties": {
          "amount": {
            "description": "부분 환불 금액을 원 단위로 지정해요.",
            "example": 5000,
            "minimum": 1,
            "type": "number"
          },
          "reason": {
            "description": "환불 사유예요.",
            "example": "고객 요청",
            "type": "string"
          }
        },
        "type": "object"
      },
      "V1RefundRequestApproveDto": {
        "properties": {
          "acknowledgeMismatch": {
            "description": "환불 계산 안내를 확인했는지 표시해요.",
            "example": true,
            "type": "boolean"
          },
          "amount": {
            "description": "승인할 환불 금액을 원 단위로 지정해요.",
            "example": 5000,
            "minimum": 1,
            "type": "number"
          }
        },
        "type": "object"
      },
      "V1RefundRequestHoldDto": {
        "properties": {
          "reason": {
            "description": "보류 사유예요.",
            "example": "추가 확인이 필요해요.",
            "minLength": 1,
            "type": "string"
          },
          "type": {
            "description": "보류 유형이에요.",
            "enum": [
              "in_review",
              "disputed"
            ],
            "example": "in_review",
            "type": "string"
          }
        },
        "required": [
          "type",
          "reason"
        ],
        "type": "object"
      },
      "V1RefundRequestPageResponse": {
        "properties": {
          "data": {
            "description": "페이지에 포함된 환불 요청 목록이에요.",
            "items": {
              "$ref": "#/components/schemas/V1RefundRequestResponse"
            },
            "type": "array"
          },
          "limit": {
            "description": "페이지 크기예요.",
            "example": 20,
            "type": "number"
          },
          "page": {
            "description": "현재 페이지 번호예요.",
            "example": 1,
            "type": "number"
          },
          "total": {
            "description": "조건에 맞는 전체 항목 수예요.",
            "example": 1,
            "type": "number"
          }
        },
        "required": [
          "page",
          "limit",
          "total",
          "data"
        ],
        "type": "object"
      },
      "V1RefundRequestRejectDto": {
        "properties": {
          "reason": {
            "description": "환불 요청을 거절하는 이유예요.",
            "example": "환불 정책상 처리할 수 없어요.",
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "reason"
        ],
        "type": "object"
      },
      "V1RefundRequestResponse": {
        "properties": {
          "approvedAmount": {
            "description": "승인한 환불 금액을 원 단위로 알려줘요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "autoApproveAt": {
            "description": "요청 후 72시간 안에 결정되지 않으면 자동 승인되는 시각이에요.",
            "example": "2026-10-18T00:00:00.000Z",
            "type": [
              "string",
              "null"
            ]
          },
          "breakdown": {
            "allOf": [
              {
                "$ref": "#/components/schemas/V1RefundBreakdown"
              }
            ],
            "description": "주문별 환불 금액 내역이에요."
          },
          "buyerEmail": {
            "description": "구매자 이메일 주소예요.",
            "example": "buyer@example.com",
            "type": "string"
          },
          "buyerReason": {
            "description": "구매자가 남긴 환불 사유예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "description": "환불 요청이 생성된 시각이에요.",
            "example": "2026-10-15T00:00:00.000Z",
            "type": "string"
          },
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "decidedAt": {
            "description": "요청이 결정된 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "decidedBy": {
            "description": "요청을 결정한 사용자 식별자예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "hold": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1RefundHold"
                  }
                ],
                "description": "보류 처리 정보예요."
              },
              {
                "type": "null"
              }
            ],
            "description": "보류 처리 정보예요."
          },
          "id": {
            "description": "환불 요청 식별자예요.",
            "example": "rfr_123",
            "type": "string"
          },
          "kind": {
            "description": "환불 요청 유형이에요.",
            "enum": [
              "order",
              "subscription_cancel"
            ],
            "example": "order",
            "type": "string"
          },
          "mode": {
            "description": "test 또는 live 환불 환경을 알려줘요.",
            "enum": [
              "live",
              "test"
            ],
            "example": "test",
            "type": "string"
          },
          "orderId": {
            "description": "환불 요청의 주문 식별자예요.",
            "example": "ord_123",
            "type": "string"
          },
          "productId": {
            "description": "환불 요청의 상품 식별자예요.",
            "example": "prod_123",
            "type": "string"
          },
          "refundableAmount": {
            "description": "현재 환불 가능한 금액을 원 단위로 알려줘요.",
            "example": 10000,
            "type": "number"
          },
          "rejectReason": {
            "description": "거절 사유예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "requestedAmount": {
            "description": "요청한 환불 금액을 원 단위로 알려줘요.",
            "example": 10000,
            "type": "number"
          },
          "status": {
            "description": "환불 요청 상태예요.",
            "enum": [
              "requested",
              "on_hold",
              "processing",
              "approved",
              "rejected",
              "withdrawn"
            ],
            "example": "requested",
            "type": "string"
          },
          "statutory": {
            "description": "법정 청약 철회 요청인지 알려줘요.",
            "example": false,
            "type": "boolean"
          },
          "subscriptionId": {
            "description": "환불 요청의 구독 식별자예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "updatedAt": {
            "description": "환불 요청이 수정된 시각이에요.",
            "example": "2026-10-15T00:00:00.000Z",
            "type": "string"
          },
          "warnings": {
            "description": "환불 처리 전에 확인할 안내 목록이에요.",
            "items": {
              "$ref": "#/components/schemas/V1Warning"
            },
            "type": "array"
          }
        },
        "required": [
          "id",
          "kind",
          "status",
          "orderId",
          "subscriptionId",
          "productId",
          "requestedAmount",
          "approvedAmount",
          "currency",
          "breakdown",
          "buyerEmail",
          "buyerReason",
          "rejectReason",
          "hold",
          "autoApproveAt",
          "decidedAt",
          "decidedBy",
          "statutory",
          "mode",
          "createdAt",
          "updatedAt"
        ],
        "type": "object"
      },
      "V1SubscriptionChange": {
        "properties": {
          "amount": {
            "description": "변경 시 적용된 금액을 원 단위로 알려줘요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "currency": {
            "description": "변경 금액 통화예요.",
            "enum": [
              "KRW",
              null
            ],
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "effectiveAt": {
            "description": "변경 효력이 발생하는 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "extensionDays": {
            "description": "구독 기간 연장 일수예요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "from": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1PlanInterval"
                  }
                ],
                "description": "변경 전 플랜과 주기예요."
              },
              {
                "type": "null"
              }
            ],
            "description": "변경 전 플랜과 주기예요."
          },
          "kind": {
            "description": "구독에 발생한 변경 유형이에요.",
            "enum": [
              "created",
              "plan_changed",
              "plan_change_scheduled",
              "billing_interval_changed",
              "billing_interval_change_scheduled",
              "cancel_scheduled",
              "cancel_resumed",
              "period_renewed",
              "period_extended",
              "price_change_scheduled",
              "price_change_consented"
            ],
            "example": "plan_changed",
            "type": "string"
          },
          "orderId": {
            "description": "변경과 연결된 주문 식별자예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "prorationAmount": {
            "description": "차액 결제 금액을 원 단위로 알려줘요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "to": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1PlanInterval"
                  }
                ],
                "description": "변경 후 플랜과 주기예요."
              },
              {
                "type": "null"
              }
            ],
            "description": "변경 후 플랜과 주기예요."
          }
        },
        "required": [
          "kind",
          "from",
          "to",
          "prorationAmount",
          "orderId",
          "effectiveAt",
          "extensionDays",
          "amount",
          "currency"
        ],
        "type": "object"
      },
      "V1SubscriptionHistoryEntry": {
        "properties": {
          "at": {
            "description": "변경이 기록된 시각이에요.",
            "example": "2026-10-15T00:00:00.000Z",
            "type": "string"
          },
          "effectiveAt": {
            "description": "변경 효력이 발생하는 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "extensionDays": {
            "description": "구독 기간 연장 일수예요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "from": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1PlanInterval"
                  }
                ],
                "description": "변경 전 플랜과 주기예요."
              },
              {
                "type": "null"
              }
            ],
            "description": "변경 전 플랜과 주기예요."
          },
          "note": {
            "description": "이전 변경 기록의 메모예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "orderId": {
            "description": "변경과 연결된 주문 식별자예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "prorationAmount": {
            "description": "차액 결제 금액, 또는 갱신·가격 변경 이력의 금액을 원 단위로 알려줘요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "to": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1PlanInterval"
                  }
                ],
                "description": "변경 후 플랜과 주기예요."
              },
              {
                "type": "null"
              }
            ],
            "description": "변경 후 플랜과 주기예요."
          },
          "type": {
            "description": "내부 변경 기록 유형이에요.",
            "enum": [
              "created",
              "renewed",
              "upgraded",
              "downgrade_scheduled",
              "downgrade_applied",
              "interval_changed",
              "cancel_scheduled",
              "cancel_undone",
              "ended",
              "refunded",
              "canceled_with_refund",
              "comp_upgraded",
              "period_extended",
              "payment_failed",
              "price_change_noticed",
              "price_change_consented",
              null
            ],
            "example": "renewed",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "at",
          "type",
          "from",
          "to",
          "prorationAmount",
          "orderId",
          "effectiveAt",
          "extensionDays",
          "note"
        ],
        "type": "object"
      },
      "V1SubscriptionListAmounts": {
        "properties": {
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "monthly": {
            "description": "월간 기준 금액이에요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "yearly": {
            "description": "연간 기준 금액이에요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "monthly",
          "yearly",
          "currency"
        ],
        "type": "object"
      },
      "V1SubscriptionPageResponse": {
        "properties": {
          "data": {
            "description": "페이지에 포함된 구독 목록이에요.",
            "items": {
              "$ref": "#/components/schemas/V1SubscriptionResponse"
            },
            "type": "array"
          },
          "limit": {
            "description": "페이지 크기예요.",
            "example": 20,
            "type": "number"
          },
          "page": {
            "description": "현재 페이지 번호예요.",
            "example": 1,
            "type": "number"
          },
          "total": {
            "description": "조건에 맞는 전체 항목 수예요.",
            "example": 1,
            "type": "number"
          }
        },
        "required": [
          "page",
          "limit",
          "total",
          "data"
        ],
        "type": "object"
      },
      "V1SubscriptionPenalty": {
        "properties": {
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "fixedAmount": {
            "description": "고정 위약금을 원 단위로 알려줘요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "rateBps": {
            "description": "위약금 비율을 1만분율로 알려줘요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "rateBps",
          "fixedAmount",
          "currency"
        ],
        "type": "object"
      },
      "V1SubscriptionPendingPriceChange": {
        "properties": {
          "amount": {
            "description": "고지된 새 구독 금액을 원 단위로 알려줘요.",
            "example": 12000,
            "type": "number"
          },
          "consentedAt": {
            "description": "구매자가 새 가격에 동의한 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "currency": {
            "description": "고지된 금액의 통화예요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "effectiveFrom": {
            "description": "새 가격이 적용될 갱신일이에요.",
            "example": "2026-11-01T00:00:00.000Z",
            "type": "string"
          },
          "noticedAt": {
            "description": "가격 인상을 고지한 시각이에요.",
            "example": "2026-10-01T00:00:00.000Z",
            "type": "string"
          }
        },
        "required": [
          "amount",
          "currency",
          "noticedAt",
          "effectiveFrom",
          "consentedAt"
        ],
        "type": "object"
      },
      "V1SubscriptionRefundTerms": {
        "properties": {
          "dayRounding": {
            "description": "일수 계산 방식이에요.",
            "enum": [
              "ceil",
              "floor",
              null
            ],
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "initialFullRefundDays": {
            "description": "최초 결제 전액 환불 가능 일수예요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "listAmounts": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1SubscriptionListAmounts"
                  }
                ],
                "description": "환불 계산에 사용하는 기준 금액이에요."
              },
              {
                "type": "null"
              }
            ],
            "description": "환불 계산에 사용하는 기준 금액이에요."
          },
          "penalty": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1SubscriptionPenalty"
                  }
                ],
                "description": "환불 위약금 조건이에요."
              },
              {
                "type": "null"
              }
            ],
            "description": "환불 위약금 조건이에요."
          },
          "policyType": {
            "description": "구독 환불 정책 유형이에요.",
            "enum": [
              "no_refund",
              "before_delivery",
              "within_7days",
              "custom",
              null
            ],
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "renewalFullRefundDays": {
            "description": "갱신 결제 전액 환불 가능 일수예요.",
            "example": null,
            "type": [
              "number",
              "null"
            ]
          },
          "requireApproval": {
            "description": "환불 요청에 승인이 필요한지 알려줘요.",
            "example": false,
            "type": [
              "boolean",
              "null"
            ]
          },
          "restrictRepeatFullRefund": {
            "description": "반복 전액 환불 제한 여부예요.",
            "example": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "yearlyDiscountClawback": {
            "description": "연간 할인 환수 여부예요.",
            "example": null,
            "type": [
              "boolean",
              "null"
            ]
          }
        },
        "required": [
          "policyType",
          "requireApproval",
          "dayRounding",
          "initialFullRefundDays",
          "renewalFullRefundDays",
          "penalty",
          "yearlyDiscountClawback",
          "restrictRepeatFullRefund",
          "listAmounts"
        ],
        "type": "object"
      },
      "V1SubscriptionResponse": {
        "properties": {
          "amount": {
            "description": "갱신할 때 청구하는 주기당 금액의 상한을 원 단위 정수로 알려줘요. 상품 가격이 내려가면 다음 갱신부터 낮아지고, 인상은 고지 후 구매자가 동의해야 적용돼요.",
            "example": 9900,
            "type": "number"
          },
          "billingInterval": {
            "description": "구독 결제 주기예요.",
            "enum": [
              "monthly",
              "yearly"
            ],
            "example": "monthly",
            "type": "string"
          },
          "buyerEmail": {
            "description": "구매자 이메일 주소예요.",
            "example": "buyer@example.com",
            "type": "string"
          },
          "buyerName": {
            "description": "구매자 이름이에요.",
            "example": "홍길동",
            "type": "string"
          },
          "cancelAtPeriodEnd": {
            "description": "다음 기간 종료 시 해지하도록 예약했는지 알려줘요.",
            "example": false,
            "type": "boolean"
          },
          "currency": {
            "description": "금액 통화를 알려줘요.",
            "enum": [
              "KRW"
            ],
            "example": "KRW",
            "type": "string"
          },
          "currentPeriodEnd": {
            "description": "현재 결제 기간 종료 시각이에요.",
            "example": "2026-11-01T00:00:00.000Z",
            "type": "string"
          },
          "currentPeriodStart": {
            "description": "현재 결제 기간 시작 시각이에요.",
            "example": "2026-10-01T00:00:00.000Z",
            "type": "string"
          },
          "endReason": {
            "description": "구독 종료 사유예요.",
            "enum": [
              "canceled",
              "refunded",
              "payment_failed",
              "product_unavailable",
              "plan_unavailable",
              "pg_canceled",
              "price_increase_declined",
              null
            ],
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "endedAt": {
            "description": "구독이 종료된 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "history": {
            "description": "구독 변경 이력이에요.",
            "items": {
              "$ref": "#/components/schemas/V1SubscriptionHistoryEntry"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "id": {
            "description": "구독 식별자예요.",
            "example": "sub_123",
            "type": "string"
          },
          "mode": {
            "description": "test 또는 live 구독 환경을 알려줘요.",
            "enum": [
              "live",
              "test"
            ],
            "example": "test",
            "type": "string"
          },
          "pastDueSince": {
            "description": "결제 실패 상태가 시작된 시각이에요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "pendingPriceChange": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1SubscriptionPendingPriceChange"
                  }
                ],
                "description": "가격 인상 고지와 구매자 동의 상태예요."
              },
              {
                "type": "null"
              }
            ],
            "description": "가격 인상 고지와 구매자 동의 상태예요."
          },
          "planId": {
            "description": "구독 플랜 식별자예요.",
            "example": "plan_basic",
            "type": "string"
          },
          "productId": {
            "description": "상품 식별자예요.",
            "example": "prod_123",
            "type": "string"
          },
          "productName": {
            "description": "상품 이름이에요.",
            "example": "멤버십",
            "type": "string"
          },
          "refundEligibleUntil": {
            "description": "최초 결제 환불 가능 기한이에요.",
            "example": "2026-10-08T00:00:00.000Z",
            "type": "string"
          },
          "refundTerms": {
            "allOf": [
              {
                "$ref": "#/components/schemas/V1SubscriptionRefundTerms"
              }
            ],
            "description": "구독 환불 조건의 스냅샷이에요."
          },
          "scheduledChange": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1SubscriptionScheduledChange"
                  }
                ],
                "description": "예약된 구독 변경 정보예요."
              },
              {
                "type": "null"
              }
            ],
            "description": "예약된 구독 변경 정보예요."
          },
          "startedAt": {
            "description": "구독이 시작된 시각이에요.",
            "example": "2026-10-01T00:00:00.000Z",
            "type": "string"
          },
          "status": {
            "description": "구독의 공개 상태예요. past_due 는 결제가 밀린 상태예요. 결제가 밀린 채 해지를 예약해도 past_due 이고 cancelAtPeriodEnd 가 true 예요.",
            "enum": [
              "active",
              "past_due",
              "ended"
            ],
            "example": "active",
            "type": "string"
          },
          "testRenewalOutcome": {
            "description": "test 갱신 결과 설정이에요.",
            "enum": [
              "succeed",
              "fail",
              null
            ],
            "example": "succeed",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "mode",
          "testRenewalOutcome",
          "productId",
          "productName",
          "buyerEmail",
          "buyerName",
          "planId",
          "billingInterval",
          "amount",
          "currency",
          "status",
          "cancelAtPeriodEnd",
          "endReason",
          "currentPeriodStart",
          "currentPeriodEnd",
          "startedAt",
          "refundEligibleUntil",
          "refundTerms",
          "history",
          "scheduledChange",
          "pendingPriceChange",
          "endedAt",
          "pastDueSince"
        ],
        "type": "object"
      },
      "V1SubscriptionScheduledChange": {
        "properties": {
          "billingInterval": {
            "description": "예약된 구독 청구 주기예요.",
            "enum": [
              "monthly",
              "yearly",
              null
            ],
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "effectiveAt": {
            "description": "예약 변경 효력이 발생하는 시각이에요.",
            "example": "2026-11-01T00:00:00.000Z",
            "type": "string"
          },
          "planId": {
            "description": "예약된 구독 플랜 식별자예요.",
            "example": null,
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "description": "예약 변경 유형이에요.",
            "enum": [
              "downgrade",
              "interval_change",
              "cancel"
            ],
            "example": "cancel",
            "type": "string"
          }
        },
        "required": [
          "effectiveAt",
          "type",
          "planId",
          "billingInterval"
        ],
        "type": "object"
      },
      "V1Warning": {
        "properties": {
          "code": {
            "description": "환불 안내를 구분하는 코드예요.",
            "enum": [
              "STATUTORY_LOCKED",
              "SUBSCRIPTION_CANCEL_REFUND_RIGHT",
              "POLICY_DECISION"
            ],
            "example": "POLICY_DECISION",
            "type": "string"
          },
          "message": {
            "description": "환불 처리 시 참고할 안내예요.",
            "example": "환불 가능 기간이 지났어요.",
            "type": "string"
          }
        },
        "required": [
          "code",
          "message"
        ],
        "type": "object"
      },
      "V1WebhookData_license_issue": {
        "allOf": [
          {
            "$ref": "#/components/schemas/V1LicenseIssueRequestData"
          },
          {
            "properties": {
              "mode": {
                "description": "이벤트의 결제 환경이에요.",
                "enum": [
                  "live",
                  "test"
                ],
                "type": "string"
              }
            },
            "required": [
              "mode"
            ],
            "type": "object"
          }
        ],
        "title": "license.issue webhook data"
      },
      "V1WebhookData_order_paid": {
        "allOf": [
          {
            "$ref": "#/components/schemas/V1OrderResponse"
          },
          {
            "properties": {
              "mode": {
                "description": "이벤트의 결제 환경이에요.",
                "enum": [
                  "live",
                  "test"
                ],
                "type": "string"
              }
            },
            "required": [
              "mode",
              "subscriptionEvent"
            ],
            "type": "object"
          }
        ],
        "title": "order.paid webhook data"
      },
      "V1WebhookData_order_refunded": {
        "allOf": [
          {
            "$ref": "#/components/schemas/V1OrderResponse"
          },
          {
            "properties": {
              "mode": {
                "description": "이벤트의 결제 환경이에요.",
                "enum": [
                  "live",
                  "test"
                ],
                "type": "string"
              },
              "refund": {
                "$ref": "#/components/schemas/V1OrderRefundDetail",
                "description": "주문 환불의 상세 정보예요."
              }
            },
            "required": [
              "mode",
              "refund"
            ],
            "type": "object"
          }
        ],
        "title": "order.refunded webhook data"
      },
      "V1WebhookData_refund_approved": {
        "allOf": [
          {
            "$ref": "#/components/schemas/V1RefundRequestResponse"
          },
          {
            "properties": {
              "mode": {
                "description": "이벤트의 결제 환경이에요.",
                "enum": [
                  "live",
                  "test"
                ],
                "type": "string"
              }
            },
            "required": [
              "mode"
            ],
            "type": "object"
          }
        ],
        "title": "refund.approved webhook data"
      },
      "V1WebhookData_refund_on_hold": {
        "allOf": [
          {
            "$ref": "#/components/schemas/V1RefundRequestResponse"
          },
          {
            "properties": {
              "mode": {
                "description": "이벤트의 결제 환경이에요.",
                "enum": [
                  "live",
                  "test"
                ],
                "type": "string"
              }
            },
            "required": [
              "mode"
            ],
            "type": "object"
          }
        ],
        "title": "refund.on_hold webhook data"
      },
      "V1WebhookData_refund_rejected": {
        "allOf": [
          {
            "$ref": "#/components/schemas/V1RefundRequestResponse"
          },
          {
            "properties": {
              "mode": {
                "description": "이벤트의 결제 환경이에요.",
                "enum": [
                  "live",
                  "test"
                ],
                "type": "string"
              }
            },
            "required": [
              "mode"
            ],
            "type": "object"
          }
        ],
        "title": "refund.rejected webhook data"
      },
      "V1WebhookData_refund_requested": {
        "allOf": [
          {
            "$ref": "#/components/schemas/V1RefundRequestResponse"
          },
          {
            "properties": {
              "mode": {
                "description": "이벤트의 결제 환경이에요.",
                "enum": [
                  "live",
                  "test"
                ],
                "type": "string"
              }
            },
            "required": [
              "mode"
            ],
            "type": "object"
          }
        ],
        "title": "refund.requested webhook data"
      },
      "V1WebhookData_refund_withdrawn": {
        "allOf": [
          {
            "$ref": "#/components/schemas/V1RefundRequestResponse"
          },
          {
            "properties": {
              "mode": {
                "description": "이벤트의 결제 환경이에요.",
                "enum": [
                  "live",
                  "test"
                ],
                "type": "string"
              }
            },
            "required": [
              "mode"
            ],
            "type": "object"
          }
        ],
        "title": "refund.withdrawn webhook data"
      },
      "V1WebhookData_subscription_created": {
        "allOf": [
          {
            "$ref": "#/components/schemas/V1SubscriptionResponse"
          },
          {
            "properties": {
              "change": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1SubscriptionChange"
                  },
                  {
                    "properties": {
                      "kind": {
                        "description": "이 이벤트에서 발생한 구독 변경 유형이에요.",
                        "enum": [
                          "created"
                        ],
                        "type": "string"
                      }
                    },
                    "required": [
                      "kind"
                    ],
                    "type": "object"
                  }
                ],
                "description": "새 구독이 만들어진 변경 정보예요."
              },
              "mode": {
                "description": "이벤트의 결제 환경이에요.",
                "enum": [
                  "live",
                  "test"
                ],
                "type": "string"
              }
            },
            "required": [
              "mode",
              "change"
            ],
            "type": "object"
          }
        ],
        "title": "subscription.created webhook data"
      },
      "V1WebhookData_subscription_ended": {
        "allOf": [
          {
            "$ref": "#/components/schemas/V1SubscriptionResponse"
          },
          {
            "properties": {
              "mode": {
                "description": "이벤트의 결제 환경이에요.",
                "enum": [
                  "live",
                  "test"
                ],
                "type": "string"
              },
              "refundAmount": {
                "description": "즉시 해지로 환불한 금액이에요. 환불을 수반한 즉시 해지에서 전달해요.",
                "example": 10000,
                "type": "integer"
              }
            },
            "required": [
              "mode"
            ],
            "type": "object"
          }
        ],
        "title": "subscription.ended webhook data"
      },
      "V1WebhookData_subscription_payment_failed": {
        "allOf": [
          {
            "$ref": "#/components/schemas/V1SubscriptionResponse"
          },
          {
            "properties": {
              "amount": {
                "description": "실패한 결제 금액이에요.",
                "example": 9900,
                "type": "integer"
              },
              "attempts": {
                "description": "누적 결제 실패 횟수예요.",
                "example": 2,
                "type": "integer"
              },
              "currency": {
                "description": "결제 금액 통화예요.",
                "enum": [
                  "KRW"
                ],
                "type": "string"
              },
              "mode": {
                "description": "이벤트의 결제 환경이에요.",
                "enum": [
                  "live",
                  "test"
                ],
                "type": "string"
              }
            },
            "required": [
              "mode",
              "attempts",
              "amount",
              "currency"
            ],
            "type": "object"
          }
        ],
        "title": "subscription.payment_failed webhook data"
      },
      "V1WebhookData_subscription_updated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/V1SubscriptionResponse"
          },
          {
            "properties": {
              "change": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/V1SubscriptionChange"
                  },
                  {
                    "properties": {
                      "kind": {
                        "description": "이 이벤트에서 발생한 구독 변경 유형이에요.",
                        "enum": [
                          "plan_changed",
                          "plan_change_scheduled",
                          "billing_interval_changed",
                          "billing_interval_change_scheduled",
                          "cancel_scheduled",
                          "cancel_resumed",
                          "period_renewed",
                          "period_extended",
                          "price_change_scheduled",
                          "price_change_consented"
                        ],
                        "type": "string"
                      }
                    },
                    "required": [
                      "kind"
                    ],
                    "type": "object"
                  }
                ],
                "description": "구독의 변경 전후 플랜 정보예요."
              },
              "mode": {
                "description": "이벤트의 결제 환경이에요.",
                "enum": [
                  "live",
                  "test"
                ],
                "type": "string"
              }
            },
            "required": [
              "mode",
              "change"
            ],
            "type": "object"
          }
        ],
        "title": "subscription.updated webhook data"
      },
      "V1WebhookData_webhook_test": {
        "allOf": [
          {
            "$ref": "#/components/schemas/V1WebhookTestData"
          },
          {
            "properties": {
              "mode": {
                "description": "이벤트의 결제 환경이에요.",
                "enum": [
                  "live",
                  "test"
                ],
                "type": "string"
              }
            },
            "required": [
              "mode"
            ],
            "type": "object"
          }
        ],
        "title": "webhook.test webhook data"
      },
      "V1WebhookEnvelope_license_issue": {
        "additionalProperties": false,
        "properties": {
          "createdAt": {
            "description": "이벤트가 생성된 시각이에요.",
            "format": "date-time",
            "type": "string"
          },
          "data": {
            "$ref": "#/components/schemas/V1WebhookData_license_issue",
            "description": "라이선스 발급을 요청하는 이벤트 데이터예요."
          },
          "id": {
            "description": "이벤트를 유일하게 구분해요.",
            "example": "evt_123",
            "type": "string"
          },
          "mode": {
            "description": "이벤트의 결제 환경이에요.",
            "enum": [
              "live",
              "test"
            ],
            "type": "string"
          },
          "type": {
            "const": "license.issue",
            "description": "구독한 웹훅 이벤트 이름이에요.",
            "enum": [
              "license.issue"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "mode",
          "data"
        ],
        "title": "license.issue webhook envelope",
        "type": "object"
      },
      "V1WebhookEnvelope_order_paid": {
        "additionalProperties": false,
        "properties": {
          "createdAt": {
            "description": "이벤트가 생성된 시각이에요.",
            "format": "date-time",
            "type": "string"
          },
          "data": {
            "$ref": "#/components/schemas/V1WebhookData_order_paid",
            "description": "결제가 완료된 주문 이벤트 데이터예요."
          },
          "id": {
            "description": "이벤트를 유일하게 구분해요.",
            "example": "evt_123",
            "type": "string"
          },
          "mode": {
            "description": "이벤트의 결제 환경이에요.",
            "enum": [
              "live",
              "test"
            ],
            "type": "string"
          },
          "type": {
            "const": "order.paid",
            "description": "구독한 웹훅 이벤트 이름이에요.",
            "enum": [
              "order.paid"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "mode",
          "data"
        ],
        "title": "order.paid webhook envelope",
        "type": "object"
      },
      "V1WebhookEnvelope_order_refunded": {
        "additionalProperties": false,
        "properties": {
          "createdAt": {
            "description": "이벤트가 생성된 시각이에요.",
            "format": "date-time",
            "type": "string"
          },
          "data": {
            "$ref": "#/components/schemas/V1WebhookData_order_refunded",
            "description": "환불이 완료된 주문 이벤트 데이터예요."
          },
          "id": {
            "description": "이벤트를 유일하게 구분해요.",
            "example": "evt_123",
            "type": "string"
          },
          "mode": {
            "description": "이벤트의 결제 환경이에요.",
            "enum": [
              "live",
              "test"
            ],
            "type": "string"
          },
          "type": {
            "const": "order.refunded",
            "description": "구독한 웹훅 이벤트 이름이에요.",
            "enum": [
              "order.refunded"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "mode",
          "data"
        ],
        "title": "order.refunded webhook envelope",
        "type": "object"
      },
      "V1WebhookEnvelope_refund_approved": {
        "additionalProperties": false,
        "properties": {
          "createdAt": {
            "description": "이벤트가 생성된 시각이에요.",
            "format": "date-time",
            "type": "string"
          },
          "data": {
            "$ref": "#/components/schemas/V1WebhookData_refund_approved",
            "description": "승인된 환불 요청 이벤트 데이터예요."
          },
          "id": {
            "description": "이벤트를 유일하게 구분해요.",
            "example": "evt_123",
            "type": "string"
          },
          "mode": {
            "description": "이벤트의 결제 환경이에요.",
            "enum": [
              "live",
              "test"
            ],
            "type": "string"
          },
          "type": {
            "const": "refund.approved",
            "description": "구독한 웹훅 이벤트 이름이에요.",
            "enum": [
              "refund.approved"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "mode",
          "data"
        ],
        "title": "refund.approved webhook envelope",
        "type": "object"
      },
      "V1WebhookEnvelope_refund_on_hold": {
        "additionalProperties": false,
        "properties": {
          "createdAt": {
            "description": "이벤트가 생성된 시각이에요.",
            "format": "date-time",
            "type": "string"
          },
          "data": {
            "$ref": "#/components/schemas/V1WebhookData_refund_on_hold",
            "description": "보류된 환불 요청 이벤트 데이터예요."
          },
          "id": {
            "description": "이벤트를 유일하게 구분해요.",
            "example": "evt_123",
            "type": "string"
          },
          "mode": {
            "description": "이벤트의 결제 환경이에요.",
            "enum": [
              "live",
              "test"
            ],
            "type": "string"
          },
          "type": {
            "const": "refund.on_hold",
            "description": "구독한 웹훅 이벤트 이름이에요.",
            "enum": [
              "refund.on_hold"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "mode",
          "data"
        ],
        "title": "refund.on_hold webhook envelope",
        "type": "object"
      },
      "V1WebhookEnvelope_refund_rejected": {
        "additionalProperties": false,
        "properties": {
          "createdAt": {
            "description": "이벤트가 생성된 시각이에요.",
            "format": "date-time",
            "type": "string"
          },
          "data": {
            "$ref": "#/components/schemas/V1WebhookData_refund_rejected",
            "description": "거절된 환불 요청 이벤트 데이터예요."
          },
          "id": {
            "description": "이벤트를 유일하게 구분해요.",
            "example": "evt_123",
            "type": "string"
          },
          "mode": {
            "description": "이벤트의 결제 환경이에요.",
            "enum": [
              "live",
              "test"
            ],
            "type": "string"
          },
          "type": {
            "const": "refund.rejected",
            "description": "구독한 웹훅 이벤트 이름이에요.",
            "enum": [
              "refund.rejected"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "mode",
          "data"
        ],
        "title": "refund.rejected webhook envelope",
        "type": "object"
      },
      "V1WebhookEnvelope_refund_requested": {
        "additionalProperties": false,
        "properties": {
          "createdAt": {
            "description": "이벤트가 생성된 시각이에요.",
            "format": "date-time",
            "type": "string"
          },
          "data": {
            "$ref": "#/components/schemas/V1WebhookData_refund_requested",
            "description": "새 환불 요청 이벤트 데이터예요."
          },
          "id": {
            "description": "이벤트를 유일하게 구분해요.",
            "example": "evt_123",
            "type": "string"
          },
          "mode": {
            "description": "이벤트의 결제 환경이에요.",
            "enum": [
              "live",
              "test"
            ],
            "type": "string"
          },
          "type": {
            "const": "refund.requested",
            "description": "구독한 웹훅 이벤트 이름이에요.",
            "enum": [
              "refund.requested"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "mode",
          "data"
        ],
        "title": "refund.requested webhook envelope",
        "type": "object"
      },
      "V1WebhookEnvelope_refund_withdrawn": {
        "additionalProperties": false,
        "properties": {
          "createdAt": {
            "description": "이벤트가 생성된 시각이에요.",
            "format": "date-time",
            "type": "string"
          },
          "data": {
            "$ref": "#/components/schemas/V1WebhookData_refund_withdrawn",
            "description": "철회된 환불 요청 이벤트 데이터예요."
          },
          "id": {
            "description": "이벤트를 유일하게 구분해요.",
            "example": "evt_123",
            "type": "string"
          },
          "mode": {
            "description": "이벤트의 결제 환경이에요.",
            "enum": [
              "live",
              "test"
            ],
            "type": "string"
          },
          "type": {
            "const": "refund.withdrawn",
            "description": "구독한 웹훅 이벤트 이름이에요.",
            "enum": [
              "refund.withdrawn"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "mode",
          "data"
        ],
        "title": "refund.withdrawn webhook envelope",
        "type": "object"
      },
      "V1WebhookEnvelope_subscription_created": {
        "additionalProperties": false,
        "properties": {
          "createdAt": {
            "description": "이벤트가 생성된 시각이에요.",
            "format": "date-time",
            "type": "string"
          },
          "data": {
            "$ref": "#/components/schemas/V1WebhookData_subscription_created",
            "description": "새 구독 생성 이벤트 데이터예요."
          },
          "id": {
            "description": "이벤트를 유일하게 구분해요.",
            "example": "evt_123",
            "type": "string"
          },
          "mode": {
            "description": "이벤트의 결제 환경이에요.",
            "enum": [
              "live",
              "test"
            ],
            "type": "string"
          },
          "type": {
            "const": "subscription.created",
            "description": "구독한 웹훅 이벤트 이름이에요.",
            "enum": [
              "subscription.created"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "mode",
          "data"
        ],
        "title": "subscription.created webhook envelope",
        "type": "object"
      },
      "V1WebhookEnvelope_subscription_ended": {
        "additionalProperties": false,
        "properties": {
          "createdAt": {
            "description": "이벤트가 생성된 시각이에요.",
            "format": "date-time",
            "type": "string"
          },
          "data": {
            "$ref": "#/components/schemas/V1WebhookData_subscription_ended",
            "description": "구독 종료 이벤트 데이터예요."
          },
          "id": {
            "description": "이벤트를 유일하게 구분해요.",
            "example": "evt_123",
            "type": "string"
          },
          "mode": {
            "description": "이벤트의 결제 환경이에요.",
            "enum": [
              "live",
              "test"
            ],
            "type": "string"
          },
          "type": {
            "const": "subscription.ended",
            "description": "구독한 웹훅 이벤트 이름이에요.",
            "enum": [
              "subscription.ended"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "mode",
          "data"
        ],
        "title": "subscription.ended webhook envelope",
        "type": "object"
      },
      "V1WebhookEnvelope_subscription_payment_failed": {
        "additionalProperties": false,
        "properties": {
          "createdAt": {
            "description": "이벤트가 생성된 시각이에요.",
            "format": "date-time",
            "type": "string"
          },
          "data": {
            "$ref": "#/components/schemas/V1WebhookData_subscription_payment_failed",
            "description": "구독 결제 실패 이벤트 데이터예요."
          },
          "id": {
            "description": "이벤트를 유일하게 구분해요.",
            "example": "evt_123",
            "type": "string"
          },
          "mode": {
            "description": "이벤트의 결제 환경이에요.",
            "enum": [
              "live",
              "test"
            ],
            "type": "string"
          },
          "type": {
            "const": "subscription.payment_failed",
            "description": "구독한 웹훅 이벤트 이름이에요.",
            "enum": [
              "subscription.payment_failed"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "mode",
          "data"
        ],
        "title": "subscription.payment_failed webhook envelope",
        "type": "object"
      },
      "V1WebhookEnvelope_subscription_updated": {
        "additionalProperties": false,
        "properties": {
          "createdAt": {
            "description": "이벤트가 생성된 시각이에요.",
            "format": "date-time",
            "type": "string"
          },
          "data": {
            "$ref": "#/components/schemas/V1WebhookData_subscription_updated",
            "description": "구독 변경 이벤트 데이터예요."
          },
          "id": {
            "description": "이벤트를 유일하게 구분해요.",
            "example": "evt_123",
            "type": "string"
          },
          "mode": {
            "description": "이벤트의 결제 환경이에요.",
            "enum": [
              "live",
              "test"
            ],
            "type": "string"
          },
          "type": {
            "const": "subscription.updated",
            "description": "구독한 웹훅 이벤트 이름이에요.",
            "enum": [
              "subscription.updated"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "mode",
          "data"
        ],
        "title": "subscription.updated webhook envelope",
        "type": "object"
      },
      "V1WebhookEnvelope_webhook_test": {
        "additionalProperties": false,
        "properties": {
          "createdAt": {
            "description": "이벤트가 생성된 시각이에요.",
            "format": "date-time",
            "type": "string"
          },
          "data": {
            "$ref": "#/components/schemas/V1WebhookData_webhook_test",
            "description": "웹훅 수신을 확인하는 테스트 이벤트 데이터예요."
          },
          "id": {
            "description": "이벤트를 유일하게 구분해요.",
            "example": "evt_123",
            "type": "string"
          },
          "mode": {
            "description": "이벤트의 결제 환경이에요.",
            "enum": [
              "live",
              "test"
            ],
            "type": "string"
          },
          "type": {
            "const": "webhook.test",
            "description": "구독한 웹훅 이벤트 이름이에요.",
            "enum": [
              "webhook.test"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "mode",
          "data"
        ],
        "title": "webhook.test webhook envelope",
        "type": "object"
      },
      "V1WebhookTestData": {
        "properties": {
          "endpointId": {
            "description": "테스트 이벤트를 보낸 엔드포인트 식별자예요.",
            "example": "wh_123",
            "type": "string"
          },
          "mode": {
            "description": "test 또는 live 웹훅 환경을 알려줘요.",
            "enum": [
              "live",
              "test"
            ],
            "example": "test",
            "type": "string"
          },
          "sentAt": {
            "description": "테스트 이벤트가 발송된 시각이에요.",
            "example": "2026-10-15T00:00:00.000Z",
            "type": "string"
          }
        },
        "required": [
          "endpointId",
          "mode",
          "sentAt"
        ],
        "type": "object"
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "bearerFormat": "PayRi API key",
        "description": "서버용 API 키를 Authorization: Bearer <API 키>로 전달해요.",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "contact": {},
    "description": "PayRi 외부 API의 단일 계약이에요. 모든 시각은 ISO 8601 형식이고, 금액은 통화와 함께 원 단위 정수로 전달해요.\n\n## 인증과 환경 분리\n서버용 API 키는 'Authorization: Bearer <API 키>' 헤더로 보내요. 키는 발급할 때 정한 'live' 또는 'test' 환경에 고정돼요. test 키로 만든 결제 세션은 실제 PG를 호출하지 않으며 주문, 구독, 환불, 웹훅 데이터도 live 환경과 분리해요. API 키로 접근하는 리소스는 키의 환경과 상품 범위 안에서만 보여요.\n\n## 멱등 요청\nAPI 키가 필요한 모든 POST 요청에 'Idempotency-Key' 헤더를 보내요. 서버는 키와 요청 본문을 24시간 보관해요. 같은 키와 같은 본문을 다시 보내면 저장한 응답을 돌려줘요. 같은 키로 다른 본문을 보내면 '409 IDEMPOTENCY_KEY_CONFLICT'를 돌려줘요. '400 VALIDATION_FAILED' 응답은 멱등 키에 저장하지 않고, 같은 요청을 다시 검증해요. 라이선스 앱의 verify·activate·deactivate 요청은 이 규칙에서 제외해요.\n\n## 호출 제한\nAPI 키 요청은 키별 분당 300회까지 허용해요. 라이선스 앱 API는 IP별 분당 60회, 라이선스 키별 분당 30회까지 허용해요. 10분 안에 존재하지 않는 라이선스 키를 10회 조회하면 해당 IP의 조회를 15분 동안 차단해요. 제한 응답의 'Retry-After' 헤더를 확인해요.\n\n## 웹훅 검증\n웹훅 본문은 '{ id, type, createdAt, mode, data }' 형식이에요. 'webhook-id'는 본문 'id'와 같고, 'webhook-timestamp'는 서명 시각이에요. 수신한 원문 본문으로 'webhook-id.webhook-timestamp.rawBody'를 HMAC-SHA256 서명하고 'whsec_' 시크릿으로 'webhook-signature'의 'v1' 값을 검증해요. 재시도에도 이벤트 ID는 같지만 시각과 서명은 다시 계산해요.\n\n## 라이선스 앱 응답 서명\n라이선스 앱 요청에는 hex 또는 base64 형식의 'nonce'를 보내요. 디코딩한 값은 16바이트 이상이어야 하고, 32바이트 hex 사용을 권장해요. 응답의 원문 본문은 Ed25519로 서명하며 'Payri-Signature' 헤더에 'kid'와 서명을 담아요. 앱은 응답 서명, 요청 nonce 일치, 'issuedAt'이 현재 시각에서 5분 이내인지 모두 확인해요. 서명 검증 공개키는 API 키 없이 'GET /v1/licenses/signing-keys'에서 조회해요. 공개 라이선스 검증 API의 응답은 PII와 다른 기기 정보를 포함하지 않아요.\n\n모든 API 오류는 '{ statusCode, code, message, details? }' 형식이에요. 오류 메시지는 한국어로 제공해요.",
    "title": "PayRi API",
    "version": "1"
  },
  "jsonSchemaDialect": "https://spec.openapis.org/oas/3.1/dialect/base",
  "openapi": "3.1.0",
  "paths": {
    "/v1/checkout/sessions": {
      "post": {
        "description": "상품 식별자로 test 또는 live 결제 세션을 만들어요. 공개 연락처 인증이 끝나지 않은 스토어는 409 응답을 돌려줘요. Idempotency-Key는 24시간 보관해요.",
        "operationId": "CheckoutV1Controller_create",
        "parameters": [
          {
            "description": "POST 요청의 중복 실행을 막는 키예요. 24시간 보관하며 본문이 다른 요청에는 같은 키를 쓸 수 없어요.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "단건 결제 세션",
                  "value": {
                    "customer": {
                      "email": "buyer@example.com"
                    },
                    "productId": "prod_123",
                    "successUrl": "https://shop.example.com/complete"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1CreateCheckoutSessionDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "cancelUrl": "https://shop.example.com/cancel",
                  "createdAt": "2026-10-15T00:00:00.000Z",
                  "customer": {
                    "email": "buyer@example.com",
                    "name": "홍길동",
                    "phone": null
                  },
                  "expiresAt": "2026-10-15T00:30:00.000Z",
                  "id": "cs_123",
                  "metadata": {
                    "campaign": "fall"
                  },
                  "mode": "test",
                  "orderId": null,
                  "productId": "prod_123",
                  "status": "open",
                  "successUrl": "https://shop.example.com/complete",
                  "url": "https://payri.kr/checkout/cs_123"
                },
                "schema": {
                  "$ref": "#/components/schemas/V1CheckoutSessionResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "결제 세션 생성",
        "tags": [
          "결제 세션"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "VALIDATION_FAILED",
          "PRODUCT_NOT_FOUND",
          "PRODUCT_ARCHIVED",
          "SOLD_OUT",
          "NOT_STARTED",
          "ENDED",
          "PUBLIC_CONTACT_VERIFICATION_REQUIRED",
          "CHECKOUT_URL_INVALID",
          "RETURN_URL_NOT_ALLOWED",
          "METADATA_INVALID",
          "CHECKOUT_NOT_CONFIGURED",
          "IDEMPOTENCY_KEY_CONFLICT",
          "IDEMPOTENCY_IN_PROGRESS",
          "INTERNAL_ERROR"
        ],
        "x-payri-idempotent": true,
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "body": {
            "customer": {
              "email": "buyer@example.com"
            },
            "productId": "prod_123",
            "successUrl": "https://shop.example.com/complete"
          },
          "headers": {
            "Idempotency-Key": "checkout-prod-123-001"
          },
          "method": "POST",
          "url": "https://api.payri.kr/v1/checkout/sessions"
        },
        "x-payri-response-example": {
          "cancelUrl": "https://shop.example.com/cancel",
          "createdAt": "2026-10-15T00:00:00.000Z",
          "customer": {
            "email": "buyer@example.com",
            "name": "홍길동",
            "phone": null
          },
          "expiresAt": "2026-10-15T00:30:00.000Z",
          "id": "cs_123",
          "metadata": {
            "campaign": "fall"
          },
          "mode": "test",
          "orderId": null,
          "productId": "prod_123",
          "status": "open",
          "successUrl": "https://shop.example.com/complete",
          "url": "https://payri.kr/checkout/cs_123"
        },
        "x-payri-scope": [
          "checkout:write"
        ]
      }
    },
    "/v1/checkout/sessions/{id}": {
      "get": {
        "description": "세션 식별자로 만료 시각과 결제 상태를 조회해요.",
        "operationId": "CheckoutV1Controller_get",
        "parameters": [
          {
            "description": "조회할 결제 세션 식별자예요.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "cs_123",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "cancelUrl": "https://shop.example.com/cancel",
                  "createdAt": "2026-10-15T00:00:00.000Z",
                  "customer": {
                    "email": "buyer@example.com",
                    "name": "홍길동",
                    "phone": null
                  },
                  "expiresAt": "2026-10-15T00:30:00.000Z",
                  "id": "cs_123",
                  "metadata": {
                    "campaign": "fall"
                  },
                  "mode": "test",
                  "orderId": null,
                  "productId": "prod_123",
                  "status": "open",
                  "successUrl": "https://shop.example.com/complete",
                  "url": "https://payri.kr/checkout/cs_123"
                },
                "schema": {
                  "$ref": "#/components/schemas/V1CheckoutSessionResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "결제 세션 조회",
        "tags": [
          "결제 세션"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "CHECKOUT_SESSION_NOT_FOUND",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "headers": {
            "Authorization": "Bearer <API key>"
          },
          "method": "GET",
          "url": "https://api.payri.kr/v1/checkout/sessions/cs_123"
        },
        "x-payri-response-example": {
          "cancelUrl": "https://shop.example.com/cancel",
          "createdAt": "2026-10-15T00:00:00.000Z",
          "customer": {
            "email": "buyer@example.com",
            "name": "홍길동",
            "phone": null
          },
          "expiresAt": "2026-10-15T00:30:00.000Z",
          "id": "cs_123",
          "metadata": {
            "campaign": "fall"
          },
          "mode": "test",
          "orderId": null,
          "productId": "prod_123",
          "status": "open",
          "successUrl": "https://shop.example.com/complete",
          "url": "https://payri.kr/checkout/cs_123"
        },
        "x-payri-scope": [
          "checkout:read"
        ]
      }
    },
    "/v1/licenses": {
      "get": {
        "description": "API 키의 test 또는 live 환경과 상품 범위에 속한 라이선스를 필터와 함께 조회해요.",
        "operationId": "LicenseV1Controller_list",
        "parameters": [
          {
            "description": "조회할 페이지 번호예요.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "number"
            }
          },
          {
            "description": "페이지마다 조회할 항목 수예요.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "example": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "number"
            }
          },
          {
            "description": "라이선스 상태로 결과를 좁혀요.",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "enum": [
                "active",
                "expired",
                "revoked",
                "pending",
                "failed"
              ],
              "example": "active",
              "type": "string"
            }
          },
          {
            "description": "상품 식별자로 라이선스를 좁혀요.",
            "in": "query",
            "name": "productId",
            "required": false,
            "schema": {
              "example": "prod_123",
              "type": "string"
            }
          },
          {
            "description": "라이선스 키로 결과를 좁혀요.",
            "in": "query",
            "name": "key",
            "required": false,
            "schema": {
              "example": "PAYRI-ABCDE-FGHJK-MNPQR-STUVW",
              "type": "string"
            }
          },
          {
            "description": "주문 식별자로 라이선스를 좁혀요.",
            "in": "query",
            "name": "orderId",
            "required": false,
            "schema": {
              "example": "ord_123",
              "type": "string"
            }
          },
          {
            "description": "구매자 이메일 문자열로 라이선스를 좁혀요.",
            "in": "query",
            "name": "buyerEmail",
            "required": false,
            "schema": {
              "example": "buyer@example.com",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "activationCount": 1,
                      "activations": [
                        {
                          "activatedAt": "2026-10-15T00:02:00.000Z",
                          "deactivatedAt": null,
                          "id": "act_123",
                          "label": "내 컴퓨터",
                          "lastVerifiedAt": "2026-10-15T00:02:30.000Z"
                        }
                      ],
                      "buyerEmail": "buyer@example.com",
                      "buyerName": "홍길동",
                      "expiresAt": null,
                      "id": "lic_123",
                      "issuedAt": "2026-10-15T00:01:00.000Z",
                      "key": "PAYRI-TEST-ABCDE-FGHJK-MNPQR-STUVW",
                      "maxActivations": null,
                      "mode": "test",
                      "orderId": "ord_123",
                      "orderItemId": "item_123",
                      "productId": "prod_123",
                      "productName": "디지털 가이드",
                      "revokeReason": null,
                      "revokedAt": null,
                      "rotatedFrom": null,
                      "status": "active"
                    }
                  ],
                  "limit": 20,
                  "page": 1,
                  "total": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/V1LicensePageResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "라이선스 목록 조회",
        "tags": [
          "라이선스"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "VALIDATION_FAILED",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "headers": {
            "Authorization": "Bearer <API key>"
          },
          "method": "GET",
          "url": "https://api.payri.kr/v1/licenses?page=1&limit=20&status=active&productId=prod_123"
        },
        "x-payri-response-example": {
          "data": [
            {
              "activationCount": 1,
              "activations": [
                {
                  "activatedAt": "2026-10-15T00:02:00.000Z",
                  "deactivatedAt": null,
                  "id": "act_123",
                  "label": "내 컴퓨터",
                  "lastVerifiedAt": "2026-10-15T00:02:30.000Z"
                }
              ],
              "buyerEmail": "buyer@example.com",
              "buyerName": "홍길동",
              "expiresAt": null,
              "id": "lic_123",
              "issuedAt": "2026-10-15T00:01:00.000Z",
              "key": "PAYRI-TEST-ABCDE-FGHJK-MNPQR-STUVW",
              "maxActivations": null,
              "mode": "test",
              "orderId": "ord_123",
              "orderItemId": "item_123",
              "productId": "prod_123",
              "productName": "디지털 가이드",
              "revokeReason": null,
              "revokedAt": null,
              "rotatedFrom": null,
              "status": "active"
            }
          ],
          "limit": 20,
          "page": 1,
          "total": 1
        },
        "x-payri-scope": [
          "licenses:read"
        ]
      }
    },
    "/v1/licenses/activate": {
      "post": {
        "description": "서버가 새 활성화 식별자를 발급하고 mode가 포함된 서명된 라이선스 상태를 돌려줘요. 활성화 식별자는 요청에서 지정하지 않아요.",
        "operationId": "LicenseAppV1Controller_activate",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "기기 활성화",
                  "value": {
                    "key": "PAYRI-TEST-ABCDE-FGHJK-MNPQR-STUVW",
                    "label": "내 컴퓨터",
                    "nonce": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
                    "productId": "prod_123"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1LicenseActivateRequestDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "activation": {
                    "activatedAt": "2026-10-15T00:02:00.000Z",
                    "deactivatedAt": null,
                    "id": "act_123",
                    "label": "내 컴퓨터",
                    "lastVerifiedAt": "2026-10-15T00:02:30.000Z"
                  },
                  "activationCount": 1,
                  "expiresAt": null,
                  "issuedAt": "2026-10-15T00:02:30.000Z",
                  "maxActivations": null,
                  "mode": "test",
                  "nonce": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
                  "status": "active"
                },
                "schema": {
                  "$ref": "#/components/schemas/V1LicenseAppResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요.",
            "headers": {
              "Payri-Signature": {
                "description": "응답 본문의 Ed25519 서명이에요.",
                "schema": {
                  "example": "kid=2026-10, ed25519=base64...",
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "LICENSE_RATE_LIMITED",
                  "message": "라이선스 API 호출 한도를 초과했어요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "summary": "라이선스 활성화",
        "tags": [
          "라이선스 앱 API"
        ],
        "x-payri-error-codes": [
          "VALIDATION_FAILED",
          "LICENSE_RATE_LIMITED",
          "LICENSE_LOOKUP_BLOCKED",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": false,
        "x-payri-request-example": {
          "body": {
            "key": "PAYRI-TEST-ABCDE-FGHJK-MNPQR-STUVW",
            "label": "내 컴퓨터",
            "nonce": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
            "productId": "prod_123"
          },
          "headers": {
            "Content-Type": "application/json"
          },
          "method": "POST",
          "url": "https://api.payri.kr/v1/licenses/activate"
        },
        "x-payri-response-example": {
          "activation": {
            "activatedAt": "2026-10-15T00:02:00.000Z",
            "deactivatedAt": null,
            "id": "act_123",
            "label": "내 컴퓨터",
            "lastVerifiedAt": "2026-10-15T00:02:30.000Z"
          },
          "activationCount": 1,
          "expiresAt": null,
          "issuedAt": "2026-10-15T00:02:30.000Z",
          "maxActivations": null,
          "mode": "test",
          "nonce": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
          "status": "active"
        },
        "x-payri-scope": []
      }
    },
    "/v1/licenses/deactivate": {
      "post": {
        "description": "라이선스 키와 활성화 식별자로 한 기기의 활성화를 해제하고 mode가 포함된 서명된 상태를 돌려줘요.",
        "operationId": "LicenseAppV1Controller_deactivate",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "기기 비활성화",
                  "value": {
                    "activationId": "act_123",
                    "key": "PAYRI-TEST-ABCDE-FGHJK-MNPQR-STUVW",
                    "nonce": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
                    "productId": "prod_123"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1LicenseDeactivateRequestDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "activation": {
                    "activatedAt": "2026-10-15T00:02:00.000Z",
                    "deactivatedAt": "2026-10-15T00:03:00.000Z",
                    "id": "act_123",
                    "label": "내 컴퓨터",
                    "lastVerifiedAt": "2026-10-15T00:02:30.000Z"
                  },
                  "activationCount": 0,
                  "expiresAt": null,
                  "issuedAt": "2026-10-15T00:03:00.000Z",
                  "maxActivations": null,
                  "mode": "test",
                  "nonce": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
                  "status": "active"
                },
                "schema": {
                  "$ref": "#/components/schemas/V1LicenseAppResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요.",
            "headers": {
              "Payri-Signature": {
                "description": "응답 본문의 Ed25519 서명이에요.",
                "schema": {
                  "example": "kid=2026-10, ed25519=base64...",
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "LICENSE_RATE_LIMITED",
                  "message": "라이선스 API 호출 한도를 초과했어요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "summary": "라이선스 비활성화",
        "tags": [
          "라이선스 앱 API"
        ],
        "x-payri-error-codes": [
          "VALIDATION_FAILED",
          "LICENSE_RATE_LIMITED",
          "LICENSE_LOOKUP_BLOCKED",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": false,
        "x-payri-request-example": {
          "body": {
            "activationId": "act_123",
            "key": "PAYRI-TEST-ABCDE-FGHJK-MNPQR-STUVW",
            "nonce": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
            "productId": "prod_123"
          },
          "headers": {
            "Content-Type": "application/json"
          },
          "method": "POST",
          "url": "https://api.payri.kr/v1/licenses/deactivate"
        },
        "x-payri-response-example": {
          "activation": {
            "activatedAt": "2026-10-15T00:02:00.000Z",
            "deactivatedAt": "2026-10-15T00:03:00.000Z",
            "id": "act_123",
            "label": "내 컴퓨터",
            "lastVerifiedAt": "2026-10-15T00:02:30.000Z"
          },
          "activationCount": 0,
          "expiresAt": null,
          "issuedAt": "2026-10-15T00:03:00.000Z",
          "maxActivations": null,
          "mode": "test",
          "nonce": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
          "status": "active"
        },
        "x-payri-scope": []
      }
    },
    "/v1/licenses/signing-keys": {
      "get": {
        "description": "키 식별자별 Ed25519 공개키를 조회해 라이선스 응답 서명을 검증해요.",
        "operationId": "LicenseAppV1Controller_signingKeys",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "keys": [
                    {
                      "createdAt": "2026-10-01T00:00:00.000Z",
                      "kid": "2026-10",
                      "publicKey": "AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA="
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/V1LicenseSigningKeysResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "summary": "라이선스 서명 공개키 조회",
        "tags": [
          "라이선스 앱 API"
        ],
        "x-payri-error-codes": [
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": false,
        "x-payri-request-example": {
          "headers": {},
          "method": "GET",
          "url": "https://api.payri.kr/v1/licenses/signing-keys"
        },
        "x-payri-response-example": {
          "keys": [
            {
              "createdAt": "2026-10-01T00:00:00.000Z",
              "kid": "2026-10",
              "publicKey": "AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA="
            }
          ]
        },
        "x-payri-scope": []
      }
    },
    "/v1/licenses/verify": {
      "post": {
        "description": "상품과 라이선스 키의 상태를 검증하고 nonce와 라이선스 mode가 포함된 응답에 Ed25519 서명을 붙여요.",
        "operationId": "LicenseAppV1Controller_verify",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "활성화 상태 검증",
                  "value": {
                    "activationId": "act_123",
                    "key": "PAYRI-TEST-ABCDE-FGHJK-MNPQR-STUVW",
                    "nonce": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
                    "productId": "prod_123"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1LicenseVerifyRequestDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "activation": {
                    "activatedAt": "2026-10-15T00:02:00.000Z",
                    "deactivatedAt": null,
                    "id": "act_123",
                    "label": "내 컴퓨터",
                    "lastVerifiedAt": "2026-10-15T00:02:30.000Z"
                  },
                  "activationCount": 1,
                  "expiresAt": null,
                  "issuedAt": "2026-10-15T00:02:30.000Z",
                  "maxActivations": null,
                  "mode": "test",
                  "nonce": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
                  "status": "active"
                },
                "schema": {
                  "$ref": "#/components/schemas/V1LicenseAppResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요.",
            "headers": {
              "Payri-Signature": {
                "description": "응답 본문의 Ed25519 서명이에요.",
                "schema": {
                  "example": "kid=2026-10, ed25519=base64...",
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "LICENSE_RATE_LIMITED",
                  "message": "라이선스 API 호출 한도를 초과했어요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "summary": "라이선스 검증",
        "tags": [
          "라이선스 앱 API"
        ],
        "x-payri-error-codes": [
          "VALIDATION_FAILED",
          "LICENSE_RATE_LIMITED",
          "LICENSE_LOOKUP_BLOCKED",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": false,
        "x-payri-request-example": {
          "body": {
            "activationId": "act_123",
            "key": "PAYRI-TEST-ABCDE-FGHJK-MNPQR-STUVW",
            "nonce": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
            "productId": "prod_123"
          },
          "headers": {
            "Content-Type": "application/json"
          },
          "method": "POST",
          "url": "https://api.payri.kr/v1/licenses/verify"
        },
        "x-payri-response-example": {
          "activation": {
            "activatedAt": "2026-10-15T00:02:00.000Z",
            "deactivatedAt": null,
            "id": "act_123",
            "label": "내 컴퓨터",
            "lastVerifiedAt": "2026-10-15T00:02:30.000Z"
          },
          "activationCount": 1,
          "expiresAt": null,
          "issuedAt": "2026-10-15T00:02:30.000Z",
          "maxActivations": null,
          "mode": "test",
          "nonce": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
          "status": "active"
        },
        "x-payri-scope": []
      }
    },
    "/v1/licenses/{id}": {
      "get": {
        "description": "라이선스 식별자로 발급 정보와 활성화 정보를 조회해요.",
        "operationId": "LicenseV1Controller_get",
        "parameters": [
          {
            "description": "조회할 라이선스 식별자예요.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "lic_123",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "activationCount": 1,
                  "activations": [
                    {
                      "activatedAt": "2026-10-15T00:02:00.000Z",
                      "deactivatedAt": null,
                      "id": "act_123",
                      "label": "내 컴퓨터",
                      "lastVerifiedAt": "2026-10-15T00:02:30.000Z"
                    }
                  ],
                  "buyerEmail": "buyer@example.com",
                  "buyerName": "홍길동",
                  "expiresAt": null,
                  "id": "lic_123",
                  "issuedAt": "2026-10-15T00:01:00.000Z",
                  "key": "PAYRI-TEST-ABCDE-FGHJK-MNPQR-STUVW",
                  "maxActivations": null,
                  "mode": "test",
                  "orderId": "ord_123",
                  "orderItemId": "item_123",
                  "productId": "prod_123",
                  "productName": "디지털 가이드",
                  "revokeReason": null,
                  "revokedAt": null,
                  "rotatedFrom": null,
                  "status": "active"
                },
                "schema": {
                  "$ref": "#/components/schemas/V1LicenseResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "라이선스 상세 조회",
        "tags": [
          "라이선스"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "LICENSE_NOT_FOUND",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "headers": {
            "Authorization": "Bearer <API key>"
          },
          "method": "GET",
          "url": "https://api.payri.kr/v1/licenses/lic_123"
        },
        "x-payri-response-example": {
          "activationCount": 1,
          "activations": [
            {
              "activatedAt": "2026-10-15T00:02:00.000Z",
              "deactivatedAt": null,
              "id": "act_123",
              "label": "내 컴퓨터",
              "lastVerifiedAt": "2026-10-15T00:02:30.000Z"
            }
          ],
          "buyerEmail": "buyer@example.com",
          "buyerName": "홍길동",
          "expiresAt": null,
          "id": "lic_123",
          "issuedAt": "2026-10-15T00:01:00.000Z",
          "key": "PAYRI-TEST-ABCDE-FGHJK-MNPQR-STUVW",
          "maxActivations": null,
          "mode": "test",
          "orderId": "ord_123",
          "orderItemId": "item_123",
          "productId": "prod_123",
          "productName": "디지털 가이드",
          "revokeReason": null,
          "revokedAt": null,
          "rotatedFrom": null,
          "status": "active"
        },
        "x-payri-scope": [
          "licenses:read"
        ]
      }
    },
    "/v1/openapi.json": {
      "get": {
        "description": "PayRi API의 OpenAPI 3.1 JSON 문서를 조회해요.",
        "operationId": "OpenApiV1Controller_getOpenApiDocument",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "info": {
                    "title": "PayRi API",
                    "version": "1"
                  },
                  "openapi": "3.1.0"
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "OpenAPI JSON 문서를 반환해요.",
            "headers": {
              "Cache-Control": {
                "description": "문서를 5분 동안 공개 캐시할 수 있어요.",
                "schema": {
                  "example": "public, max-age=300",
                  "type": "string"
                }
              }
            }
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "summary": "OpenAPI 문서 조회",
        "tags": [
          "명세"
        ],
        "x-payri-error-codes": [
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": false,
        "x-payri-request-example": {
          "headers": {},
          "method": "GET",
          "url": "https://api.payri.kr/v1/openapi.json"
        },
        "x-payri-response-example": {
          "info": {
            "title": "PayRi API",
            "version": "1"
          },
          "openapi": "3.1.0"
        },
        "x-payri-scope": []
      }
    },
    "/v1/orders": {
      "get": {
        "description": "API 키의 test 또는 live 환경과 상품 범위에 속한 주문을 페이지 단위로 조회해요.",
        "operationId": "OrdersV1Controller_list",
        "parameters": [
          {
            "description": "조회할 페이지 번호예요.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "number"
            }
          },
          {
            "description": "페이지마다 조회할 항목 수예요.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "example": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "number"
            }
          },
          {
            "description": "구매자 이메일로 주문을 찾아요.",
            "in": "query",
            "name": "buyerEmail",
            "required": false,
            "schema": {
              "example": "buyer@example.com",
              "format": "email",
              "type": "string"
            }
          },
          {
            "description": "주문 상태로 결과를 좁혀요.",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "enum": [
                "pending",
                "paid",
                "failed",
                "refunded"
              ],
              "example": "paid",
              "type": "string"
            }
          },
          {
            "description": "상품 식별자로 주문을 좁혀요.",
            "in": "query",
            "name": "productId",
            "required": false,
            "schema": {
              "example": "prod_123",
              "type": "string"
            }
          },
          {
            "description": "결제 세션 식별자로 주문을 좁혀요.",
            "in": "query",
            "name": "checkoutSessionId",
            "required": false,
            "schema": {
              "example": "cs_123",
              "type": "string"
            }
          },
          {
            "description": "이 시각 이후 생성된 주문을 조회해요.",
            "in": "query",
            "name": "createdFrom",
            "required": false,
            "schema": {
              "example": "2026-10-01T00:00:00.000Z",
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "이 시각 이전 생성된 주문을 조회해요.",
            "in": "query",
            "name": "createdTo",
            "required": false,
            "schema": {
              "example": "2026-10-15T00:00:00.000Z",
              "format": "date-time",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "amount": 10000,
                      "billingInterval": null,
                      "buyerEmail": "buyer@example.com",
                      "buyerName": "홍길동",
                      "buyerPhone": "01012345678",
                      "checkoutSessionId": "cs_123",
                      "confirmedAt": null,
                      "coupon": null,
                      "createdAt": "2026-10-15T00:00:00.000Z",
                      "currency": "KRW",
                      "id": "ord_123",
                      "metadata": {
                        "campaign": "fall"
                      },
                      "method": "card",
                      "mode": "test",
                      "paidAt": "2026-10-15T00:01:00.000Z",
                      "periodEnd": null,
                      "periodStart": null,
                      "planId": null,
                      "productId": "prod_123",
                      "productName": "디지털 가이드",
                      "refundedAmount": 0,
                      "refundedAt": null,
                      "refunds": [],
                      "status": "paid",
                      "subscriptionEvent": null,
                      "subscriptionId": null,
                      "withdrawalNoticeAgreedAt": "2026-10-15T00:00:00.000Z"
                    }
                  ],
                  "limit": 20,
                  "page": 1,
                  "total": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/V1OrderPageResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "주문 목록 조회",
        "tags": [
          "주문"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "VALIDATION_FAILED",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "headers": {
            "Authorization": "Bearer <API key>"
          },
          "method": "GET",
          "url": "https://api.payri.kr/v1/orders?page=1&limit=20&status=paid"
        },
        "x-payri-response-example": {
          "data": [
            {
              "amount": 10000,
              "billingInterval": null,
              "buyerEmail": "buyer@example.com",
              "buyerName": "홍길동",
              "buyerPhone": "01012345678",
              "checkoutSessionId": "cs_123",
              "confirmedAt": null,
              "coupon": null,
              "createdAt": "2026-10-15T00:00:00.000Z",
              "currency": "KRW",
              "id": "ord_123",
              "metadata": {
                "campaign": "fall"
              },
              "method": "card",
              "mode": "test",
              "paidAt": "2026-10-15T00:01:00.000Z",
              "periodEnd": null,
              "periodStart": null,
              "planId": null,
              "productId": "prod_123",
              "productName": "디지털 가이드",
              "refundedAmount": 0,
              "refundedAt": null,
              "refunds": [],
              "status": "paid",
              "subscriptionEvent": null,
              "subscriptionId": null,
              "withdrawalNoticeAgreedAt": "2026-10-15T00:00:00.000Z"
            }
          ],
          "limit": 20,
          "page": 1,
          "total": 1
        },
        "x-payri-scope": [
          "orders:read"
        ]
      }
    },
    "/v1/orders/{id}": {
      "get": {
        "description": "주문 식별자로 상세 정보와 환불 내역을 조회해요.",
        "operationId": "OrdersV1Controller_get",
        "parameters": [
          {
            "description": "조회할 주문 식별자예요.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "ord_123",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "amount": 10000,
                  "billingInterval": null,
                  "buyerEmail": "buyer@example.com",
                  "buyerName": "홍길동",
                  "buyerPhone": "01012345678",
                  "checkoutSessionId": "cs_123",
                  "confirmedAt": null,
                  "coupon": null,
                  "createdAt": "2026-10-15T00:00:00.000Z",
                  "currency": "KRW",
                  "id": "ord_123",
                  "metadata": {
                    "campaign": "fall"
                  },
                  "method": "card",
                  "mode": "test",
                  "paidAt": "2026-10-15T00:01:00.000Z",
                  "periodEnd": null,
                  "periodStart": null,
                  "planId": null,
                  "productId": "prod_123",
                  "productName": "디지털 가이드",
                  "refundedAmount": 0,
                  "refundedAt": null,
                  "refunds": [],
                  "status": "paid",
                  "subscriptionEvent": null,
                  "subscriptionId": null,
                  "withdrawalNoticeAgreedAt": "2026-10-15T00:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/V1OrderResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "주문 상세 조회",
        "tags": [
          "주문"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "ORDER_NOT_FOUND",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "headers": {
            "Authorization": "Bearer <API key>"
          },
          "method": "GET",
          "url": "https://api.payri.kr/v1/orders/ord_123"
        },
        "x-payri-response-example": {
          "amount": 10000,
          "billingInterval": null,
          "buyerEmail": "buyer@example.com",
          "buyerName": "홍길동",
          "buyerPhone": "01012345678",
          "checkoutSessionId": "cs_123",
          "confirmedAt": null,
          "coupon": null,
          "createdAt": "2026-10-15T00:00:00.000Z",
          "currency": "KRW",
          "id": "ord_123",
          "metadata": {
            "campaign": "fall"
          },
          "method": "card",
          "mode": "test",
          "paidAt": "2026-10-15T00:01:00.000Z",
          "periodEnd": null,
          "periodStart": null,
          "planId": null,
          "productId": "prod_123",
          "productName": "디지털 가이드",
          "refundedAmount": 0,
          "refundedAt": null,
          "refunds": [],
          "status": "paid",
          "subscriptionEvent": null,
          "subscriptionId": null,
          "withdrawalNoticeAgreedAt": "2026-10-15T00:00:00.000Z"
        },
        "x-payri-scope": [
          "orders:read"
        ]
      }
    },
    "/v1/orders/{id}/refund": {
      "post": {
        "description": "전액 또는 부분 환불을 요청해요. Idempotency-Key를 24시간 기억하며 같은 키에 다른 본문을 보내면 409 응답을 돌려줘요.",
        "operationId": "OrdersV1Controller_refund",
        "parameters": [
          {
            "description": "환불할 주문 식별자예요.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "ord_123",
              "type": "string"
            }
          },
          {
            "description": "POST 요청의 중복 실행을 막는 키예요. 24시간 보관하며 본문이 다른 요청에는 같은 키를 쓸 수 없어요.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "부분 환불 요청",
                  "value": {
                    "amount": 5000,
                    "reason": "고객 요청"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1RefundOrderDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "amount": 10000,
                  "billingInterval": null,
                  "buyerEmail": "buyer@example.com",
                  "buyerName": "홍길동",
                  "buyerPhone": "01012345678",
                  "checkoutSessionId": "cs_123",
                  "confirmedAt": null,
                  "coupon": null,
                  "createdAt": "2026-10-15T00:00:00.000Z",
                  "currency": "KRW",
                  "id": "ord_123",
                  "metadata": {
                    "campaign": "fall"
                  },
                  "method": "card",
                  "mode": "test",
                  "paidAt": "2026-10-15T00:01:00.000Z",
                  "periodEnd": null,
                  "periodStart": null,
                  "planId": null,
                  "productId": "prod_123",
                  "productName": "디지털 가이드",
                  "refundedAmount": 5000,
                  "refundedAt": null,
                  "refunds": [
                    {
                      "amount": 5000,
                      "at": "2026-10-15T00:02:00.000Z",
                      "currency": "KRW",
                      "reason": "고객 요청"
                    }
                  ],
                  "status": "paid",
                  "subscriptionEvent": null,
                  "subscriptionId": null,
                  "withdrawalNoticeAgreedAt": "2026-10-15T00:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/V1OrderResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "주문 환불",
        "tags": [
          "주문"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "VALIDATION_FAILED",
          "ORDER_NOT_FOUND",
          "REFUND_NOT_PAID",
          "REFUND_REQUEST_PENDING",
          "REFUND_SETTLED",
          "REFUND_NONE_LEFT",
          "REFUND_AMOUNT_INVALID",
          "REFUND_EXCEEDS",
          "REFUND_IN_PROGRESS",
          "PG_REFUND_FAILED",
          "PG_REFUND_AMBIGUOUS",
          "IDEMPOTENCY_KEY_CONFLICT",
          "IDEMPOTENCY_IN_PROGRESS",
          "INTERNAL_ERROR"
        ],
        "x-payri-idempotent": true,
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "body": {
            "amount": 5000,
            "reason": "고객 요청"
          },
          "headers": {
            "Idempotency-Key": "refund-ord-123-001"
          },
          "method": "POST",
          "url": "https://api.payri.kr/v1/orders/ord_123/refund"
        },
        "x-payri-response-example": {
          "amount": 10000,
          "billingInterval": null,
          "buyerEmail": "buyer@example.com",
          "buyerName": "홍길동",
          "buyerPhone": "01012345678",
          "checkoutSessionId": "cs_123",
          "confirmedAt": null,
          "coupon": null,
          "createdAt": "2026-10-15T00:00:00.000Z",
          "currency": "KRW",
          "id": "ord_123",
          "metadata": {
            "campaign": "fall"
          },
          "method": "card",
          "mode": "test",
          "paidAt": "2026-10-15T00:01:00.000Z",
          "periodEnd": null,
          "periodStart": null,
          "planId": null,
          "productId": "prod_123",
          "productName": "디지털 가이드",
          "refundedAmount": 5000,
          "refundedAt": null,
          "refunds": [
            {
              "amount": 5000,
              "at": "2026-10-15T00:02:00.000Z",
              "currency": "KRW",
              "reason": "고객 요청"
            }
          ],
          "status": "paid",
          "subscriptionEvent": null,
          "subscriptionId": null,
          "withdrawalNoticeAgreedAt": "2026-10-15T00:00:00.000Z"
        },
        "x-payri-scope": [
          "refunds:write"
        ]
      }
    },
    "/v1/portal-sessions": {
      "post": {
        "description": "구독 식별자 또는 구매자 이메일로 제한된 구매자 포털 세션을 만들어요. Idempotency-Key는 24시간 보관해요.",
        "operationId": "PortalSessionsV1Controller_create",
        "parameters": [
          {
            "description": "POST 요청의 중복 실행을 막는 키예요. 24시간 보관하며 본문이 다른 요청에는 같은 키를 쓸 수 없어요.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "구독 기반 포털 세션",
                  "value": {
                    "returnUrl": "https://shop.example.com/account",
                    "subscriptionId": "sub_123"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/CreatePortalSessionV1Dto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "expiresAt": "2026-10-15T01:00:00.000Z",
                  "mode": "test",
                  "token": "eyJhbGciOi...",
                  "url": "https://payri.kr/portal#token=eyJhbGciOi..."
                },
                "schema": {
                  "$ref": "#/components/schemas/V1PortalSessionResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "구매자 포털 세션 생성",
        "tags": [
          "포털 세션"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "VALIDATION_FAILED",
          "BAD_REQUEST",
          "NOT_FOUND",
          "SUBSCRIPTION_NOT_FOUND",
          "CHECKOUT_URL_INVALID",
          "RETURN_URL_NOT_ALLOWED",
          "IDEMPOTENCY_KEY_CONFLICT",
          "IDEMPOTENCY_IN_PROGRESS",
          "INTERNAL_ERROR"
        ],
        "x-payri-idempotent": true,
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "body": {
            "returnUrl": "https://shop.example.com/account",
            "subscriptionId": "sub_123"
          },
          "headers": {
            "Idempotency-Key": "portal-sub-123-001"
          },
          "method": "POST",
          "url": "https://api.payri.kr/v1/portal-sessions"
        },
        "x-payri-response-example": {
          "expiresAt": "2026-10-15T01:00:00.000Z",
          "mode": "test",
          "token": "eyJhbGciOi...",
          "url": "https://payri.kr/portal#token=eyJhbGciOi..."
        },
        "x-payri-scope": [
          "subscriptions:write"
        ]
      }
    },
    "/v1/products": {
      "get": {
        "description": "API 키의 test 또는 live 환경과 공개 상품 범위에 속한 상품을 페이지 단위로 조회해요.",
        "operationId": "ProductsV1Controller_list",
        "parameters": [
          {
            "description": "조회할 페이지 번호예요.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "number"
            }
          },
          {
            "description": "페이지마다 조회할 항목 수예요.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "example": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "number"
            }
          },
          {
            "description": "상품 상태로 결과를 좁혀요.",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "enum": [
                "draft",
                "active",
                "archived"
              ],
              "example": "active",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "contents": [
                        {
                          "label": "가이드 PDF",
                          "type": "file"
                        }
                      ],
                      "createdAt": "2026-10-01T00:00:00.000Z",
                      "deliverable": {
                        "fileName": "guide.pdf",
                        "fileSize": 102400,
                        "licenseIssuance": null,
                        "maxActivations": null,
                        "note": null,
                        "type": "file"
                      },
                      "description": "상품 상세 설명이에요.",
                      "displayOrder": 0,
                      "icon": "",
                      "iconColor": null,
                      "id": "prod_123",
                      "imageUrl": null,
                      "linkId": "link_123",
                      "listed": true,
                      "mode": "test",
                      "name": "디지털 가이드",
                      "pricing": {
                        "amount": 10000,
                        "currency": "KRW",
                        "interval": null,
                        "plans": null,
                        "type": "one_time"
                      },
                      "refundPolicy": {
                        "customText": null,
                        "refundDays": null,
                        "requireApproval": false,
                        "subscription": null,
                        "type": "within_7days"
                      },
                      "saleEndsAt": null,
                      "saleStartsAt": null,
                      "status": "active",
                      "stockLimit": null,
                      "summary": "바로 내려받는 가이드예요."
                    }
                  ],
                  "limit": 20,
                  "page": 1,
                  "total": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/V1ProductPageResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "상품 목록 조회",
        "tags": [
          "상품"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "VALIDATION_FAILED",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "headers": {
            "Authorization": "Bearer <API key>"
          },
          "method": "GET",
          "url": "https://api.payri.kr/v1/products?page=1&limit=20&status=active"
        },
        "x-payri-response-example": {
          "data": [
            {
              "contents": [
                {
                  "label": "가이드 PDF",
                  "type": "file"
                }
              ],
              "createdAt": "2026-10-01T00:00:00.000Z",
              "deliverable": {
                "fileName": "guide.pdf",
                "fileSize": 102400,
                "licenseIssuance": null,
                "maxActivations": null,
                "note": null,
                "type": "file"
              },
              "description": "상품 상세 설명이에요.",
              "displayOrder": 0,
              "icon": "",
              "iconColor": null,
              "id": "prod_123",
              "imageUrl": null,
              "linkId": "link_123",
              "listed": true,
              "mode": "test",
              "name": "디지털 가이드",
              "pricing": {
                "amount": 10000,
                "currency": "KRW",
                "interval": null,
                "plans": null,
                "type": "one_time"
              },
              "refundPolicy": {
                "customText": null,
                "refundDays": null,
                "requireApproval": false,
                "subscription": null,
                "type": "within_7days"
              },
              "saleEndsAt": null,
              "saleStartsAt": null,
              "status": "active",
              "stockLimit": null,
              "summary": "바로 내려받는 가이드예요."
            }
          ],
          "limit": 20,
          "page": 1,
          "total": 1
        },
        "x-payri-scope": [
          "products:read"
        ]
      }
    },
    "/v1/products/{id}": {
      "get": {
        "description": "상품 식별자로 가격과 전달 정보를 조회해요.",
        "operationId": "ProductsV1Controller_get",
        "parameters": [
          {
            "description": "조회할 상품 식별자예요.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "prod_123",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "contents": [
                    {
                      "label": "가이드 PDF",
                      "type": "file"
                    }
                  ],
                  "createdAt": "2026-10-01T00:00:00.000Z",
                  "deliverable": {
                    "fileName": "guide.pdf",
                    "fileSize": 102400,
                    "licenseIssuance": null,
                    "maxActivations": null,
                    "note": null,
                    "type": "file"
                  },
                  "description": "상품 상세 설명이에요.",
                  "displayOrder": 0,
                  "icon": "",
                  "iconColor": null,
                  "id": "prod_123",
                  "imageUrl": null,
                  "linkId": "link_123",
                  "listed": true,
                  "mode": "test",
                  "name": "디지털 가이드",
                  "pricing": {
                    "amount": 10000,
                    "currency": "KRW",
                    "interval": null,
                    "plans": null,
                    "type": "one_time"
                  },
                  "refundPolicy": {
                    "customText": null,
                    "refundDays": null,
                    "requireApproval": false,
                    "subscription": null,
                    "type": "within_7days"
                  },
                  "saleEndsAt": null,
                  "saleStartsAt": null,
                  "status": "active",
                  "stockLimit": null,
                  "summary": "바로 내려받는 가이드예요."
                },
                "schema": {
                  "$ref": "#/components/schemas/V1ProductResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "상품 상세 조회",
        "tags": [
          "상품"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "PRODUCT_NOT_FOUND",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "headers": {
            "Authorization": "Bearer <API key>"
          },
          "method": "GET",
          "url": "https://api.payri.kr/v1/products/prod_123"
        },
        "x-payri-response-example": {
          "contents": [
            {
              "label": "가이드 PDF",
              "type": "file"
            }
          ],
          "createdAt": "2026-10-01T00:00:00.000Z",
          "deliverable": {
            "fileName": "guide.pdf",
            "fileSize": 102400,
            "licenseIssuance": null,
            "maxActivations": null,
            "note": null,
            "type": "file"
          },
          "description": "상품 상세 설명이에요.",
          "displayOrder": 0,
          "icon": "",
          "iconColor": null,
          "id": "prod_123",
          "imageUrl": null,
          "linkId": "link_123",
          "listed": true,
          "mode": "test",
          "name": "디지털 가이드",
          "pricing": {
            "amount": 10000,
            "currency": "KRW",
            "interval": null,
            "plans": null,
            "type": "one_time"
          },
          "refundPolicy": {
            "customText": null,
            "refundDays": null,
            "requireApproval": false,
            "subscription": null,
            "type": "within_7days"
          },
          "saleEndsAt": null,
          "saleStartsAt": null,
          "status": "active",
          "stockLimit": null,
          "summary": "바로 내려받는 가이드예요."
        },
        "x-payri-scope": [
          "products:read"
        ]
      }
    },
    "/v1/refund-requests": {
      "get": {
        "description": "API 키의 test 또는 live 환경에 속한 환불 요청을 페이지 단위로 조회해요.",
        "operationId": "RefundRequestsV1Controller_list",
        "parameters": [
          {
            "description": "조회할 페이지 번호예요.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "number"
            }
          },
          {
            "description": "페이지마다 조회할 항목 수예요.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "example": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "number"
            }
          },
          {
            "description": "환불 요청 상태로 결과를 좁혀요.",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "enum": [
                "requested",
                "on_hold",
                "processing",
                "approved",
                "rejected",
                "withdrawn"
              ],
              "example": "requested",
              "type": "string"
            }
          },
          {
            "description": "주문 식별자로 환불 요청을 좁혀요.",
            "in": "query",
            "name": "orderId",
            "required": false,
            "schema": {
              "example": "ord_123",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "approvedAmount": null,
                      "autoApproveAt": "2026-10-18T00:00:00.000Z",
                      "breakdown": {
                        "orders": [
                          {
                            "amount": 10000,
                            "currency": "KRW",
                            "orderId": "ord_123"
                          }
                        ]
                      },
                      "buyerEmail": "buyer@example.com",
                      "buyerReason": "상품을 환불하고 싶어요.",
                      "createdAt": "2026-10-15T00:00:00.000Z",
                      "currency": "KRW",
                      "decidedAt": null,
                      "decidedBy": null,
                      "hold": null,
                      "id": "rfr_123",
                      "kind": "order",
                      "mode": "test",
                      "orderId": "ord_123",
                      "productId": "prod_123",
                      "refundableAmount": 10000,
                      "rejectReason": null,
                      "requestedAmount": 10000,
                      "status": "requested",
                      "statutory": true,
                      "subscriptionId": null,
                      "updatedAt": "2026-10-15T00:00:00.000Z",
                      "warnings": []
                    }
                  ],
                  "limit": 20,
                  "page": 1,
                  "total": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/V1RefundRequestPageResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "환불 요청 목록 조회",
        "tags": [
          "환불 요청"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "VALIDATION_FAILED",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "headers": {
            "Authorization": "Bearer <API key>"
          },
          "method": "GET",
          "url": "https://api.payri.kr/v1/refund-requests?page=1&limit=20&status=requested"
        },
        "x-payri-response-example": {
          "data": [
            {
              "approvedAmount": null,
              "autoApproveAt": "2026-10-18T00:00:00.000Z",
              "breakdown": {
                "orders": [
                  {
                    "amount": 10000,
                    "currency": "KRW",
                    "orderId": "ord_123"
                  }
                ]
              },
              "buyerEmail": "buyer@example.com",
              "buyerReason": "상품을 환불하고 싶어요.",
              "createdAt": "2026-10-15T00:00:00.000Z",
              "currency": "KRW",
              "decidedAt": null,
              "decidedBy": null,
              "hold": null,
              "id": "rfr_123",
              "kind": "order",
              "mode": "test",
              "orderId": "ord_123",
              "productId": "prod_123",
              "refundableAmount": 10000,
              "rejectReason": null,
              "requestedAmount": 10000,
              "status": "requested",
              "statutory": true,
              "subscriptionId": null,
              "updatedAt": "2026-10-15T00:00:00.000Z",
              "warnings": []
            }
          ],
          "limit": 20,
          "page": 1,
          "total": 1
        },
        "x-payri-scope": [
          "refunds:read"
        ]
      }
    },
    "/v1/refund-requests/{id}": {
      "get": {
        "description": "환불 요청 식별자로 요청, 결정과 환불 계산 정보를 조회해요.",
        "operationId": "RefundRequestsV1Controller_get",
        "parameters": [
          {
            "description": "조회할 환불 요청 식별자예요.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "rfr_123",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "approvedAmount": null,
                  "autoApproveAt": "2026-10-18T00:00:00.000Z",
                  "breakdown": {
                    "orders": [
                      {
                        "amount": 10000,
                        "currency": "KRW",
                        "orderId": "ord_123"
                      }
                    ]
                  },
                  "buyerEmail": "buyer@example.com",
                  "buyerReason": "상품을 환불하고 싶어요.",
                  "createdAt": "2026-10-15T00:00:00.000Z",
                  "currency": "KRW",
                  "decidedAt": null,
                  "decidedBy": null,
                  "hold": null,
                  "id": "rfr_123",
                  "kind": "order",
                  "mode": "test",
                  "orderId": "ord_123",
                  "productId": "prod_123",
                  "refundableAmount": 10000,
                  "rejectReason": null,
                  "requestedAmount": 10000,
                  "status": "requested",
                  "statutory": true,
                  "subscriptionId": null,
                  "updatedAt": "2026-10-15T00:00:00.000Z",
                  "warnings": []
                },
                "schema": {
                  "$ref": "#/components/schemas/V1RefundRequestResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "환불 요청 상세 조회",
        "tags": [
          "환불 요청"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "REFUND_REQUEST_NOT_FOUND",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "headers": {
            "Authorization": "Bearer <API key>"
          },
          "method": "GET",
          "url": "https://api.payri.kr/v1/refund-requests/rfr_123"
        },
        "x-payri-response-example": {
          "approvedAmount": null,
          "autoApproveAt": "2026-10-18T00:00:00.000Z",
          "breakdown": {
            "orders": [
              {
                "amount": 10000,
                "currency": "KRW",
                "orderId": "ord_123"
              }
            ]
          },
          "buyerEmail": "buyer@example.com",
          "buyerReason": "상품을 환불하고 싶어요.",
          "createdAt": "2026-10-15T00:00:00.000Z",
          "currency": "KRW",
          "decidedAt": null,
          "decidedBy": null,
          "hold": null,
          "id": "rfr_123",
          "kind": "order",
          "mode": "test",
          "orderId": "ord_123",
          "productId": "prod_123",
          "refundableAmount": 10000,
          "rejectReason": null,
          "requestedAmount": 10000,
          "status": "requested",
          "statutory": true,
          "subscriptionId": null,
          "updatedAt": "2026-10-15T00:00:00.000Z",
          "warnings": []
        },
        "x-payri-scope": [
          "refunds:read"
        ]
      }
    },
    "/v1/refund-requests/{id}/approve": {
      "post": {
        "description": "환불 요청을 승인해요. Idempotency-Key는 24시간 보관해요.",
        "operationId": "RefundRequestsV1Controller_approve",
        "parameters": [
          {
            "description": "승인할 환불 요청 식별자예요.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "rfr_123",
              "type": "string"
            }
          },
          {
            "description": "POST 요청의 중복 실행을 막는 키예요. 24시간 보관하며 본문이 다른 요청에는 같은 키를 쓸 수 없어요.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "환불 요청 승인",
                  "value": {
                    "acknowledgeMismatch": true,
                    "amount": 10000
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1RefundRequestApproveDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "refundRequest": {
                    "approvedAmount": null,
                    "autoApproveAt": "2026-10-18T00:00:00.000Z",
                    "breakdown": {
                      "orders": [
                        {
                          "amount": 10000,
                          "currency": "KRW",
                          "orderId": "ord_123"
                        }
                      ]
                    },
                    "buyerEmail": "buyer@example.com",
                    "buyerReason": "상품을 환불하고 싶어요.",
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "currency": "KRW",
                    "decidedAt": null,
                    "decidedBy": null,
                    "hold": null,
                    "id": "rfr_123",
                    "kind": "order",
                    "mode": "test",
                    "orderId": "ord_123",
                    "productId": "prod_123",
                    "refundableAmount": 10000,
                    "rejectReason": null,
                    "requestedAmount": 10000,
                    "status": "requested",
                    "statutory": true,
                    "subscriptionId": null,
                    "updatedAt": "2026-10-15T00:00:00.000Z",
                    "warnings": []
                  },
                  "warnings": []
                },
                "schema": {
                  "$ref": "#/components/schemas/V1RefundActionResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "환불 요청 승인",
        "tags": [
          "환불 요청"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "VALIDATION_FAILED",
          "REFUND_REQUEST_NOT_FOUND",
          "REFUND_REQUEST_NOT_ACTIONABLE",
          "REFUND_STATUTORY_LOCKED",
          "REFUND_AMOUNT_INVALID",
          "REFUND_AMOUNT_EXCEEDS",
          "REFUND_AMOUNT_MISMATCH",
          "ORDER_NOT_FOUND",
          "REFUND_NOT_PAID",
          "REFUND_SETTLED",
          "REFUND_NONE_LEFT",
          "REFUND_EXCEEDS",
          "REFUND_IN_PROGRESS",
          "PG_REFUND_FAILED",
          "PG_REFUND_AMBIGUOUS",
          "SUBSCRIPTION_REFUND_AMOUNT_INVALID",
          "SUBSCRIPTION_REFUND_NONE_LEFT",
          "SUBSCRIPTION_REFUND_SETTLED",
          "IDEMPOTENCY_KEY_CONFLICT",
          "IDEMPOTENCY_IN_PROGRESS",
          "INTERNAL_ERROR"
        ],
        "x-payri-idempotent": true,
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "body": {
            "acknowledgeMismatch": true,
            "amount": 10000
          },
          "headers": {
            "Idempotency-Key": "approve-rfr-123-001"
          },
          "method": "POST",
          "url": "https://api.payri.kr/v1/refund-requests/rfr_123/approve"
        },
        "x-payri-response-example": {
          "refundRequest": {
            "approvedAmount": null,
            "autoApproveAt": "2026-10-18T00:00:00.000Z",
            "breakdown": {
              "orders": [
                {
                  "amount": 10000,
                  "currency": "KRW",
                  "orderId": "ord_123"
                }
              ]
            },
            "buyerEmail": "buyer@example.com",
            "buyerReason": "상품을 환불하고 싶어요.",
            "createdAt": "2026-10-15T00:00:00.000Z",
            "currency": "KRW",
            "decidedAt": null,
            "decidedBy": null,
            "hold": null,
            "id": "rfr_123",
            "kind": "order",
            "mode": "test",
            "orderId": "ord_123",
            "productId": "prod_123",
            "refundableAmount": 10000,
            "rejectReason": null,
            "requestedAmount": 10000,
            "status": "requested",
            "statutory": true,
            "subscriptionId": null,
            "updatedAt": "2026-10-15T00:00:00.000Z",
            "warnings": []
          },
          "warnings": []
        },
        "x-payri-scope": [
          "refunds:write"
        ]
      }
    },
    "/v1/refund-requests/{id}/hold": {
      "post": {
        "description": "추가 확인이 필요한 환불 요청을 보류해요. Idempotency-Key는 24시간 보관해요.",
        "operationId": "RefundRequestsV1Controller_hold",
        "parameters": [
          {
            "description": "보류할 환불 요청 식별자예요.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "rfr_123",
              "type": "string"
            }
          },
          {
            "description": "POST 요청의 중복 실행을 막는 키예요. 24시간 보관하며 본문이 다른 요청에는 같은 키를 쓸 수 없어요.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "검토 중 보류",
                  "value": {
                    "reason": "구매 내역을 확인 중이에요.",
                    "type": "in_review"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1RefundRequestHoldDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "refundRequest": {
                    "approvedAmount": null,
                    "autoApproveAt": "2026-10-18T00:00:00.000Z",
                    "breakdown": {
                      "orders": [
                        {
                          "amount": 10000,
                          "currency": "KRW",
                          "orderId": "ord_123"
                        }
                      ]
                    },
                    "buyerEmail": "buyer@example.com",
                    "buyerReason": "상품을 환불하고 싶어요.",
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "currency": "KRW",
                    "decidedAt": null,
                    "decidedBy": null,
                    "hold": null,
                    "id": "rfr_123",
                    "kind": "order",
                    "mode": "test",
                    "orderId": "ord_123",
                    "productId": "prod_123",
                    "refundableAmount": 10000,
                    "rejectReason": null,
                    "requestedAmount": 10000,
                    "status": "requested",
                    "statutory": true,
                    "subscriptionId": null,
                    "updatedAt": "2026-10-15T00:00:00.000Z",
                    "warnings": []
                  },
                  "warnings": []
                },
                "schema": {
                  "$ref": "#/components/schemas/V1RefundActionResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "환불 요청 보류",
        "tags": [
          "환불 요청"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "VALIDATION_FAILED",
          "REFUND_REQUEST_NOT_FOUND",
          "REFUND_REQUEST_NOT_ACTIONABLE",
          "REFUND_STATUTORY_LOCKED",
          "REFUND_REASON_REQUIRED",
          "IDEMPOTENCY_KEY_CONFLICT",
          "IDEMPOTENCY_IN_PROGRESS",
          "INTERNAL_ERROR"
        ],
        "x-payri-idempotent": true,
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "body": {
            "reason": "구매 내역을 확인 중이에요.",
            "type": "in_review"
          },
          "headers": {
            "Idempotency-Key": "hold-rfr-123-001"
          },
          "method": "POST",
          "url": "https://api.payri.kr/v1/refund-requests/rfr_123/hold"
        },
        "x-payri-response-example": {
          "refundRequest": {
            "approvedAmount": null,
            "autoApproveAt": "2026-10-18T00:00:00.000Z",
            "breakdown": {
              "orders": [
                {
                  "amount": 10000,
                  "currency": "KRW",
                  "orderId": "ord_123"
                }
              ]
            },
            "buyerEmail": "buyer@example.com",
            "buyerReason": "상품을 환불하고 싶어요.",
            "createdAt": "2026-10-15T00:00:00.000Z",
            "currency": "KRW",
            "decidedAt": null,
            "decidedBy": null,
            "hold": null,
            "id": "rfr_123",
            "kind": "order",
            "mode": "test",
            "orderId": "ord_123",
            "productId": "prod_123",
            "refundableAmount": 10000,
            "rejectReason": null,
            "requestedAmount": 10000,
            "status": "requested",
            "statutory": true,
            "subscriptionId": null,
            "updatedAt": "2026-10-15T00:00:00.000Z",
            "warnings": []
          },
          "warnings": []
        },
        "x-payri-scope": [
          "refunds:write"
        ]
      }
    },
    "/v1/refund-requests/{id}/reject": {
      "post": {
        "description": "환불 요청을 거절하고 사유를 기록해요. Idempotency-Key는 24시간 보관해요.",
        "operationId": "RefundRequestsV1Controller_reject",
        "parameters": [
          {
            "description": "거절할 환불 요청 식별자예요.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "rfr_123",
              "type": "string"
            }
          },
          {
            "description": "POST 요청의 중복 실행을 막는 키예요. 24시간 보관하며 본문이 다른 요청에는 같은 키를 쓸 수 없어요.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "거절 사유",
                  "value": {
                    "reason": "환불 정책상 처리할 수 없어요."
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1RefundRequestRejectDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "refundRequest": {
                    "approvedAmount": null,
                    "autoApproveAt": "2026-10-18T00:00:00.000Z",
                    "breakdown": {
                      "orders": [
                        {
                          "amount": 10000,
                          "currency": "KRW",
                          "orderId": "ord_123"
                        }
                      ]
                    },
                    "buyerEmail": "buyer@example.com",
                    "buyerReason": "상품을 환불하고 싶어요.",
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "currency": "KRW",
                    "decidedAt": null,
                    "decidedBy": null,
                    "hold": null,
                    "id": "rfr_123",
                    "kind": "order",
                    "mode": "test",
                    "orderId": "ord_123",
                    "productId": "prod_123",
                    "refundableAmount": 10000,
                    "rejectReason": null,
                    "requestedAmount": 10000,
                    "status": "requested",
                    "statutory": true,
                    "subscriptionId": null,
                    "updatedAt": "2026-10-15T00:00:00.000Z",
                    "warnings": []
                  },
                  "warnings": []
                },
                "schema": {
                  "$ref": "#/components/schemas/V1RefundActionResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "환불 요청 거절",
        "tags": [
          "환불 요청"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "VALIDATION_FAILED",
          "REFUND_REQUEST_NOT_FOUND",
          "REFUND_REQUEST_NOT_ACTIONABLE",
          "REFUND_STATUTORY_LOCKED",
          "REFUND_REASON_REQUIRED",
          "IDEMPOTENCY_KEY_CONFLICT",
          "IDEMPOTENCY_IN_PROGRESS",
          "INTERNAL_ERROR"
        ],
        "x-payri-idempotent": true,
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "body": {
            "reason": "환불 정책상 처리할 수 없어요."
          },
          "headers": {
            "Idempotency-Key": "reject-rfr-123-001"
          },
          "method": "POST",
          "url": "https://api.payri.kr/v1/refund-requests/rfr_123/reject"
        },
        "x-payri-response-example": {
          "refundRequest": {
            "approvedAmount": null,
            "autoApproveAt": "2026-10-18T00:00:00.000Z",
            "breakdown": {
              "orders": [
                {
                  "amount": 10000,
                  "currency": "KRW",
                  "orderId": "ord_123"
                }
              ]
            },
            "buyerEmail": "buyer@example.com",
            "buyerReason": "상품을 환불하고 싶어요.",
            "createdAt": "2026-10-15T00:00:00.000Z",
            "currency": "KRW",
            "decidedAt": null,
            "decidedBy": null,
            "hold": null,
            "id": "rfr_123",
            "kind": "order",
            "mode": "test",
            "orderId": "ord_123",
            "productId": "prod_123",
            "refundableAmount": 10000,
            "rejectReason": null,
            "requestedAmount": 10000,
            "status": "requested",
            "statutory": true,
            "subscriptionId": null,
            "updatedAt": "2026-10-15T00:00:00.000Z",
            "warnings": []
          },
          "warnings": []
        },
        "x-payri-scope": [
          "refunds:write"
        ]
      }
    },
    "/v1/subscriptions": {
      "get": {
        "description": "API 키의 test 또는 live 환경과 상품 범위에 속한 구독을 페이지 단위로 조회해요.",
        "operationId": "SubscriptionsV1Controller_list",
        "parameters": [
          {
            "description": "조회할 페이지 번호예요.",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "number"
            }
          },
          {
            "description": "페이지마다 조회할 항목 수예요.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "example": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "number"
            }
          },
          {
            "description": "구매자 이메일로 구독을 찾아요.",
            "in": "query",
            "name": "buyerEmail",
            "required": false,
            "schema": {
              "example": "buyer@example.com",
              "format": "email",
              "type": "string"
            }
          },
          {
            "description": "구독의 공개 상태로 결과를 좁혀요. 결제가 밀린 채 해지를 예약한 구독도 past_due 로 조회돼요.",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "enum": [
                "active",
                "past_due",
                "ended"
              ],
              "example": "active",
              "type": "string"
            }
          },
          {
            "description": "상품 식별자로 구독을 좁혀요.",
            "in": "query",
            "name": "productId",
            "required": false,
            "schema": {
              "example": "prod_123",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "amount": 9900,
                      "billingInterval": "monthly",
                      "buyerEmail": "buyer@example.com",
                      "buyerName": "홍길동",
                      "cancelAtPeriodEnd": false,
                      "currency": "KRW",
                      "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
                      "currentPeriodStart": "2026-10-01T00:00:00.000Z",
                      "endReason": null,
                      "endedAt": null,
                      "history": [],
                      "id": "sub_123",
                      "mode": "test",
                      "pastDueSince": null,
                      "pendingPriceChange": null,
                      "planId": "plan_basic",
                      "productId": "prod_123",
                      "productName": "멤버십",
                      "refundEligibleUntil": "2026-10-08T00:00:00.000Z",
                      "refundTerms": {
                        "dayRounding": "ceil",
                        "initialFullRefundDays": 7,
                        "listAmounts": null,
                        "penalty": null,
                        "policyType": "within_7days",
                        "renewalFullRefundDays": 7,
                        "requireApproval": false,
                        "restrictRepeatFullRefund": false,
                        "yearlyDiscountClawback": false
                      },
                      "scheduledChange": null,
                      "startedAt": "2026-10-01T00:00:00.000Z",
                      "status": "active",
                      "testRenewalOutcome": "succeed"
                    }
                  ],
                  "limit": 20,
                  "page": 1,
                  "total": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/V1SubscriptionPageResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "구독 목록 조회",
        "tags": [
          "구독"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "VALIDATION_FAILED",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "headers": {
            "Authorization": "Bearer <API key>"
          },
          "method": "GET",
          "url": "https://api.payri.kr/v1/subscriptions?page=1&limit=20&status=active"
        },
        "x-payri-response-example": {
          "data": [
            {
              "amount": 9900,
              "billingInterval": "monthly",
              "buyerEmail": "buyer@example.com",
              "buyerName": "홍길동",
              "cancelAtPeriodEnd": false,
              "currency": "KRW",
              "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
              "currentPeriodStart": "2026-10-01T00:00:00.000Z",
              "endReason": null,
              "endedAt": null,
              "history": [],
              "id": "sub_123",
              "mode": "test",
              "pastDueSince": null,
              "pendingPriceChange": null,
              "planId": "plan_basic",
              "productId": "prod_123",
              "productName": "멤버십",
              "refundEligibleUntil": "2026-10-08T00:00:00.000Z",
              "refundTerms": {
                "dayRounding": "ceil",
                "initialFullRefundDays": 7,
                "listAmounts": null,
                "penalty": null,
                "policyType": "within_7days",
                "renewalFullRefundDays": 7,
                "requireApproval": false,
                "restrictRepeatFullRefund": false,
                "yearlyDiscountClawback": false
              },
              "scheduledChange": null,
              "startedAt": "2026-10-01T00:00:00.000Z",
              "status": "active",
              "testRenewalOutcome": "succeed"
            }
          ],
          "limit": 20,
          "page": 1,
          "total": 1
        },
        "x-payri-scope": [
          "subscriptions:read"
        ]
      }
    },
    "/v1/subscriptions/{id}": {
      "get": {
        "description": "구독의 공개 상태, 청구 기간과 예약 변경 정보를 조회해요.",
        "operationId": "SubscriptionsV1Controller_get",
        "parameters": [
          {
            "description": "조회할 구독 식별자예요.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "sub_123",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "amount": 9900,
                  "billingInterval": "monthly",
                  "buyerEmail": "buyer@example.com",
                  "buyerName": "홍길동",
                  "cancelAtPeriodEnd": false,
                  "currency": "KRW",
                  "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
                  "currentPeriodStart": "2026-10-01T00:00:00.000Z",
                  "endReason": null,
                  "endedAt": null,
                  "history": [],
                  "id": "sub_123",
                  "mode": "test",
                  "pastDueSince": null,
                  "pendingPriceChange": null,
                  "planId": "plan_basic",
                  "productId": "prod_123",
                  "productName": "멤버십",
                  "refundEligibleUntil": "2026-10-08T00:00:00.000Z",
                  "refundTerms": {
                    "dayRounding": "ceil",
                    "initialFullRefundDays": 7,
                    "listAmounts": null,
                    "penalty": null,
                    "policyType": "within_7days",
                    "renewalFullRefundDays": 7,
                    "requireApproval": false,
                    "restrictRepeatFullRefund": false,
                    "yearlyDiscountClawback": false
                  },
                  "scheduledChange": null,
                  "startedAt": "2026-10-01T00:00:00.000Z",
                  "status": "active",
                  "testRenewalOutcome": "succeed"
                },
                "schema": {
                  "$ref": "#/components/schemas/V1SubscriptionResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "구독 상세 조회",
        "tags": [
          "구독"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "SUBSCRIPTION_NOT_FOUND",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "headers": {
            "Authorization": "Bearer <API key>"
          },
          "method": "GET",
          "url": "https://api.payri.kr/v1/subscriptions/sub_123"
        },
        "x-payri-response-example": {
          "amount": 9900,
          "billingInterval": "monthly",
          "buyerEmail": "buyer@example.com",
          "buyerName": "홍길동",
          "cancelAtPeriodEnd": false,
          "currency": "KRW",
          "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
          "currentPeriodStart": "2026-10-01T00:00:00.000Z",
          "endReason": null,
          "endedAt": null,
          "history": [],
          "id": "sub_123",
          "mode": "test",
          "pastDueSince": null,
          "pendingPriceChange": null,
          "planId": "plan_basic",
          "productId": "prod_123",
          "productName": "멤버십",
          "refundEligibleUntil": "2026-10-08T00:00:00.000Z",
          "refundTerms": {
            "dayRounding": "ceil",
            "initialFullRefundDays": 7,
            "listAmounts": null,
            "penalty": null,
            "policyType": "within_7days",
            "renewalFullRefundDays": 7,
            "requireApproval": false,
            "restrictRepeatFullRefund": false,
            "yearlyDiscountClawback": false
          },
          "scheduledChange": null,
          "startedAt": "2026-10-01T00:00:00.000Z",
          "status": "active",
          "testRenewalOutcome": "succeed"
        },
        "x-payri-scope": [
          "subscriptions:read"
        ]
      }
    },
    "/v1/subscriptions/{id}/cancel": {
      "post": {
        "description": "기간 종료 시점 또는 즉시 해지해요. timing은 필수예요. Idempotency-Key는 24시간 보관해요.",
        "operationId": "SubscriptionsV1Controller_cancel",
        "parameters": [
          {
            "description": "해지할 구독 식별자예요.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "sub_123",
              "type": "string"
            }
          },
          {
            "description": "POST 요청의 중복 실행을 막는 키예요. 24시간 보관하며 본문이 다른 요청에는 같은 키를 쓸 수 없어요.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "기간 종료 시 해지",
                  "value": {
                    "reason": "이용 종료",
                    "timing": "period_end"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1CancelSubscriptionDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "currency": "KRW",
                  "refundAmount": 0,
                  "refundOrderId": null,
                  "refundedOrderIds": [],
                  "subscription": {
                    "amount": 9900,
                    "billingInterval": "monthly",
                    "buyerEmail": "buyer@example.com",
                    "buyerName": "홍길동",
                    "cancelAtPeriodEnd": true,
                    "currency": "KRW",
                    "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
                    "currentPeriodStart": "2026-10-01T00:00:00.000Z",
                    "endReason": null,
                    "endedAt": null,
                    "history": [],
                    "id": "sub_123",
                    "mode": "test",
                    "pastDueSince": null,
                    "pendingPriceChange": null,
                    "planId": "plan_basic",
                    "productId": "prod_123",
                    "productName": "멤버십",
                    "refundEligibleUntil": "2026-10-08T00:00:00.000Z",
                    "refundTerms": {
                      "dayRounding": "ceil",
                      "initialFullRefundDays": 7,
                      "listAmounts": null,
                      "penalty": null,
                      "policyType": "within_7days",
                      "renewalFullRefundDays": 7,
                      "requireApproval": false,
                      "restrictRepeatFullRefund": false,
                      "yearlyDiscountClawback": false
                    },
                    "scheduledChange": {
                      "billingInterval": null,
                      "effectiveAt": "2026-11-01T00:00:00.000Z",
                      "planId": null,
                      "type": "cancel"
                    },
                    "startedAt": "2026-10-01T00:00:00.000Z",
                    "status": "active",
                    "testRenewalOutcome": "succeed"
                  },
                  "timing": "period_end"
                },
                "schema": {
                  "$ref": "#/components/schemas/V1CancelResultResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "구독 해지",
        "tags": [
          "구독"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "VALIDATION_FAILED",
          "SUBSCRIPTION_NOT_FOUND",
          "REFUND_REQUEST_PENDING",
          "REFUND_IN_PROGRESS",
          "SUBSCRIPTION_REFUND_SETTLED",
          "SUBSCRIPTION_REFUND_NONE_LEFT",
          "ORDER_NOT_FOUND",
          "PG_REFUND_FAILED",
          "PG_REFUND_AMBIGUOUS",
          "ALREADY_ENDED",
          "ALREADY_CANCELED",
          "IDEMPOTENCY_KEY_CONFLICT",
          "IDEMPOTENCY_IN_PROGRESS",
          "INTERNAL_ERROR"
        ],
        "x-payri-idempotent": true,
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "body": {
            "reason": "이용 종료",
            "timing": "period_end"
          },
          "headers": {
            "Idempotency-Key": "cancel-sub-123-001"
          },
          "method": "POST",
          "url": "https://api.payri.kr/v1/subscriptions/sub_123/cancel"
        },
        "x-payri-response-example": {
          "currency": "KRW",
          "refundAmount": 0,
          "refundOrderId": null,
          "refundedOrderIds": [],
          "subscription": {
            "amount": 9900,
            "billingInterval": "monthly",
            "buyerEmail": "buyer@example.com",
            "buyerName": "홍길동",
            "cancelAtPeriodEnd": true,
            "currency": "KRW",
            "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
            "currentPeriodStart": "2026-10-01T00:00:00.000Z",
            "endReason": null,
            "endedAt": null,
            "history": [],
            "id": "sub_123",
            "mode": "test",
            "pastDueSince": null,
            "pendingPriceChange": null,
            "planId": "plan_basic",
            "productId": "prod_123",
            "productName": "멤버십",
            "refundEligibleUntil": "2026-10-08T00:00:00.000Z",
            "refundTerms": {
              "dayRounding": "ceil",
              "initialFullRefundDays": 7,
              "listAmounts": null,
              "penalty": null,
              "policyType": "within_7days",
              "renewalFullRefundDays": 7,
              "requireApproval": false,
              "restrictRepeatFullRefund": false,
              "yearlyDiscountClawback": false
            },
            "scheduledChange": {
              "billingInterval": null,
              "effectiveAt": "2026-11-01T00:00:00.000Z",
              "planId": null,
              "type": "cancel"
            },
            "startedAt": "2026-10-01T00:00:00.000Z",
            "status": "active",
            "testRenewalOutcome": "succeed"
          },
          "timing": "period_end"
        },
        "x-payri-scope": [
          "subscriptions:write"
        ]
      }
    },
    "/v1/subscriptions/{id}/cancel-preview": {
      "get": {
        "description": "기간 종료 해지와 즉시 해지의 적용 시점과 환불 금액을 비교해요.",
        "operationId": "SubscriptionsV1Controller_previewCancel",
        "parameters": [
          {
            "description": "미리보기할 구독 식별자예요.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "sub_123",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "immediate": {
                    "blockedReason": null,
                    "breakdown": [
                      {
                        "amount": 5000,
                        "currency": "KRW",
                        "orderId": "ord_123"
                      }
                    ],
                    "clawback": 0,
                    "clawbackCurrency": "KRW",
                    "currency": "KRW",
                    "deduction": 0,
                    "deductionCap": 0,
                    "deductionCapCurrency": "KRW",
                    "deductionCurrency": "KRW",
                    "fullRefund": false,
                    "penalty": 0,
                    "penaltyCurrency": "KRW",
                    "reason": "prorated",
                    "refundAmount": 5000,
                    "requiresApproval": false,
                    "statutory": false,
                    "totalDays": 30,
                    "usedDays": 5
                  },
                  "mode": "test",
                  "pendingRefundRequest": null,
                  "periodEnd": {
                    "effectiveAt": "2026-11-01T00:00:00.000Z"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/V1CancelPreviewResponse"
                }
              }
            },
            "description": "요청 결과를 반환해요."
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_FAILED",
                  "details": {
                    "fields": [
                      {
                        "field": "productId",
                        "message": "필수 값이에요."
                      }
                    ]
                  },
                  "message": "요청 필드를 확인해 주세요.",
                  "statusCode": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청 값이 올바르지 않아요."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_INVALID",
                  "message": "API 키가 없거나 올바르지 않아요.",
                  "statusCode": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "API 키가 없거나 올바르지 않아요."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "API_KEY_SCOPE_MISSING",
                  "details": {
                    "required": [
                      "orders:read"
                    ]
                  },
                  "message": "요청에 필요한 권한이 없어요.",
                  "statusCode": 403
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "키의 IP 주소가 허용되지 않거나 필요한 권한이 없어요."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": "요청한 리소스를 찾을 수 없어요.",
                  "statusCode": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청한 리소스를 찾을 수 없어요."
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "IDEMPOTENCY_KEY_CONFLICT",
                  "message": "같은 Idempotency-Key 를 다른 요청에 사용할 수 없어요.",
                  "statusCode": 409
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "현재 상태에서 처리할 수 없거나 멱등키 충돌이 있어요."
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "RATE_LIMITED",
                  "message": "잠시 후 다시 시도해 주세요.",
                  "statusCode": 429
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "호출 한도를 초과했어요."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_ERROR",
                  "message": "요청을 처리하는 중 오류가 발생했어요.",
                  "statusCode": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "description": "요청을 처리하는 중 오류가 발생했어요."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "구독 해지 미리보기",
        "tags": [
          "구독"
        ],
        "x-payri-error-codes": [
          "API_KEY_INVALID",
          "API_KEY_IP_NOT_ALLOWED",
          "API_KEY_SCOPE_MISSING",
          "RATE_LIMITED",
          "SUBSCRIPTION_NOT_FOUND",
          "INTERNAL_ERROR"
        ],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "headers": {
            "Authorization": "Bearer <API key>"
          },
          "method": "GET",
          "url": "https://api.payri.kr/v1/subscriptions/sub_123/cancel-preview"
        },
        "x-payri-response-example": {
          "immediate": {
            "blockedReason": null,
            "breakdown": [
              {
                "amount": 5000,
                "currency": "KRW",
                "orderId": "ord_123"
              }
            ],
            "clawback": 0,
            "clawbackCurrency": "KRW",
            "currency": "KRW",
            "deduction": 0,
            "deductionCap": 0,
            "deductionCapCurrency": "KRW",
            "deductionCurrency": "KRW",
            "fullRefund": false,
            "penalty": 0,
            "penaltyCurrency": "KRW",
            "reason": "prorated",
            "refundAmount": 5000,
            "requiresApproval": false,
            "statutory": false,
            "totalDays": 30,
            "usedDays": 5
          },
          "mode": "test",
          "pendingRefundRequest": null,
          "periodEnd": {
            "effectiveAt": "2026-11-01T00:00:00.000Z"
          }
        },
        "x-payri-scope": [
          "subscriptions:read"
        ]
      }
    }
  },
  "servers": [
    {
      "description": "운영 서버예요.",
      "url": "https://api.payri.kr"
    }
  ],
  "tags": [],
  "webhooks": {
    "license.issue": {
      "post": {
        "description": "판매자가 발급한 라이선스 키가 필요한 상품의 결제 뒤 동기 발급을 요청해요. 응답 처리가 5회 모두 실패하면 라이선스 상태를 failed로 바꿔요. 본문과 data의 mode는 test 또는 live예요.",
        "operationId": "webhook_license_issue",
        "parameters": [
          {
            "description": "이벤트 ID예요. 본문 id와 같고 재시도에도 유지돼요.",
            "in": "header",
            "name": "webhook-id",
            "required": true,
            "schema": {
              "example": "evt_123",
              "type": "string"
            }
          },
          {
            "description": "서명 시각을 초 단위 문자열로 전달해요.",
            "in": "header",
            "name": "webhook-timestamp",
            "required": true,
            "schema": {
              "example": "1792022400",
              "type": "string"
            }
          },
          {
            "description": "HMAC-SHA256 v1 서명 목록이에요.",
            "in": "header",
            "name": "webhook-signature",
            "required": true,
            "schema": {
              "example": "v1,base64...",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "license.issue 이벤트 예시",
                  "value": {
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "data": {
                      "buyer": {
                        "email": "buyer@example.com",
                        "name": "홍길동"
                      },
                      "expiresAt": null,
                      "interval": null,
                      "licenseId": "lic_123",
                      "maxActivations": null,
                      "mode": "test",
                      "orderId": "ord_123",
                      "planId": null,
                      "productId": "prod_123"
                    },
                    "id": "evt_123",
                    "mode": "test",
                    "type": "license.issue"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1WebhookEnvelope_license_issue"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "expiresAt": "2027-10-15T00:00:00.000Z",
                  "key": "VENDOR-12345-ABCDE",
                  "maxActivations": 3
                },
                "schema": {
                  "$ref": "#/components/schemas/V1LicenseIssueResponse"
                }
              }
            },
            "description": "라이선스 키를 동기 응답으로 전달해요."
          }
        },
        "security": [],
        "summary": "license.issue",
        "x-payri-error-codes": [],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "createdAt": "2026-10-15T00:00:00.000Z",
          "data": {
            "buyer": {
              "email": "buyer@example.com",
              "name": "홍길동"
            },
            "expiresAt": null,
            "interval": null,
            "licenseId": "lic_123",
            "maxActivations": null,
            "mode": "test",
            "orderId": "ord_123",
            "planId": null,
            "productId": "prod_123"
          },
          "id": "evt_123",
          "mode": "test",
          "type": "license.issue"
        },
        "x-payri-response-example": {
          "expiresAt": "2027-10-15T00:00:00.000Z",
          "key": "VENDOR-12345-ABCDE",
          "maxActivations": 3
        },
        "x-payri-scope": []
      }
    },
    "order.paid": {
      "post": {
        "description": "최초 결제, 갱신 또는 차액 결제가 완료되면 보내요. 본문과 data의 mode는 test 또는 live예요.",
        "operationId": "webhook_order_paid",
        "parameters": [
          {
            "description": "이벤트 ID예요. 본문 id와 같고 재시도에도 유지돼요.",
            "in": "header",
            "name": "webhook-id",
            "required": true,
            "schema": {
              "example": "evt_123",
              "type": "string"
            }
          },
          {
            "description": "서명 시각을 초 단위 문자열로 전달해요.",
            "in": "header",
            "name": "webhook-timestamp",
            "required": true,
            "schema": {
              "example": "1792022400",
              "type": "string"
            }
          },
          {
            "description": "HMAC-SHA256 v1 서명 목록이에요.",
            "in": "header",
            "name": "webhook-signature",
            "required": true,
            "schema": {
              "example": "v1,base64...",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "order.paid 이벤트 예시",
                  "value": {
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "data": {
                      "amount": 10000,
                      "billingInterval": null,
                      "buyerEmail": "buyer@example.com",
                      "buyerName": "홍길동",
                      "buyerPhone": "01012345678",
                      "checkoutSessionId": "cs_123",
                      "confirmedAt": null,
                      "coupon": null,
                      "createdAt": "2026-10-15T00:00:00.000Z",
                      "currency": "KRW",
                      "id": "ord_123",
                      "metadata": {
                        "campaign": "fall"
                      },
                      "method": "card",
                      "mode": "test",
                      "paidAt": "2026-10-15T00:01:00.000Z",
                      "periodEnd": null,
                      "periodStart": null,
                      "planId": null,
                      "productId": "prod_123",
                      "productName": "디지털 가이드",
                      "refundedAmount": 0,
                      "refundedAt": null,
                      "refunds": [],
                      "status": "paid",
                      "subscriptionEvent": null,
                      "subscriptionId": null,
                      "withdrawalNoticeAgreedAt": "2026-10-15T00:00:00.000Z"
                    },
                    "id": "evt_123",
                    "mode": "test",
                    "type": "order.paid"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1WebhookEnvelope_order_paid"
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "content": {
              "application/json": {
                "example": {
                  "received": true
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "이벤트를 수신했어요."
          }
        },
        "security": [],
        "summary": "order.paid",
        "x-payri-error-codes": [],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "createdAt": "2026-10-15T00:00:00.000Z",
          "data": {
            "amount": 10000,
            "billingInterval": null,
            "buyerEmail": "buyer@example.com",
            "buyerName": "홍길동",
            "buyerPhone": "01012345678",
            "checkoutSessionId": "cs_123",
            "confirmedAt": null,
            "coupon": null,
            "createdAt": "2026-10-15T00:00:00.000Z",
            "currency": "KRW",
            "id": "ord_123",
            "metadata": {
              "campaign": "fall"
            },
            "method": "card",
            "mode": "test",
            "paidAt": "2026-10-15T00:01:00.000Z",
            "periodEnd": null,
            "periodStart": null,
            "planId": null,
            "productId": "prod_123",
            "productName": "디지털 가이드",
            "refundedAmount": 0,
            "refundedAt": null,
            "refunds": [],
            "status": "paid",
            "subscriptionEvent": null,
            "subscriptionId": null,
            "withdrawalNoticeAgreedAt": "2026-10-15T00:00:00.000Z"
          },
          "id": "evt_123",
          "mode": "test",
          "type": "order.paid"
        },
        "x-payri-response-example": {
          "received": true
        },
        "x-payri-scope": []
      }
    },
    "order.refunded": {
      "post": {
        "description": "부분 또는 전체 환불이 확정되면 보내요. 본문과 data의 mode는 test 또는 live예요.",
        "operationId": "webhook_order_refunded",
        "parameters": [
          {
            "description": "이벤트 ID예요. 본문 id와 같고 재시도에도 유지돼요.",
            "in": "header",
            "name": "webhook-id",
            "required": true,
            "schema": {
              "example": "evt_123",
              "type": "string"
            }
          },
          {
            "description": "서명 시각을 초 단위 문자열로 전달해요.",
            "in": "header",
            "name": "webhook-timestamp",
            "required": true,
            "schema": {
              "example": "1792022400",
              "type": "string"
            }
          },
          {
            "description": "HMAC-SHA256 v1 서명 목록이에요.",
            "in": "header",
            "name": "webhook-signature",
            "required": true,
            "schema": {
              "example": "v1,base64...",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "order.refunded 이벤트 예시",
                  "value": {
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "data": {
                      "amount": 10000,
                      "billingInterval": null,
                      "buyerEmail": "buyer@example.com",
                      "buyerName": "홍길동",
                      "buyerPhone": "01012345678",
                      "checkoutSessionId": "cs_123",
                      "confirmedAt": null,
                      "coupon": null,
                      "createdAt": "2026-10-15T00:00:00.000Z",
                      "currency": "KRW",
                      "id": "ord_123",
                      "metadata": {
                        "campaign": "fall"
                      },
                      "method": "card",
                      "mode": "test",
                      "paidAt": "2026-10-15T00:01:00.000Z",
                      "periodEnd": null,
                      "periodStart": null,
                      "planId": null,
                      "productId": "prod_123",
                      "productName": "디지털 가이드",
                      "refund": {
                        "amount": 5000,
                        "currency": "KRW",
                        "id": "ref_123",
                        "reason": "고객 요청",
                        "refundedAt": "2026-10-15T00:02:00.000Z"
                      },
                      "refundedAmount": 5000,
                      "refundedAt": null,
                      "refunds": [
                        {
                          "amount": 5000,
                          "at": "2026-10-15T00:02:00.000Z",
                          "currency": "KRW",
                          "reason": "고객 요청"
                        }
                      ],
                      "status": "paid",
                      "subscriptionEvent": null,
                      "subscriptionId": null,
                      "withdrawalNoticeAgreedAt": "2026-10-15T00:00:00.000Z"
                    },
                    "id": "evt_123",
                    "mode": "test",
                    "type": "order.refunded"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1WebhookEnvelope_order_refunded"
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "content": {
              "application/json": {
                "example": {
                  "received": true
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "이벤트를 수신했어요."
          }
        },
        "security": [],
        "summary": "order.refunded",
        "x-payri-error-codes": [],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "createdAt": "2026-10-15T00:00:00.000Z",
          "data": {
            "amount": 10000,
            "billingInterval": null,
            "buyerEmail": "buyer@example.com",
            "buyerName": "홍길동",
            "buyerPhone": "01012345678",
            "checkoutSessionId": "cs_123",
            "confirmedAt": null,
            "coupon": null,
            "createdAt": "2026-10-15T00:00:00.000Z",
            "currency": "KRW",
            "id": "ord_123",
            "metadata": {
              "campaign": "fall"
            },
            "method": "card",
            "mode": "test",
            "paidAt": "2026-10-15T00:01:00.000Z",
            "periodEnd": null,
            "periodStart": null,
            "planId": null,
            "productId": "prod_123",
            "productName": "디지털 가이드",
            "refund": {
              "amount": 5000,
              "currency": "KRW",
              "id": "ref_123",
              "reason": "고객 요청",
              "refundedAt": "2026-10-15T00:02:00.000Z"
            },
            "refundedAmount": 5000,
            "refundedAt": null,
            "refunds": [
              {
                "amount": 5000,
                "at": "2026-10-15T00:02:00.000Z",
                "currency": "KRW",
                "reason": "고객 요청"
              }
            ],
            "status": "paid",
            "subscriptionEvent": null,
            "subscriptionId": null,
            "withdrawalNoticeAgreedAt": "2026-10-15T00:00:00.000Z"
          },
          "id": "evt_123",
          "mode": "test",
          "type": "order.refunded"
        },
        "x-payri-response-example": {
          "received": true
        },
        "x-payri-scope": []
      }
    },
    "refund.approved": {
      "post": {
        "description": "환불 요청이 승인되면 보내요. 본문과 data의 mode는 test 또는 live예요.",
        "operationId": "webhook_refund_approved",
        "parameters": [
          {
            "description": "이벤트 ID예요. 본문 id와 같고 재시도에도 유지돼요.",
            "in": "header",
            "name": "webhook-id",
            "required": true,
            "schema": {
              "example": "evt_123",
              "type": "string"
            }
          },
          {
            "description": "서명 시각을 초 단위 문자열로 전달해요.",
            "in": "header",
            "name": "webhook-timestamp",
            "required": true,
            "schema": {
              "example": "1792022400",
              "type": "string"
            }
          },
          {
            "description": "HMAC-SHA256 v1 서명 목록이에요.",
            "in": "header",
            "name": "webhook-signature",
            "required": true,
            "schema": {
              "example": "v1,base64...",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "refund.approved 이벤트 예시",
                  "value": {
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "data": {
                      "approvedAmount": 10000,
                      "autoApproveAt": "2026-10-18T00:00:00.000Z",
                      "breakdown": {
                        "orders": [
                          {
                            "amount": 10000,
                            "currency": "KRW",
                            "orderId": "ord_123"
                          }
                        ]
                      },
                      "buyerEmail": "buyer@example.com",
                      "buyerReason": "상품을 환불하고 싶어요.",
                      "createdAt": "2026-10-15T00:00:00.000Z",
                      "currency": "KRW",
                      "decidedAt": "2026-10-15T00:02:00.000Z",
                      "decidedBy": "seller",
                      "hold": null,
                      "id": "rfr_123",
                      "kind": "order",
                      "mode": "test",
                      "orderId": "ord_123",
                      "productId": "prod_123",
                      "refundableAmount": 10000,
                      "rejectReason": null,
                      "requestedAmount": 10000,
                      "status": "approved",
                      "statutory": true,
                      "subscriptionId": null,
                      "updatedAt": "2026-10-15T00:02:00.000Z",
                      "warnings": []
                    },
                    "id": "evt_123",
                    "mode": "test",
                    "type": "refund.approved"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1WebhookEnvelope_refund_approved"
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "content": {
              "application/json": {
                "example": {
                  "received": true
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "이벤트를 수신했어요."
          }
        },
        "security": [],
        "summary": "refund.approved",
        "x-payri-error-codes": [],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "createdAt": "2026-10-15T00:00:00.000Z",
          "data": {
            "approvedAmount": 10000,
            "autoApproveAt": "2026-10-18T00:00:00.000Z",
            "breakdown": {
              "orders": [
                {
                  "amount": 10000,
                  "currency": "KRW",
                  "orderId": "ord_123"
                }
              ]
            },
            "buyerEmail": "buyer@example.com",
            "buyerReason": "상품을 환불하고 싶어요.",
            "createdAt": "2026-10-15T00:00:00.000Z",
            "currency": "KRW",
            "decidedAt": "2026-10-15T00:02:00.000Z",
            "decidedBy": "seller",
            "hold": null,
            "id": "rfr_123",
            "kind": "order",
            "mode": "test",
            "orderId": "ord_123",
            "productId": "prod_123",
            "refundableAmount": 10000,
            "rejectReason": null,
            "requestedAmount": 10000,
            "status": "approved",
            "statutory": true,
            "subscriptionId": null,
            "updatedAt": "2026-10-15T00:02:00.000Z",
            "warnings": []
          },
          "id": "evt_123",
          "mode": "test",
          "type": "refund.approved"
        },
        "x-payri-response-example": {
          "received": true
        },
        "x-payri-scope": []
      }
    },
    "refund.on_hold": {
      "post": {
        "description": "환불 요청 처리가 보류되면 보내요. 본문과 data의 mode는 test 또는 live예요.",
        "operationId": "webhook_refund_on_hold",
        "parameters": [
          {
            "description": "이벤트 ID예요. 본문 id와 같고 재시도에도 유지돼요.",
            "in": "header",
            "name": "webhook-id",
            "required": true,
            "schema": {
              "example": "evt_123",
              "type": "string"
            }
          },
          {
            "description": "서명 시각을 초 단위 문자열로 전달해요.",
            "in": "header",
            "name": "webhook-timestamp",
            "required": true,
            "schema": {
              "example": "1792022400",
              "type": "string"
            }
          },
          {
            "description": "HMAC-SHA256 v1 서명 목록이에요.",
            "in": "header",
            "name": "webhook-signature",
            "required": true,
            "schema": {
              "example": "v1,base64...",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "refund.on_hold 이벤트 예시",
                  "value": {
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "data": {
                      "approvedAmount": null,
                      "autoApproveAt": null,
                      "breakdown": {
                        "orders": [
                          {
                            "amount": 10000,
                            "currency": "KRW",
                            "orderId": "ord_123"
                          }
                        ]
                      },
                      "buyerEmail": "buyer@example.com",
                      "buyerReason": "상품을 환불하고 싶어요.",
                      "createdAt": "2026-10-15T00:00:00.000Z",
                      "currency": "KRW",
                      "decidedAt": "2026-10-15T00:01:00.000Z",
                      "decidedBy": "seller",
                      "hold": {
                        "heldAt": "2026-10-15T00:01:00.000Z",
                        "reason": "추가 확인 중이에요.",
                        "type": "in_review"
                      },
                      "id": "rfr_123",
                      "kind": "order",
                      "mode": "test",
                      "orderId": "ord_123",
                      "productId": "prod_123",
                      "refundableAmount": 10000,
                      "rejectReason": null,
                      "requestedAmount": 10000,
                      "status": "on_hold",
                      "statutory": true,
                      "subscriptionId": null,
                      "updatedAt": "2026-10-15T00:01:00.000Z",
                      "warnings": []
                    },
                    "id": "evt_123",
                    "mode": "test",
                    "type": "refund.on_hold"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1WebhookEnvelope_refund_on_hold"
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "content": {
              "application/json": {
                "example": {
                  "received": true
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "이벤트를 수신했어요."
          }
        },
        "security": [],
        "summary": "refund.on_hold",
        "x-payri-error-codes": [],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "createdAt": "2026-10-15T00:00:00.000Z",
          "data": {
            "approvedAmount": null,
            "autoApproveAt": null,
            "breakdown": {
              "orders": [
                {
                  "amount": 10000,
                  "currency": "KRW",
                  "orderId": "ord_123"
                }
              ]
            },
            "buyerEmail": "buyer@example.com",
            "buyerReason": "상품을 환불하고 싶어요.",
            "createdAt": "2026-10-15T00:00:00.000Z",
            "currency": "KRW",
            "decidedAt": "2026-10-15T00:01:00.000Z",
            "decidedBy": "seller",
            "hold": {
              "heldAt": "2026-10-15T00:01:00.000Z",
              "reason": "추가 확인 중이에요.",
              "type": "in_review"
            },
            "id": "rfr_123",
            "kind": "order",
            "mode": "test",
            "orderId": "ord_123",
            "productId": "prod_123",
            "refundableAmount": 10000,
            "rejectReason": null,
            "requestedAmount": 10000,
            "status": "on_hold",
            "statutory": true,
            "subscriptionId": null,
            "updatedAt": "2026-10-15T00:01:00.000Z",
            "warnings": []
          },
          "id": "evt_123",
          "mode": "test",
          "type": "refund.on_hold"
        },
        "x-payri-response-example": {
          "received": true
        },
        "x-payri-scope": []
      }
    },
    "refund.rejected": {
      "post": {
        "description": "환불 요청이 거절되면 보내요. 본문과 data의 mode는 test 또는 live예요.",
        "operationId": "webhook_refund_rejected",
        "parameters": [
          {
            "description": "이벤트 ID예요. 본문 id와 같고 재시도에도 유지돼요.",
            "in": "header",
            "name": "webhook-id",
            "required": true,
            "schema": {
              "example": "evt_123",
              "type": "string"
            }
          },
          {
            "description": "서명 시각을 초 단위 문자열로 전달해요.",
            "in": "header",
            "name": "webhook-timestamp",
            "required": true,
            "schema": {
              "example": "1792022400",
              "type": "string"
            }
          },
          {
            "description": "HMAC-SHA256 v1 서명 목록이에요.",
            "in": "header",
            "name": "webhook-signature",
            "required": true,
            "schema": {
              "example": "v1,base64...",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "refund.rejected 이벤트 예시",
                  "value": {
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "data": {
                      "approvedAmount": null,
                      "autoApproveAt": "2026-10-18T00:00:00.000Z",
                      "breakdown": {
                        "orders": [
                          {
                            "amount": 10000,
                            "currency": "KRW",
                            "orderId": "ord_123"
                          }
                        ]
                      },
                      "buyerEmail": "buyer@example.com",
                      "buyerReason": "상품을 환불하고 싶어요.",
                      "createdAt": "2026-10-15T00:00:00.000Z",
                      "currency": "KRW",
                      "decidedAt": "2026-10-15T00:02:00.000Z",
                      "decidedBy": "seller",
                      "hold": null,
                      "id": "rfr_123",
                      "kind": "order",
                      "mode": "test",
                      "orderId": "ord_123",
                      "productId": "prod_123",
                      "refundableAmount": 10000,
                      "rejectReason": "환불 정책에 따라 승인할 수 없어요.",
                      "requestedAmount": 10000,
                      "status": "rejected",
                      "statutory": true,
                      "subscriptionId": null,
                      "updatedAt": "2026-10-15T00:02:00.000Z",
                      "warnings": []
                    },
                    "id": "evt_123",
                    "mode": "test",
                    "type": "refund.rejected"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1WebhookEnvelope_refund_rejected"
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "content": {
              "application/json": {
                "example": {
                  "received": true
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "이벤트를 수신했어요."
          }
        },
        "security": [],
        "summary": "refund.rejected",
        "x-payri-error-codes": [],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "createdAt": "2026-10-15T00:00:00.000Z",
          "data": {
            "approvedAmount": null,
            "autoApproveAt": "2026-10-18T00:00:00.000Z",
            "breakdown": {
              "orders": [
                {
                  "amount": 10000,
                  "currency": "KRW",
                  "orderId": "ord_123"
                }
              ]
            },
            "buyerEmail": "buyer@example.com",
            "buyerReason": "상품을 환불하고 싶어요.",
            "createdAt": "2026-10-15T00:00:00.000Z",
            "currency": "KRW",
            "decidedAt": "2026-10-15T00:02:00.000Z",
            "decidedBy": "seller",
            "hold": null,
            "id": "rfr_123",
            "kind": "order",
            "mode": "test",
            "orderId": "ord_123",
            "productId": "prod_123",
            "refundableAmount": 10000,
            "rejectReason": "환불 정책에 따라 승인할 수 없어요.",
            "requestedAmount": 10000,
            "status": "rejected",
            "statutory": true,
            "subscriptionId": null,
            "updatedAt": "2026-10-15T00:02:00.000Z",
            "warnings": []
          },
          "id": "evt_123",
          "mode": "test",
          "type": "refund.rejected"
        },
        "x-payri-response-example": {
          "received": true
        },
        "x-payri-scope": []
      }
    },
    "refund.requested": {
      "post": {
        "description": "구매자가 환불을 요청하면 보내요. 본문과 data의 mode는 test 또는 live예요.",
        "operationId": "webhook_refund_requested",
        "parameters": [
          {
            "description": "이벤트 ID예요. 본문 id와 같고 재시도에도 유지돼요.",
            "in": "header",
            "name": "webhook-id",
            "required": true,
            "schema": {
              "example": "evt_123",
              "type": "string"
            }
          },
          {
            "description": "서명 시각을 초 단위 문자열로 전달해요.",
            "in": "header",
            "name": "webhook-timestamp",
            "required": true,
            "schema": {
              "example": "1792022400",
              "type": "string"
            }
          },
          {
            "description": "HMAC-SHA256 v1 서명 목록이에요.",
            "in": "header",
            "name": "webhook-signature",
            "required": true,
            "schema": {
              "example": "v1,base64...",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "refund.requested 이벤트 예시",
                  "value": {
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "data": {
                      "approvedAmount": null,
                      "autoApproveAt": "2026-10-18T00:00:00.000Z",
                      "breakdown": {
                        "orders": [
                          {
                            "amount": 10000,
                            "currency": "KRW",
                            "orderId": "ord_123"
                          }
                        ]
                      },
                      "buyerEmail": "buyer@example.com",
                      "buyerReason": "상품을 환불하고 싶어요.",
                      "createdAt": "2026-10-15T00:00:00.000Z",
                      "currency": "KRW",
                      "decidedAt": null,
                      "decidedBy": null,
                      "hold": null,
                      "id": "rfr_123",
                      "kind": "order",
                      "mode": "test",
                      "orderId": "ord_123",
                      "productId": "prod_123",
                      "refundableAmount": 10000,
                      "rejectReason": null,
                      "requestedAmount": 10000,
                      "status": "requested",
                      "statutory": true,
                      "subscriptionId": null,
                      "updatedAt": "2026-10-15T00:00:00.000Z",
                      "warnings": []
                    },
                    "id": "evt_123",
                    "mode": "test",
                    "type": "refund.requested"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1WebhookEnvelope_refund_requested"
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "content": {
              "application/json": {
                "example": {
                  "received": true
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "이벤트를 수신했어요."
          }
        },
        "security": [],
        "summary": "refund.requested",
        "x-payri-error-codes": [],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "createdAt": "2026-10-15T00:00:00.000Z",
          "data": {
            "approvedAmount": null,
            "autoApproveAt": "2026-10-18T00:00:00.000Z",
            "breakdown": {
              "orders": [
                {
                  "amount": 10000,
                  "currency": "KRW",
                  "orderId": "ord_123"
                }
              ]
            },
            "buyerEmail": "buyer@example.com",
            "buyerReason": "상품을 환불하고 싶어요.",
            "createdAt": "2026-10-15T00:00:00.000Z",
            "currency": "KRW",
            "decidedAt": null,
            "decidedBy": null,
            "hold": null,
            "id": "rfr_123",
            "kind": "order",
            "mode": "test",
            "orderId": "ord_123",
            "productId": "prod_123",
            "refundableAmount": 10000,
            "rejectReason": null,
            "requestedAmount": 10000,
            "status": "requested",
            "statutory": true,
            "subscriptionId": null,
            "updatedAt": "2026-10-15T00:00:00.000Z",
            "warnings": []
          },
          "id": "evt_123",
          "mode": "test",
          "type": "refund.requested"
        },
        "x-payri-response-example": {
          "received": true
        },
        "x-payri-scope": []
      }
    },
    "refund.withdrawn": {
      "post": {
        "description": "구매자가 환불 요청을 철회하면 보내요. 본문과 data의 mode는 test 또는 live예요.",
        "operationId": "webhook_refund_withdrawn",
        "parameters": [
          {
            "description": "이벤트 ID예요. 본문 id와 같고 재시도에도 유지돼요.",
            "in": "header",
            "name": "webhook-id",
            "required": true,
            "schema": {
              "example": "evt_123",
              "type": "string"
            }
          },
          {
            "description": "서명 시각을 초 단위 문자열로 전달해요.",
            "in": "header",
            "name": "webhook-timestamp",
            "required": true,
            "schema": {
              "example": "1792022400",
              "type": "string"
            }
          },
          {
            "description": "HMAC-SHA256 v1 서명 목록이에요.",
            "in": "header",
            "name": "webhook-signature",
            "required": true,
            "schema": {
              "example": "v1,base64...",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "refund.withdrawn 이벤트 예시",
                  "value": {
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "data": {
                      "approvedAmount": null,
                      "autoApproveAt": "2026-10-18T00:00:00.000Z",
                      "breakdown": {
                        "orders": [
                          {
                            "amount": 10000,
                            "currency": "KRW",
                            "orderId": "ord_123"
                          }
                        ]
                      },
                      "buyerEmail": "buyer@example.com",
                      "buyerReason": "상품을 환불하고 싶어요.",
                      "createdAt": "2026-10-15T00:00:00.000Z",
                      "currency": "KRW",
                      "decidedAt": "2026-10-15T00:02:00.000Z",
                      "decidedBy": null,
                      "hold": null,
                      "id": "rfr_123",
                      "kind": "order",
                      "mode": "test",
                      "orderId": "ord_123",
                      "productId": "prod_123",
                      "refundableAmount": 10000,
                      "rejectReason": null,
                      "requestedAmount": 10000,
                      "status": "withdrawn",
                      "statutory": true,
                      "subscriptionId": null,
                      "updatedAt": "2026-10-15T00:02:00.000Z",
                      "warnings": []
                    },
                    "id": "evt_123",
                    "mode": "test",
                    "type": "refund.withdrawn"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1WebhookEnvelope_refund_withdrawn"
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "content": {
              "application/json": {
                "example": {
                  "received": true
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "이벤트를 수신했어요."
          }
        },
        "security": [],
        "summary": "refund.withdrawn",
        "x-payri-error-codes": [],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "createdAt": "2026-10-15T00:00:00.000Z",
          "data": {
            "approvedAmount": null,
            "autoApproveAt": "2026-10-18T00:00:00.000Z",
            "breakdown": {
              "orders": [
                {
                  "amount": 10000,
                  "currency": "KRW",
                  "orderId": "ord_123"
                }
              ]
            },
            "buyerEmail": "buyer@example.com",
            "buyerReason": "상품을 환불하고 싶어요.",
            "createdAt": "2026-10-15T00:00:00.000Z",
            "currency": "KRW",
            "decidedAt": "2026-10-15T00:02:00.000Z",
            "decidedBy": null,
            "hold": null,
            "id": "rfr_123",
            "kind": "order",
            "mode": "test",
            "orderId": "ord_123",
            "productId": "prod_123",
            "refundableAmount": 10000,
            "rejectReason": null,
            "requestedAmount": 10000,
            "status": "withdrawn",
            "statutory": true,
            "subscriptionId": null,
            "updatedAt": "2026-10-15T00:02:00.000Z",
            "warnings": []
          },
          "id": "evt_123",
          "mode": "test",
          "type": "refund.withdrawn"
        },
        "x-payri-response-example": {
          "received": true
        },
        "x-payri-scope": []
      }
    },
    "subscription.created": {
      "post": {
        "description": "새 구독이 생성되면 보내요. 본문과 data의 mode는 test 또는 live예요.",
        "operationId": "webhook_subscription_created",
        "parameters": [
          {
            "description": "이벤트 ID예요. 본문 id와 같고 재시도에도 유지돼요.",
            "in": "header",
            "name": "webhook-id",
            "required": true,
            "schema": {
              "example": "evt_123",
              "type": "string"
            }
          },
          {
            "description": "서명 시각을 초 단위 문자열로 전달해요.",
            "in": "header",
            "name": "webhook-timestamp",
            "required": true,
            "schema": {
              "example": "1792022400",
              "type": "string"
            }
          },
          {
            "description": "HMAC-SHA256 v1 서명 목록이에요.",
            "in": "header",
            "name": "webhook-signature",
            "required": true,
            "schema": {
              "example": "v1,base64...",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "subscription.created 이벤트 예시",
                  "value": {
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "data": {
                      "amount": 9900,
                      "billingInterval": "monthly",
                      "buyerEmail": "buyer@example.com",
                      "buyerName": "홍길동",
                      "cancelAtPeriodEnd": false,
                      "change": {
                        "amount": null,
                        "currency": null,
                        "effectiveAt": null,
                        "extensionDays": null,
                        "from": null,
                        "kind": "created",
                        "orderId": null,
                        "prorationAmount": null,
                        "to": null
                      },
                      "currency": "KRW",
                      "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
                      "currentPeriodStart": "2026-10-01T00:00:00.000Z",
                      "endReason": null,
                      "endedAt": null,
                      "history": [],
                      "id": "sub_123",
                      "mode": "test",
                      "pastDueSince": null,
                      "pendingPriceChange": null,
                      "planId": "plan_basic",
                      "productId": "prod_123",
                      "productName": "멤버십",
                      "refundEligibleUntil": "2026-10-08T00:00:00.000Z",
                      "refundTerms": {
                        "dayRounding": "ceil",
                        "initialFullRefundDays": 7,
                        "listAmounts": null,
                        "penalty": null,
                        "policyType": "within_7days",
                        "renewalFullRefundDays": 7,
                        "requireApproval": false,
                        "restrictRepeatFullRefund": false,
                        "yearlyDiscountClawback": false
                      },
                      "scheduledChange": null,
                      "startedAt": "2026-10-01T00:00:00.000Z",
                      "status": "active",
                      "testRenewalOutcome": "succeed"
                    },
                    "id": "evt_123",
                    "mode": "test",
                    "type": "subscription.created"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1WebhookEnvelope_subscription_created"
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "content": {
              "application/json": {
                "example": {
                  "received": true
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "이벤트를 수신했어요."
          }
        },
        "security": [],
        "summary": "subscription.created",
        "x-payri-error-codes": [],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "createdAt": "2026-10-15T00:00:00.000Z",
          "data": {
            "amount": 9900,
            "billingInterval": "monthly",
            "buyerEmail": "buyer@example.com",
            "buyerName": "홍길동",
            "cancelAtPeriodEnd": false,
            "change": {
              "amount": null,
              "currency": null,
              "effectiveAt": null,
              "extensionDays": null,
              "from": null,
              "kind": "created",
              "orderId": null,
              "prorationAmount": null,
              "to": null
            },
            "currency": "KRW",
            "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
            "currentPeriodStart": "2026-10-01T00:00:00.000Z",
            "endReason": null,
            "endedAt": null,
            "history": [],
            "id": "sub_123",
            "mode": "test",
            "pastDueSince": null,
            "pendingPriceChange": null,
            "planId": "plan_basic",
            "productId": "prod_123",
            "productName": "멤버십",
            "refundEligibleUntil": "2026-10-08T00:00:00.000Z",
            "refundTerms": {
              "dayRounding": "ceil",
              "initialFullRefundDays": 7,
              "listAmounts": null,
              "penalty": null,
              "policyType": "within_7days",
              "renewalFullRefundDays": 7,
              "requireApproval": false,
              "restrictRepeatFullRefund": false,
              "yearlyDiscountClawback": false
            },
            "scheduledChange": null,
            "startedAt": "2026-10-01T00:00:00.000Z",
            "status": "active",
            "testRenewalOutcome": "succeed"
          },
          "id": "evt_123",
          "mode": "test",
          "type": "subscription.created"
        },
        "x-payri-response-example": {
          "received": true
        },
        "x-payri-scope": []
      }
    },
    "subscription.ended": {
      "post": {
        "description": "구독이 종료되면 보내요. 본문과 data의 mode는 test 또는 live예요.",
        "operationId": "webhook_subscription_ended",
        "parameters": [
          {
            "description": "이벤트 ID예요. 본문 id와 같고 재시도에도 유지돼요.",
            "in": "header",
            "name": "webhook-id",
            "required": true,
            "schema": {
              "example": "evt_123",
              "type": "string"
            }
          },
          {
            "description": "서명 시각을 초 단위 문자열로 전달해요.",
            "in": "header",
            "name": "webhook-timestamp",
            "required": true,
            "schema": {
              "example": "1792022400",
              "type": "string"
            }
          },
          {
            "description": "HMAC-SHA256 v1 서명 목록이에요.",
            "in": "header",
            "name": "webhook-signature",
            "required": true,
            "schema": {
              "example": "v1,base64...",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "subscription.ended 이벤트 예시",
                  "value": {
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "data": {
                      "amount": 9900,
                      "billingInterval": "monthly",
                      "buyerEmail": "buyer@example.com",
                      "buyerName": "홍길동",
                      "cancelAtPeriodEnd": false,
                      "currency": "KRW",
                      "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
                      "currentPeriodStart": "2026-10-01T00:00:00.000Z",
                      "endReason": "refunded",
                      "endedAt": "2026-10-15T00:02:00.000Z",
                      "history": [],
                      "id": "sub_123",
                      "mode": "test",
                      "pastDueSince": null,
                      "pendingPriceChange": null,
                      "planId": "plan_basic",
                      "productId": "prod_123",
                      "productName": "멤버십",
                      "refundAmount": 10000,
                      "refundEligibleUntil": "2026-10-08T00:00:00.000Z",
                      "refundTerms": {
                        "dayRounding": "ceil",
                        "initialFullRefundDays": 7,
                        "listAmounts": null,
                        "penalty": null,
                        "policyType": "within_7days",
                        "renewalFullRefundDays": 7,
                        "requireApproval": false,
                        "restrictRepeatFullRefund": false,
                        "yearlyDiscountClawback": false
                      },
                      "scheduledChange": null,
                      "startedAt": "2026-10-01T00:00:00.000Z",
                      "status": "ended",
                      "testRenewalOutcome": "succeed"
                    },
                    "id": "evt_123",
                    "mode": "test",
                    "type": "subscription.ended"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1WebhookEnvelope_subscription_ended"
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "content": {
              "application/json": {
                "example": {
                  "received": true
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "이벤트를 수신했어요."
          }
        },
        "security": [],
        "summary": "subscription.ended",
        "x-payri-error-codes": [],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "createdAt": "2026-10-15T00:00:00.000Z",
          "data": {
            "amount": 9900,
            "billingInterval": "monthly",
            "buyerEmail": "buyer@example.com",
            "buyerName": "홍길동",
            "cancelAtPeriodEnd": false,
            "currency": "KRW",
            "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
            "currentPeriodStart": "2026-10-01T00:00:00.000Z",
            "endReason": "refunded",
            "endedAt": "2026-10-15T00:02:00.000Z",
            "history": [],
            "id": "sub_123",
            "mode": "test",
            "pastDueSince": null,
            "pendingPriceChange": null,
            "planId": "plan_basic",
            "productId": "prod_123",
            "productName": "멤버십",
            "refundAmount": 10000,
            "refundEligibleUntil": "2026-10-08T00:00:00.000Z",
            "refundTerms": {
              "dayRounding": "ceil",
              "initialFullRefundDays": 7,
              "listAmounts": null,
              "penalty": null,
              "policyType": "within_7days",
              "renewalFullRefundDays": 7,
              "requireApproval": false,
              "restrictRepeatFullRefund": false,
              "yearlyDiscountClawback": false
            },
            "scheduledChange": null,
            "startedAt": "2026-10-01T00:00:00.000Z",
            "status": "ended",
            "testRenewalOutcome": "succeed"
          },
          "id": "evt_123",
          "mode": "test",
          "type": "subscription.ended"
        },
        "x-payri-response-example": {
          "received": true
        },
        "x-payri-scope": []
      }
    },
    "subscription.payment_failed": {
      "post": {
        "description": "구독 갱신 결제가 실패하면 보내요. 본문과 data의 mode는 test 또는 live예요.",
        "operationId": "webhook_subscription_payment_failed",
        "parameters": [
          {
            "description": "이벤트 ID예요. 본문 id와 같고 재시도에도 유지돼요.",
            "in": "header",
            "name": "webhook-id",
            "required": true,
            "schema": {
              "example": "evt_123",
              "type": "string"
            }
          },
          {
            "description": "서명 시각을 초 단위 문자열로 전달해요.",
            "in": "header",
            "name": "webhook-timestamp",
            "required": true,
            "schema": {
              "example": "1792022400",
              "type": "string"
            }
          },
          {
            "description": "HMAC-SHA256 v1 서명 목록이에요.",
            "in": "header",
            "name": "webhook-signature",
            "required": true,
            "schema": {
              "example": "v1,base64...",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "subscription.payment_failed 이벤트 예시",
                  "value": {
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "data": {
                      "amount": 9900,
                      "attempts": 2,
                      "billingInterval": "monthly",
                      "buyerEmail": "buyer@example.com",
                      "buyerName": "홍길동",
                      "cancelAtPeriodEnd": false,
                      "currency": "KRW",
                      "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
                      "currentPeriodStart": "2026-10-01T00:00:00.000Z",
                      "endReason": null,
                      "endedAt": null,
                      "history": [],
                      "id": "sub_123",
                      "mode": "test",
                      "pastDueSince": "2026-10-15T00:02:00.000Z",
                      "pendingPriceChange": null,
                      "planId": "plan_basic",
                      "productId": "prod_123",
                      "productName": "멤버십",
                      "refundEligibleUntil": "2026-10-08T00:00:00.000Z",
                      "refundTerms": {
                        "dayRounding": "ceil",
                        "initialFullRefundDays": 7,
                        "listAmounts": null,
                        "penalty": null,
                        "policyType": "within_7days",
                        "renewalFullRefundDays": 7,
                        "requireApproval": false,
                        "restrictRepeatFullRefund": false,
                        "yearlyDiscountClawback": false
                      },
                      "scheduledChange": null,
                      "startedAt": "2026-10-01T00:00:00.000Z",
                      "status": "past_due",
                      "testRenewalOutcome": "fail"
                    },
                    "id": "evt_123",
                    "mode": "test",
                    "type": "subscription.payment_failed"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1WebhookEnvelope_subscription_payment_failed"
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "content": {
              "application/json": {
                "example": {
                  "received": true
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "이벤트를 수신했어요."
          }
        },
        "security": [],
        "summary": "subscription.payment_failed",
        "x-payri-error-codes": [],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "createdAt": "2026-10-15T00:00:00.000Z",
          "data": {
            "amount": 9900,
            "attempts": 2,
            "billingInterval": "monthly",
            "buyerEmail": "buyer@example.com",
            "buyerName": "홍길동",
            "cancelAtPeriodEnd": false,
            "currency": "KRW",
            "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
            "currentPeriodStart": "2026-10-01T00:00:00.000Z",
            "endReason": null,
            "endedAt": null,
            "history": [],
            "id": "sub_123",
            "mode": "test",
            "pastDueSince": "2026-10-15T00:02:00.000Z",
            "pendingPriceChange": null,
            "planId": "plan_basic",
            "productId": "prod_123",
            "productName": "멤버십",
            "refundEligibleUntil": "2026-10-08T00:00:00.000Z",
            "refundTerms": {
              "dayRounding": "ceil",
              "initialFullRefundDays": 7,
              "listAmounts": null,
              "penalty": null,
              "policyType": "within_7days",
              "renewalFullRefundDays": 7,
              "requireApproval": false,
              "restrictRepeatFullRefund": false,
              "yearlyDiscountClawback": false
            },
            "scheduledChange": null,
            "startedAt": "2026-10-01T00:00:00.000Z",
            "status": "past_due",
            "testRenewalOutcome": "fail"
          },
          "id": "evt_123",
          "mode": "test",
          "type": "subscription.payment_failed"
        },
        "x-payri-response-example": {
          "received": true
        },
        "x-payri-scope": []
      }
    },
    "subscription.updated": {
      "post": {
        "description": "플랜, 주기, 기간 또는 해지 예약이 변경되면 보내요. 본문과 data의 mode는 test 또는 live예요.",
        "operationId": "webhook_subscription_updated",
        "parameters": [
          {
            "description": "이벤트 ID예요. 본문 id와 같고 재시도에도 유지돼요.",
            "in": "header",
            "name": "webhook-id",
            "required": true,
            "schema": {
              "example": "evt_123",
              "type": "string"
            }
          },
          {
            "description": "서명 시각을 초 단위 문자열로 전달해요.",
            "in": "header",
            "name": "webhook-timestamp",
            "required": true,
            "schema": {
              "example": "1792022400",
              "type": "string"
            }
          },
          {
            "description": "HMAC-SHA256 v1 서명 목록이에요.",
            "in": "header",
            "name": "webhook-signature",
            "required": true,
            "schema": {
              "example": "v1,base64...",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "subscription.updated 이벤트 예시",
                  "value": {
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "data": {
                      "amount": 9900,
                      "billingInterval": "monthly",
                      "buyerEmail": "buyer@example.com",
                      "buyerName": "홍길동",
                      "cancelAtPeriodEnd": false,
                      "change": {
                        "amount": 9900,
                        "currency": "KRW",
                        "effectiveAt": null,
                        "extensionDays": null,
                        "from": {
                          "billingInterval": "monthly",
                          "planId": "plan_basic"
                        },
                        "kind": "plan_changed",
                        "orderId": null,
                        "prorationAmount": 0,
                        "to": {
                          "billingInterval": "monthly",
                          "planId": "plan_pro"
                        }
                      },
                      "currency": "KRW",
                      "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
                      "currentPeriodStart": "2026-10-01T00:00:00.000Z",
                      "endReason": null,
                      "endedAt": null,
                      "history": [],
                      "id": "sub_123",
                      "mode": "test",
                      "pastDueSince": null,
                      "pendingPriceChange": null,
                      "planId": "plan_pro",
                      "productId": "prod_123",
                      "productName": "멤버십",
                      "refundEligibleUntil": "2026-10-08T00:00:00.000Z",
                      "refundTerms": {
                        "dayRounding": "ceil",
                        "initialFullRefundDays": 7,
                        "listAmounts": null,
                        "penalty": null,
                        "policyType": "within_7days",
                        "renewalFullRefundDays": 7,
                        "requireApproval": false,
                        "restrictRepeatFullRefund": false,
                        "yearlyDiscountClawback": false
                      },
                      "scheduledChange": null,
                      "startedAt": "2026-10-01T00:00:00.000Z",
                      "status": "active",
                      "testRenewalOutcome": "succeed"
                    },
                    "id": "evt_123",
                    "mode": "test",
                    "type": "subscription.updated"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1WebhookEnvelope_subscription_updated"
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "content": {
              "application/json": {
                "example": {
                  "received": true
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "이벤트를 수신했어요."
          }
        },
        "security": [],
        "summary": "subscription.updated",
        "x-payri-error-codes": [],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "createdAt": "2026-10-15T00:00:00.000Z",
          "data": {
            "amount": 9900,
            "billingInterval": "monthly",
            "buyerEmail": "buyer@example.com",
            "buyerName": "홍길동",
            "cancelAtPeriodEnd": false,
            "change": {
              "amount": 9900,
              "currency": "KRW",
              "effectiveAt": null,
              "extensionDays": null,
              "from": {
                "billingInterval": "monthly",
                "planId": "plan_basic"
              },
              "kind": "plan_changed",
              "orderId": null,
              "prorationAmount": 0,
              "to": {
                "billingInterval": "monthly",
                "planId": "plan_pro"
              }
            },
            "currency": "KRW",
            "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
            "currentPeriodStart": "2026-10-01T00:00:00.000Z",
            "endReason": null,
            "endedAt": null,
            "history": [],
            "id": "sub_123",
            "mode": "test",
            "pastDueSince": null,
            "pendingPriceChange": null,
            "planId": "plan_pro",
            "productId": "prod_123",
            "productName": "멤버십",
            "refundEligibleUntil": "2026-10-08T00:00:00.000Z",
            "refundTerms": {
              "dayRounding": "ceil",
              "initialFullRefundDays": 7,
              "listAmounts": null,
              "penalty": null,
              "policyType": "within_7days",
              "renewalFullRefundDays": 7,
              "requireApproval": false,
              "restrictRepeatFullRefund": false,
              "yearlyDiscountClawback": false
            },
            "scheduledChange": null,
            "startedAt": "2026-10-01T00:00:00.000Z",
            "status": "active",
            "testRenewalOutcome": "succeed"
          },
          "id": "evt_123",
          "mode": "test",
          "type": "subscription.updated"
        },
        "x-payri-response-example": {
          "received": true
        },
        "x-payri-scope": []
      }
    },
    "webhook.test": {
      "post": {
        "description": "콘솔에서 테스트 발송을 요청하면 실제 이벤트와 구분되는 테스트 이벤트를 보내요. 본문과 data의 mode는 test 또는 live예요.",
        "operationId": "webhook_webhook_test",
        "parameters": [
          {
            "description": "이벤트 ID예요. 본문 id와 같고 재시도에도 유지돼요.",
            "in": "header",
            "name": "webhook-id",
            "required": true,
            "schema": {
              "example": "evt_123",
              "type": "string"
            }
          },
          {
            "description": "서명 시각을 초 단위 문자열로 전달해요.",
            "in": "header",
            "name": "webhook-timestamp",
            "required": true,
            "schema": {
              "example": "1792022400",
              "type": "string"
            }
          },
          {
            "description": "HMAC-SHA256 v1 서명 목록이에요.",
            "in": "header",
            "name": "webhook-signature",
            "required": true,
            "schema": {
              "example": "v1,base64...",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "default": {
                  "summary": "webhook.test 이벤트 예시",
                  "value": {
                    "createdAt": "2026-10-15T00:00:00.000Z",
                    "data": {
                      "endpointId": "wh_123",
                      "mode": "test",
                      "sentAt": "2026-10-15T00:00:00.000Z"
                    },
                    "id": "evt_123",
                    "mode": "test",
                    "type": "webhook.test"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/V1WebhookEnvelope_webhook_test"
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "content": {
              "application/json": {
                "example": {
                  "received": true
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "이벤트를 수신했어요."
          }
        },
        "security": [],
        "summary": "webhook.test",
        "x-payri-error-codes": [],
        "x-payri-mode-isolated": true,
        "x-payri-request-example": {
          "createdAt": "2026-10-15T00:00:00.000Z",
          "data": {
            "endpointId": "wh_123",
            "mode": "test",
            "sentAt": "2026-10-15T00:00:00.000Z"
          },
          "id": "evt_123",
          "mode": "test",
          "type": "webhook.test"
        },
        "x-payri-response-example": {
          "received": true
        },
        "x-payri-scope": []
      }
    }
  },
  "x-payri-error-catalog": {
    "ALREADY_CANCELED": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "ALREADY_ENDED": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "API_KEY_INVALID": {
      "generic": true,
      "message": "API 키가 없거나 올바르지 않아요.",
      "status": 401
    },
    "API_KEY_IP_NOT_ALLOWED": {
      "message": "허용된 IP 주소에서 요청해야 해요.",
      "status": 403
    },
    "API_KEY_SCOPE_MISSING": {
      "message": "요청에 필요한 권한이 없어요.",
      "status": 403
    },
    "BAD_REQUEST": {
      "generic": true,
      "message": "요청 필드를 확인해 주세요.",
      "status": 400
    },
    "CHECKOUT_NOT_CONFIGURED": {
      "message": "요청을 처리하는 중 오류가 발생했어요.",
      "status": 500
    },
    "CHECKOUT_SESSION_NOT_FOUND": {
      "message": "요청한 리소스를 찾을 수 없어요.",
      "status": 404
    },
    "CHECKOUT_URL_INVALID": {
      "message": "요청 필드를 확인해 주세요.",
      "status": 400
    },
    "ENDED": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "IDEMPOTENCY_IN_PROGRESS": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "IDEMPOTENCY_KEY_CONFLICT": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "INTERNAL_ERROR": {
      "generic": true,
      "message": "요청을 처리하는 중 오류가 발생했어요.",
      "status": 500
    },
    "LICENSE_LOOKUP_BLOCKED": {
      "message": "API 키 호출 한도를 초과했어요. 잠시 후 다시 시도해 주세요.",
      "status": 429
    },
    "LICENSE_NOT_FOUND": {
      "message": "요청한 리소스를 찾을 수 없어요.",
      "status": 404
    },
    "LICENSE_RATE_LIMITED": {
      "message": "API 키 호출 한도를 초과했어요. 잠시 후 다시 시도해 주세요.",
      "status": 429
    },
    "METADATA_INVALID": {
      "message": "요청 필드를 확인해 주세요.",
      "status": 400
    },
    "NOT_FOUND": {
      "generic": true,
      "message": "요청한 리소스를 찾을 수 없어요.",
      "status": 404
    },
    "NOT_STARTED": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "ORDER_NOT_FOUND": {
      "message": "요청한 리소스를 찾을 수 없어요.",
      "status": 404
    },
    "PG_REFUND_AMBIGUOUS": {
      "message": "요청을 처리하는 중 오류가 발생했어요.",
      "status": 502
    },
    "PG_REFUND_FAILED": {
      "message": "요청을 처리하는 중 오류가 발생했어요.",
      "status": 502
    },
    "PRODUCT_ARCHIVED": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "PRODUCT_NOT_FOUND": {
      "message": "요청한 리소스를 찾을 수 없어요.",
      "status": 404
    },
    "PUBLIC_CONTACT_VERIFICATION_REQUIRED": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "RATE_LIMITED": {
      "generic": true,
      "message": "API 키 호출 한도를 초과했어요. 잠시 후 다시 시도해 주세요.",
      "status": 429
    },
    "REFUND_AMOUNT_EXCEEDS": {
      "message": "요청 필드를 확인해 주세요.",
      "status": 400
    },
    "REFUND_AMOUNT_INVALID": {
      "message": "요청 필드를 확인해 주세요.",
      "status": 400
    },
    "REFUND_AMOUNT_MISMATCH": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "REFUND_EXCEEDS": {
      "message": "요청 필드를 확인해 주세요.",
      "status": 400
    },
    "REFUND_IN_PROGRESS": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "REFUND_NONE_LEFT": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "REFUND_NOT_PAID": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "REFUND_REASON_REQUIRED": {
      "message": "요청 필드를 확인해 주세요.",
      "status": 400
    },
    "REFUND_REQUEST_NOT_ACTIONABLE": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "REFUND_REQUEST_NOT_FOUND": {
      "message": "요청한 리소스를 찾을 수 없어요.",
      "status": 404
    },
    "REFUND_REQUEST_PENDING": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "REFUND_SETTLED": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "REFUND_STATUTORY_LOCKED": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "RETURN_URL_NOT_ALLOWED": {
      "message": "요청 필드를 확인해 주세요.",
      "status": 400
    },
    "SOLD_OUT": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "SUBSCRIPTION_NOT_FOUND": {
      "message": "요청한 리소스를 찾을 수 없어요.",
      "status": 404
    },
    "SUBSCRIPTION_REFUND_AMOUNT_INVALID": {
      "message": "요청 필드를 확인해 주세요.",
      "status": 400
    },
    "SUBSCRIPTION_REFUND_NONE_LEFT": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "SUBSCRIPTION_REFUND_SETTLED": {
      "message": "요청을 현재 상태에서 처리할 수 없어요.",
      "status": 409
    },
    "VALIDATION_FAILED": {
      "generic": true,
      "message": "요청 필드를 확인해 주세요.",
      "status": 400
    }
  }
}
