---
title: "Create Refund"
method: POST
path: "/v1/refunds"
tags: ["Billing Refunds"]
---

# Create Refund

`POST /v1/refunds`

## Request

- Base URL: `https://api.xdata.kr` (the document declares no server; this is the origin it was published from)
- URL: `https://api.xdata.kr/v1/refunds`
- Auth: one of:
  - HTTP basic
  - OAuth 2 (authorization code, authorization URL `https://api.xdata.kr/oauth/authorize`, token URL `https://api.xdata.kr/oauth/token`)

환불 요청.

## Query parameters

- `env_scope` 'production' | 'real_test' | 'sandbox', nullable: 환경을 고릅니다. 값은 production · real_test · sandbox 중 하나입니다. 경로 접두(/v1/sandbox/... · /v1/test/... · /v1/production/...) · X-Env-Scope 헤더 · env_scope 쿼리 가운데 한 통로로 반드시 보내야 합니다. 빼면 자동으로 채우지 않고 거절하며, 두 통로에 서로 다른 값을 담아도 거절합니다. 거절은 HTTP 422 이고 오류 코드는 VALIDATION_ENV_SCOPE_MISMATCH 입니다.

## Headers

- `Idempotency-Key` string, required
- `Authorization` string, nullable
- `X-Env-Scope` 'production' | 'real_test' | 'sandbox', nullable: 환경을 고릅니다. 값은 production · real_test · sandbox 중 하나입니다. 경로 접두(/v1/sandbox/... · /v1/test/... · /v1/production/...) · X-Env-Scope 헤더 · env_scope 쿼리 가운데 한 통로로 반드시 보내야 합니다. 빼면 자동으로 채우지 않고 거절하며, 두 통로에 서로 다른 값을 담아도 거절합니다. 거절은 HTTP 422 이고 오류 코드는 VALIDATION_ENV_SCOPE_MISMATCH 입니다.

## Request body

- RefundCreateRequest: 환불 생성 요청. 처리 흐름: 1) AuthPolicyService 평가(`request_kind='billing'`, BILLING_ADMIN·SYS_ADMIN 또는 본인 조직 OWNER) 2) `payment_id` 소유권·`status='SUCCEEDED'`·env_scope 검증 - 무통장 입금 결제는 이 요청으로 환불하지 않는다 (`422 BILLING_INVALID_INPUT`, 아무것도 쓰기 전에 거절) 3) 환불 합산 ≤ 원 결제 amount_minor 검증 (서비스 계층 책임) - 위반 시 `422 BILLING_REFUND_EXCEEDS_PAYMENT` 4) `refunds` 행 PENDING 생성 5) 환불 요청 (어댑터 호출) 6) `cancel_subscription=true`인 경우 동일 트랜잭션에서 구독 `CANCELED` 전이 7) 동기 응답: 202 Accepted + 환불 객체 스냅샷(PENDING) PG 환불 결과는 웹훅으로 수신 → `refunds.SUCCEEDED` + `invoices.REFUNDED/PARTIALLY_REFUNDED`. `organization_id`는 본 DTO에 두지 않는다 — `payment_id`로 조직이 자동 추론되며, 라우트가 `PrincipalContext.organization_id`와의 일치를 검증하여 403 차단(IDOR 방지). `metadata`는 입력만 허용(응답 미노출). PII/secret/credential 적재 금지. 키 화이트리스트는 후속 ADR.
  - `payment_id` integer, required: 환불 대상 결제 ID (payments.id). status='SUCCEEDED'만 허용하고 무통장 입금 결제는 받지 않는다(422).
  - `amount_minor` integer, required: 환불 금액 (minor unit 정수). 0 과 음수는 받지 않는다. 전액/부분 모두 허용. 환불 합산 ≤ 원 결제 amount_minor는 서비스 계층 검증.
  - `reason` 'USER_REQUEST' | 'OPS_DISPUTE_RESOLUTION' | 'DUPLICATE_PAYMENT' | 'PAYMENT_FAILED_REVERSAL' | 'FRAUDULENT_PAYMENT' | 'OTHER', nullable: 환불 사유 enum. 미지정이면 기본값 없이 저장하고 응답 reason 은 null (note 만 있으면 그 글만 저장).
  - `note` string, nullable: 운영 메모(자유 텍스트). SCHEMA `refunds.reason VARCHAR(255)` 컬럼에 `reason` enum과 함께 합성 저장(서비스 계층 책임). PII/secret 금지.
  - `cancel_subscription` boolean: 동일 트랜잭션에서 구독 CANCELED 전이 여부. True 시 `subscriptions.status='CANCELED'`로 전이 + `audit_logs.billing.subscription.transition` 적재.
  - `metadata` object, nullable: 운영 메타데이터 (예: source, dispute_id, support_ticket_id). PII/secret 적재 금지. 키 화이트리스트는 별도 결정.

## Response `202`

Successful Response

- RefundCreateResponse: 환불 생성 응답 - `202 Accepted` 의 몸통. 즉시 응답 다섯 칸(`refund_id` · `payment_id` · `amount_minor` · `status` · `requested_at`)에 **`env_scope` 를 더해** 모든 응답에 일관되게 노출한다. GET 단건 조회(`RefundResponse`)와 다른 객체: - 본 응답은 **즉시 응답 최소 필드**만 포함(BFF 폴링 시작용). - 상세 조회는 `GET /v1/refunds/{id}` (`RefundResponse`) 사용. `status`는 통상 PENDING(PG 비동기 결과 대기). 동기 SUCCEEDED는 PG가 즉시 응답한 경우만.
  - `refund_id` integer, required: refunds.id (BIGSERIAL PK).
  - `payment_id` integer, required: 원 결제 ID (payments.id).
  - `amount_minor` integer, required: 환불 금액 (minor unit). 0 과 음수는 받지 않는다.
  - `status` 'PENDING' | 'SUCCEEDED' | 'FAILED' | 'CANCELED', required: 환불 상태. 본 응답은 보통 PENDING(202 Accepted).
  - `env_scope` 'sandbox' | 'real_test' | 'production', required: 환경 스코프. 모든 응답에 일관되게 노출한다(환경 식별 + 감사 일관성).
  - `requested_at` string, date-time, required: 환불 요청 시각 (aware datetime, naive 입력 시 422).

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/xdata/apis/xboss-api.md) · [All operations](https://skmtc.dev/xdata/apis/xboss-api/llms.txt) · [OpenAPI document](https://skmtc.dev/xdata/apis/xboss-api/revisions/816de78950db?raw)
