---
template: policy-doc@1
doc_id: 03-payments
title: 결제 벤더 연동 — 게이트는 코드에 있고 값은 저장소 밖에 있다
config: ../_meta/template-config.yaml
last_verified: 4d798f55
policies:
  - id: payment:smartro:timeout
    status: conditional-unknown
    evidence: [A-vendor, S-constant]
    unknown_reason: 요청 만료 시점을 정하는 층이다. 만료 뒤 호출자가 어느 분기로 가는가는 동결 stg 4d798f55 의 코드로 확정되지만, 그 분기가 발효하려면 이 연동이 먼저 서야 한다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 재유도된 게이트 종류는 `env` 이고 닫을 경로가 이 PRD 범위 밖이다. 만료 값 자체는 이 문서가 옮기지 않는다(30-contracts-limits 소유).
    anchors:
      - insuniverse-server/libs/pg/src/smartro/smartro.service.ts:125
      - insuniverse-server/libs/pg/src/smartro/smartro.service.ts:215
  - id: payment:smartro:fallback
    status: conditional-unknown
    evidence: [A-vendor, S-constant]
    unknown_reason: 폴백이 없다는 것이 이 층의 내용이다 — 멀티 공급자 폴백은 진료수집 축 전용이고 이 벤더는 디스패처 풀에 없다. **관측된 부재이지 미조사가 아니다.** 그 부재가 운영에서 유지되는지는 이 연동이 실제로 서 있는가에 걸린다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 재유도된 게이트 종류는 `env` 이고 닫을 경로가 이 PRD 범위 밖이다.
    anchors:
      - insuniverse-server/libs/pg/src/smartro/smartro.service.ts:125
      - insuniverse-server/libs/pg/src/smartro/smartro.service.ts:215
  - id: payment:smartro:normalization
    status: conditional-unknown
    evidence: [A-vendor, S-constant]
    unknown_reason: 정규화 매퍼가 없다는 것이 이 층의 내용이다 — 응답을 그대로 응답 DTO 로 옮기거나 자기 형태를 정본으로 쓴다. **관측된 부재다.** 그 형태가 발효하려면 이 연동이 서야 한다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 재유도된 게이트 종류는 `env` 이고 닫을 경로가 이 PRD 범위 밖이다.
    anchors:
      - insuniverse-server/libs/pg/src/smartro/smartro.service.ts:125
      - insuniverse-server/libs/pg/src/smartro/smartro.service.ts:215
  - id: payment:smartro:exception
    status: conditional-unknown
    evidence: [A-vendor, S-constant]
    unknown_reason: 호출 실패를 어느 예외 형태로 감싸는가를 정하는 층이다. 예외 클래스 이름과 HTTP 상태는 코드로 확정되지만 그 예외가 던져지려면 이 연동이 서야 한다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 재유도된 게이트 종류는 `none` 이고 닫을 경로가 이 PRD 범위 밖이다. 게이트 종류가 `none` 인 것은 이 층의 앵커 파일에서 DB 토큰도 env 토큰도 관측되지 않았다는 뜻이지 미명세가 아니다.
    anchors:
      - insuniverse-server/libs/pg/src/smartro/smartro.exception.ts:3
  - id: payment:smartro:env_gates
    status: conditional-unknown
    evidence: [A-vendor, S-constant]
    unknown_reason: 이 벤더 연동이 서려면 설정 블록의 환경 키가 주입돼야 한다는 것이 이 층의 내용이다. 키 **이름**은 런타임 인덱스가 열거하지만 값은 저장소 밖이고 이 문서도 이름을 나열하지 않는다(11-index-code · 12-index-runtime 소유). 재유도된 게이트 종류는 `env` 이고 닫을 경로가 이 PRD 범위 밖이다.
    anchors:
      - insuniverse-server/apps/api/src/config/configuration.ts:155
  - id: payment:toss:timeout
    status: conditional-unknown
    evidence: [A-vendor, S-constant]
    unknown_reason: 요청 만료 시점을 정하는 층이다. 만료 뒤 호출자가 어느 분기로 가는가는 동결 stg 4d798f55 의 코드로 확정되지만, 그 분기가 발효하려면 이 연동이 먼저 서야 한다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 재유도된 게이트 종류는 `env` 이고 닫을 경로가 이 PRD 범위 밖이다. 만료 값 자체는 이 문서가 옮기지 않는다(30-contracts-limits 소유).
    anchors:
      - insuniverse-server/libs/pg/src/toss/toss.service.ts:43
      - insuniverse-server/libs/pg/src/toss/toss.service.ts:95
  - id: payment:toss:fallback
    status: conditional-unknown
    evidence: [A-vendor, S-constant]
    unknown_reason: 폴백이 없다는 것이 이 층의 내용이다 — 멀티 공급자 폴백은 진료수집 축 전용이고 이 벤더는 디스패처 풀에 없다. **관측된 부재이지 미조사가 아니다.** 그 부재가 운영에서 유지되는지는 이 연동이 실제로 서 있는가에 걸린다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 재유도된 게이트 종류는 `env` 이고 닫을 경로가 이 PRD 범위 밖이다.
    anchors:
      - insuniverse-server/libs/pg/src/toss/toss.service.ts:43
      - insuniverse-server/libs/pg/src/toss/toss.service.ts:95
  - id: payment:toss:normalization
    status: conditional-unknown
    evidence: [A-vendor, S-constant]
    unknown_reason: 정규화 매퍼가 없다는 것이 이 층의 내용이다 — 응답을 그대로 응답 DTO 로 옮기거나 자기 형태를 정본으로 쓴다. **관측된 부재다.** 그 형태가 발효하려면 이 연동이 서야 한다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 재유도된 게이트 종류는 `env` 이고 닫을 경로가 이 PRD 범위 밖이다.
    anchors:
      - insuniverse-server/libs/pg/src/toss/toss.service.ts:43
      - insuniverse-server/libs/pg/src/toss/toss.service.ts:95
  - id: payment:toss:exception
    status: conditional-unknown
    evidence: [A-vendor, S-constant]
    unknown_reason: 호출 실패를 어느 예외 형태로 감싸는가를 정하는 층이다. 예외 클래스 이름과 HTTP 상태는 코드로 확정되지만 그 예외가 던져지려면 이 연동이 서야 한다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 재유도된 게이트 종류는 `none` 이고 닫을 경로가 이 PRD 범위 밖이다. 게이트 종류가 `none` 인 것은 이 층의 앵커 파일에서 DB 토큰도 env 토큰도 관측되지 않았다는 뜻이지 미명세가 아니다.
    anchors:
      - insuniverse-server/libs/pg/src/toss/toss.exception.ts:3
  - id: payment:toss:env_gates
    status: conditional-unknown
    evidence: [A-vendor, S-constant]
    unknown_reason: 이 벤더 연동이 서려면 설정 블록의 환경 키가 주입돼야 한다는 것이 이 층의 내용이다. 키 **이름**은 런타임 인덱스가 열거하지만 값은 저장소 밖이고 이 문서도 이름을 나열하지 않는다(11-index-code · 12-index-runtime 소유). 재유도된 게이트 종류는 `env` 이고 닫을 경로가 이 PRD 범위 밖이다.
    anchors:
      - insuniverse-server/apps/api/src/config/configuration.ts:103
  - id: payment:__env__:mode
    status: conditional-unknown
    evidence: [S-component, A-comment]
    unknown_reason: 결제 모드와 클라이언트 키가 프런트 코드에서 환경 변수로 읽히고 그 값이 저장소 밖이다. **동결 site 트리에 `.env` 계열 파일이 0건이라** 값이 앵커로 결박되지도 않는다 — 게이트는 코드에 있고 값은 밖에 있다는 미판정의 정확한 형태다. 닫을 경로가 이 PRD 범위 밖이다.
    anchors:
      - insuniverse-site/front/src/pages/payment/PaymentPage.tsx:95
      - insuniverse-site/front/src/features/payment/hooks/usePayment.tsx:73
---

이 문서는 결제 두 벤더의 **연동 층**을 다룬다. 두 벤더가 무엇을 응답하고 그 응답이 무슨 뜻인가는
`02-failure-semantics.md` 가 이미 닫았다. 여기서 여는 것은 그 앞 단계 — 호출 만료 · 폴백 유무 ·
정규화 · 예외 형태 · 연동이 서기 위한 주입 조건이다.

**이 축의 미판정은 한 종류뿐이고 그 사실 자체가 판정이다.** 진료 공급자 축은 풀 구성이 관리자
설정 행이라 닫을 경로가 이름을 갖지만, 결제 축에는 그런 층이 없다. 여기서 걸리는 것은 전부
주입 환경값이고 그 값은 이 PRD 범위 밖이다. **그래서 이 문서의 정책 열한 건은 전건 `게이트
미상` 이다.**

그 미판정이 가장 뚜렷한 형태로 나타나는 자리가 결제 모드다 — 게이트는 코드에 있고, 값은 동결
트리 어디에서도 재현되지 않는다.

---

## 정책 (기획)

**이 절에 정책 블록이 없다.** 본문 배치 자격을 갖는 상태 세 종은 전부 그 정책이 지금 어딘가에
도달해 활성이라는 주장을 담는데, 이 축에는 그 주장을 세울 근거가 저장소 안에 없다.

이 문서의 사유는 형제 문서 둘과 다르다. 진료 공급자 축은 *풀에 들어 있는가* 가 걸림돌이고
메시징 축은 *프로세스가 서 있는가* 가 걸림돌인데, 결제 축의 걸림돌은 **주입값 그 자체**다.
가맹점 식별자도, 대칭키도, 클라이언트 키도, 엔드포인트도 전부 환경 변수로 들어온다. 그리고
**동결 site 트리에 `.env` 계열 파일이 0건이다** — 값을 관측할 자리가 코드 트리에 아예 없다.

부록의 격리는 판정 보류이지 부정이 아니다. 규칙 자체는 동결 stg `4d798f55` 의 코드로 확정된다.

## 연동 층 대조

첫 열은 정책 id 이고 나머지는 **게이트가 재유도한다**. `인덱스 값` 은 값이 아니라 **형태**만
옮긴다 — 열거형과 유무이며 수치는 넣지 않는다(만료 값은 `30-contracts-limits` 소유).
`게이트 종류` 는 동결 트리 재스캔이 만들고 `미판정 종류` 는 그 종류에서 유도된다.

| 정책 id | 벤더 | 층 | 인덱스 값 | 게이트 종류 | 미판정 종류 | 닫을 경로 |
|---|---|---|---|---|---|---|
| `payment:smartro:timeout` | smartro | `timeout` | vendor_side_key: 없음 · dynamic_anchors 無 | `env` | `conditional-unknown` | 없음 — 범위 밖 |
| `payment:smartro:fallback` | smartro | `fallback` | kind: none | `env` | `conditional-unknown` | 없음 — 범위 밖 |
| `payment:smartro:normalization` | smartro | `normalization` | mapper 無 · target: none | `env` | `conditional-unknown` | 없음 — 범위 밖 |
| `payment:smartro:exception` | smartro | `exception` | name: SmartroException · http_status: INTERNAL_SERVER_ERROR | `none` | `conditional-unknown` | 없음 — 범위 밖 |
| `payment:smartro:env_gates` | smartro | `env_gates` | 키 이름만 (값 미이관) | `env` | `conditional-unknown` | 없음 — 범위 밖 |
| `payment:toss:timeout` | toss | `timeout` | vendor_side_key: 없음 · dynamic_anchors 無 | `env` | `conditional-unknown` | 없음 — 범위 밖 |
| `payment:toss:fallback` | toss | `fallback` | kind: none | `env` | `conditional-unknown` | 없음 — 범위 밖 |
| `payment:toss:normalization` | toss | `normalization` | mapper 無 · target: none | `env` | `conditional-unknown` | 없음 — 범위 밖 |
| `payment:toss:exception` | toss | `exception` | name: TossException · http_status: INTERNAL_SERVER_ERROR | `none` | `conditional-unknown` | 없음 — 범위 밖 |
| `payment:toss:env_gates` | toss | `env_gates` | 키 이름만 (값 미이관) | `env` | `conditional-unknown` | 없음 — 범위 밖 |
| `payment:__env__:mode` | — | `mode` | 결제 모드·클라이언트 키가 env 참조이고 값은 저장소 밖 | `env` | `conditional-unknown` | 없음 — 범위 밖 |

`payment:smartro:exception` 과 `payment:toss:exception` 의 게이트 종류가 `none` 인 것은 그 층의
앵커 파일이 예외 클래스 선언 하나뿐이라 DB 토큰도 환경값 토큰도 그 안에 없다는 **관측**이다.
미명세가 아니며, 게이트가 그 판정마다 스캔한 파일 목록을 출력에 함께 낸다.

## 환경별 모드

결제 화면은 벤더 모드와 클라이언트 키를 **프런트 코드에서 환경 변수로 읽는다**
(`insuniverse-site/front/src/pages/payment/PaymentPage.tsx:95` ·
`insuniverse-site/front/src/features/payment/hooks/usePayment.tsx:73`).
그 참조 지점이 그대로 강제 지점이다 — 코드가 모드를 정하지 않고 주입값이 정한다.

**그런데 그 값은 동결 트리에서 재현되지 않는다.** 동결 site `767d1e44` 의 파일 전수에서 `.env`
계열 파일은 0건이다. 부모 PRD 의 Evidence 절이 이 축에 대해 든 값 두 개는 구 조사 기준점의
워킹트리 독출이었고, 신 기준점에서는 **앵커로 결박되지 않는다**. 그래서 이 문서는 그 값을
옮기지 않는다 — 옮기면 문서가 재현 불가능한 관측을 정본으로 승격시키는 것이 된다.

이것이 `게이트 미상` 의 정확한 형태다. 게이트는 코드에 있고(참조 지점이 앵커로 열린다) 값은
저장소 밖에 있으며(관측 자리가 트리에 없다), 그 둘의 결합이 *발효 여부를 판정할 수 없음* 이다.
**닫을 경로가 이 PRD 범위 밖이다** — 배포 정의와 함께 다뤄야 하는 축이다.

## 서명과 암호화

두 벤더의 자격 형태가 다르다. 형태만 적고 키 이름·값·알고리즘 파라미터는 나열하지 않는다
(`11-index-code` · `12-index-runtime` 소유).

- 일반결제 벤더는 **가맹점 식별자와 대칭키**를 갖는다. 승인·취소 엔드포인트가 각각 따로
  주입되고, 취소에는 별도 비밀번호 성격의 값이 하나 더 걸린다 — 취소가 승인보다 한 겹 더
  잠겨 있다는 것이 이 축에서 읽을 수 있는 형태다.
- 정기결제 벤더는 **공개키와 시크릿의 쌍**을 갖는다. 클라이언트 쪽에 노출되는 키와 서버 쪽
  시크릿이 나뉘어 있고, 빌링키 발급·결제·취소 엔드포인트가 각각 따로 주입된다.

두 형태 모두 **연동이 서려면 그 키들이 주입돼야 한다**는 같은 조건 위에 있다. 그래서 두 벤더의
`env_gates` 층이 같은 미판정 어휘로 닫힌다.

## 사용자 노출 분기

정기결제 벤더의 서버 코드에는 **벤더 오류 코드 중 일부만 사용자에게 노출하는 분기**가 있다
(`insuniverse-server/libs/pg/src/toss/toss.service.ts:14` · `insuniverse-server/libs/pg/src/toss/toss.service.ts:71`).
노출 대상 집합이 상수로 선언돼 있고 그 집합에 든 코드만 사용자 메시지로 나간다.

**이 문서는 그 값을 열거하지 않는다.** 코드 값 목록은 `11-index-code` 소유이고, 이 문서가 적는
것은 *분기가 존재한다* 는 사실과 좌표뿐이다.

그런데 여기에 관측이 하나 더 있다. 코드 인덱스의 벤더 코드 namespace 는 **진료수집 축 다섯
벤더의 접두만 받는 패턴**으로 정의돼 있어, 결제·발송 축의 코드는 **구조적으로 그 namespace 에
들어갈 수 없다.** 그래서 이 분기가 참조하는 값들은 지금 인덱스에 항목이 없다. 이 문서는 그
사실을 관측으로 적고 목록화를 `11-index-code` 로 회부한다 — 여기서 값을 적으면 인덱스가 아니라
문서가 코드값의 정본이 된다.

## 강제 지점 (개발)

정책 내용을 다시 쓰지 않는다. 각 정책이 저장소 안 **어디에서** 강제되는가와 그 기제, 그리고
게이트 종류만 적는다. 배치가 부록인 정책도 이 절에 있어야 한다. 기준점은 전건 동결 stg
`4d798f55` 다.

### payment:smartro:timeout

- 강제 방식: HTTP 클라이언트 호출 옵션. 만료가 지나면 호출이 끊기고 그 실패가 위층 분기로 간다
- 앵커: `insuniverse-server/libs/pg/src/smartro/smartro.service.ts:125` · `insuniverse-server/libs/pg/src/smartro/smartro.service.ts:215`
- 게이트: `env` — 주입 환경값이 정한다

### payment:smartro:fallback

- 강제 방식: 디스패처 루프의 공급자 순회. 이 벤더가 그 순회의 원소인가로 강제된다
- 앵커: `insuniverse-server/libs/pg/src/smartro/smartro.service.ts:125` · `insuniverse-server/libs/pg/src/smartro/smartro.service.ts:215`
- 게이트: `env` — 주입 환경값이 정한다

### payment:smartro:normalization

- 강제 방식: 응답 변환 지점. 매퍼가 있으면 그 파일이, 없으면 서비스가 응답 형태를 그대로 진다
- 앵커: `insuniverse-server/libs/pg/src/smartro/smartro.service.ts:125` · `insuniverse-server/libs/pg/src/smartro/smartro.service.ts:215`
- 게이트: `env` — 주입 환경값이 정한다

### payment:smartro:exception

- 강제 방식: 전용 예외 클래스 생성 지점. 호출 실패가 그 형태로 감싸여 위로 간다
- 앵커: `insuniverse-server/libs/pg/src/smartro/smartro.exception.ts:3`
- 게이트: `none` — 걸 게이트가 없다 — 앵커 파일에서 두 토큰이 모두 관측되지 않았다

### payment:smartro:env_gates

- 강제 방식: 설정 블록 로드. 키가 비면 연동 진입이 성립하지 않는다
- 앵커: `insuniverse-server/apps/api/src/config/configuration.ts:155`
- 게이트: `env` — 주입 환경값이 정한다

### payment:toss:timeout

- 강제 방식: HTTP 클라이언트 호출 옵션. 만료가 지나면 호출이 끊기고 그 실패가 위층 분기로 간다
- 앵커: `insuniverse-server/libs/pg/src/toss/toss.service.ts:43` · `insuniverse-server/libs/pg/src/toss/toss.service.ts:95`
- 게이트: `env` — 주입 환경값이 정한다

### payment:toss:fallback

- 강제 방식: 디스패처 루프의 공급자 순회. 이 벤더가 그 순회의 원소인가로 강제된다
- 앵커: `insuniverse-server/libs/pg/src/toss/toss.service.ts:43` · `insuniverse-server/libs/pg/src/toss/toss.service.ts:95`
- 게이트: `env` — 주입 환경값이 정한다

### payment:toss:normalization

- 강제 방식: 응답 변환 지점. 매퍼가 있으면 그 파일이, 없으면 서비스가 응답 형태를 그대로 진다
- 앵커: `insuniverse-server/libs/pg/src/toss/toss.service.ts:43` · `insuniverse-server/libs/pg/src/toss/toss.service.ts:95`
- 게이트: `env` — 주입 환경값이 정한다

### payment:toss:exception

- 강제 방식: 전용 예외 클래스 생성 지점. 호출 실패가 그 형태로 감싸여 위로 간다
- 앵커: `insuniverse-server/libs/pg/src/toss/toss.exception.ts:3`
- 게이트: `none` — 걸 게이트가 없다 — 앵커 파일에서 두 토큰이 모두 관측되지 않았다

### payment:toss:env_gates

- 강제 방식: 설정 블록 로드. 키가 비면 연동 진입이 성립하지 않는다
- 앵커: `insuniverse-server/apps/api/src/config/configuration.ts:103`
- 게이트: `env` — 주입 환경값이 정한다

### payment:__env__:mode

- 강제 방식: 프런트 결제 화면과 결제 훅. 환경 변수 참조 지점이 그대로 강제 지점이다
- 앵커: `insuniverse-site/front/src/pages/payment/PaymentPage.tsx:95` · `insuniverse-site/front/src/features/payment/hooks/usePayment.tsx:73`
- 게이트: `env` — 주입 환경값이 정한다

## 적용 범위 (actor × 서비스)

`적용` 은 **그 정책의 앵커가 그 저장소에 있는 경우**에만 쓴다. 그 밖은 `—` 이며 빈 칸이 아니라
관측된 부재다. 이 규칙은 게이트가 다시 돌린다.

actor 다섯 열이 전건 `—` 인 이유는 형제 문서와 같다 — 연동 층은 로그인 주체를 보지 않는다.
결제를 시작한 사람이 어느 주체인가와 어느 엔드포인트가 어떤 만료값으로 불리는가는 저장소
안에서 갈리지 않는다.

**서비스 축은 이 문서에서 갈린다.** 벤더 연동 열 건의 앵커는 server 에 있고, 환경별 모드 한
건의 앵커는 site 에 있다 — 결제는 프런트가 벤더 SDK 를 직접 부르는 구간을 갖는 유일한 축이다.
그 갈림이 표에 그대로 나온다.

| 정책 id | 관리자 | 영업대리점 | 기업담당자 | 일반회원 | 기업회원 | server | site | admin |
|---|---|---|---|---|---|---|---|---|
| payment:smartro:timeout | — | — | — | — | — | 적용 | — | — |
| payment:smartro:fallback | — | — | — | — | — | 적용 | — | — |
| payment:smartro:normalization | — | — | — | — | — | 적용 | — | — |
| payment:smartro:exception | — | — | — | — | — | 적용 | — | — |
| payment:smartro:env_gates | — | — | — | — | — | 적용 | — | — |
| payment:toss:timeout | — | — | — | — | — | 적용 | — | — |
| payment:toss:fallback | — | — | — | — | — | 적용 | — | — |
| payment:toss:normalization | — | — | — | — | — | 적용 | — | — |
| payment:toss:exception | — | — | — | — | — | 적용 | — | — |
| payment:toss:env_gates | — | — | — | — | — | 적용 | — | — |
| payment:__env__:mode | — | — | — | — | — | — | 적용 | — |

## 예외·폴백

예외가 없는 정책도 행을 둔다 — 없다는 것도 확인의 결과이며 그 확인 근거를 함께 적는다.

| 정책 id | 예외 | 폴백 | 확인 근거 |
|---|---|---|---|
| payment:smartro:timeout | 만료 자체가 예외 경로다 | 만료 뒤 폴백은 이 층이 정하지 않는다 — 폴백 층이 진다 | 런타임 인덱스의 이 벤더 층이 그 값을 담고, 그 층이 이 정책의 근거다 |
| payment:smartro:fallback | 없음 | 없음 — 멀티 공급자 폴백은 진료수집 축 전용이고 이 벤더는 디스패처 풀에 없다 | 런타임 인덱스의 이 벤더 층이 그 값을 담고, 그 층이 이 정책의 근거다 |
| payment:smartro:normalization | 없음 | 없음 — 변환 없이 응답 형태를 그대로 쓴다 | 런타임 인덱스의 이 벤더 층이 그 값을 담고, 그 층이 이 정책의 근거다 |
| payment:smartro:exception | 이 층 자체가 예외 경로다 | 없음 — 예외를 대체하는 경로가 이 벤더에 없다 | 런타임 인덱스의 이 벤더 층이 그 값을 담고, 그 층이 이 정책의 근거다 |
| payment:smartro:env_gates | 키가 비면 연동 진입이 성립하지 않는다 | 없음 — 키 부재를 메우는 기본값이 이 층에 없다 | 런타임 인덱스의 이 벤더 층이 그 값을 담고, 그 층이 이 정책의 근거다 |
| payment:toss:timeout | 만료 자체가 예외 경로다 | 만료 뒤 폴백은 이 층이 정하지 않는다 — 폴백 층이 진다 | 런타임 인덱스의 이 벤더 층이 그 값을 담고, 그 층이 이 정책의 근거다 |
| payment:toss:fallback | 없음 | 없음 — 멀티 공급자 폴백은 진료수집 축 전용이고 이 벤더는 디스패처 풀에 없다 | 런타임 인덱스의 이 벤더 층이 그 값을 담고, 그 층이 이 정책의 근거다 |
| payment:toss:normalization | 없음 | 없음 — 변환 없이 응답 형태를 그대로 쓴다 | 런타임 인덱스의 이 벤더 층이 그 값을 담고, 그 층이 이 정책의 근거다 |
| payment:toss:exception | 이 층 자체가 예외 경로다 | 없음 — 예외를 대체하는 경로가 이 벤더에 없다 | 런타임 인덱스의 이 벤더 층이 그 값을 담고, 그 층이 이 정책의 근거다 |
| payment:toss:env_gates | 키가 비면 연동 진입이 성립하지 않는다 | 없음 — 키 부재를 메우는 기본값이 이 층에 없다 | 런타임 인덱스의 이 벤더 층이 그 값을 담고, 그 층이 이 정책의 근거다 |
| payment:__env__:mode | 환경값이 없으면 모드가 결정되지 않는다 | 없음 — 동결 트리에 `.env` 계열 파일이 0건이라 기본값을 관측할 수 없다 | 프런트 결제 화면·훅의 환경 변수 참조 지점 |

## 부록

이 절의 정책은 **현행 발효 여부가 판정되지 않아** 본문에서 격리한다. 이 문서의 미판정은 전건
`게이트 미상` 이며 그것이 이 축의 성질이다 — 걸리는 것이 전부 주입 환경값이고, **닫을 경로가 이
PRD 범위 밖이다**. 형제 문서 `01-treat-providers.md` 에는 닫을 경로가 이름을 갖는 `데이터 의존`
칸이 있지만 결제 축에는 그런 층이 없다.

**여기 실린 어떤 정책도 그 규칙이 틀렸다고 말하지 않는다.** 미판정은 부정이 아니다. 규칙 자체는
동결 stg `4d798f55` 의 코드로 확정되며, 판정되지 않은 것은 그 규칙이 지금 걸려 있는가다.

각 블록은 **그 층이 무엇을 정하고 그것이 발효하려면 무엇이 서야 하는가**를 적는다. 좌표는
프론트매터 `anchors` 와 `## 강제 지점 (개발)` 이 진다.

### payment:smartro:timeout

요청 만료 시점을 정하는 층이다. 만료 뒤 호출자가 어느 분기로 가는가는 동결 stg 4d798f55 의 코드로 확정되지만, 그 분기가 발효하려면 이 연동이 먼저 서야 한다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 이 층의 게이트 종류는 `env` 로 재유도됐고 닫을 경로가 이 PRD 범위 밖이다. 만료 값 자체는 이 문서가 옮기지 않는다(30-contracts-limits 소유).

### payment:smartro:fallback

폴백이 없다는 것이 이 층의 내용이다 — 멀티 공급자 폴백은 진료수집 축 전용이고 이 벤더는 디스패처 풀에 없다. **관측된 부재이지 미조사가 아니다.** 그 부재가 운영에서 유지되는지는 이 연동이 실제로 서 있는가에 걸린다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 이 층의 게이트 종류는 `env` 로 재유도됐고 닫을 경로가 이 PRD 범위 밖이다.

### payment:smartro:normalization

정규화 매퍼가 없다는 것이 이 층의 내용이다 — 응답을 그대로 응답 DTO 로 옮기거나 자기 형태를 정본으로 쓴다. **관측된 부재다.** 그 형태가 발효하려면 이 연동이 서야 한다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 이 층의 게이트 종류는 `env` 로 재유도됐고 닫을 경로가 이 PRD 범위 밖이다.

### payment:smartro:exception

호출 실패를 어느 예외 형태로 감싸는가를 정하는 층이다. 예외 클래스 이름과 HTTP 상태는 코드로 확정되지만 그 예외가 던져지려면 이 연동이 서야 한다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 이 층의 게이트 종류는 `none` 로 재유도됐고 닫을 경로가 이 PRD 범위 밖이다. 게이트 종류가 `none` 인 것은 이 층의 앵커 파일에서 DB 토큰도 env 토큰도 관측되지 않았다는 뜻이지 미명세가 아니다.

### payment:smartro:env_gates

이 벤더 연동이 서려면 설정 블록의 환경 키가 주입돼야 한다는 것이 이 층의 내용이다. 키 **이름**은 런타임 인덱스가 열거하지만 값은 저장소 밖이고 이 문서도 이름을 나열하지 않는다(11-index-code · 12-index-runtime 소유). 이 층의 게이트 종류는 `env` 로 재유도됐고 닫을 경로가 이 PRD 범위 밖이다.

### payment:toss:timeout

요청 만료 시점을 정하는 층이다. 만료 뒤 호출자가 어느 분기로 가는가는 동결 stg 4d798f55 의 코드로 확정되지만, 그 분기가 발효하려면 이 연동이 먼저 서야 한다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 이 층의 게이트 종류는 `env` 로 재유도됐고 닫을 경로가 이 PRD 범위 밖이다. 만료 값 자체는 이 문서가 옮기지 않는다(30-contracts-limits 소유).

### payment:toss:fallback

폴백이 없다는 것이 이 층의 내용이다 — 멀티 공급자 폴백은 진료수집 축 전용이고 이 벤더는 디스패처 풀에 없다. **관측된 부재이지 미조사가 아니다.** 그 부재가 운영에서 유지되는지는 이 연동이 실제로 서 있는가에 걸린다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 이 층의 게이트 종류는 `env` 로 재유도됐고 닫을 경로가 이 PRD 범위 밖이다.

### payment:toss:normalization

정규화 매퍼가 없다는 것이 이 층의 내용이다 — 응답을 그대로 응답 DTO 로 옮기거나 자기 형태를 정본으로 쓴다. **관측된 부재다.** 그 형태가 발효하려면 이 연동이 서야 한다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 이 층의 게이트 종류는 `env` 로 재유도됐고 닫을 경로가 이 PRD 범위 밖이다.

### payment:toss:exception

호출 실패를 어느 예외 형태로 감싸는가를 정하는 층이다. 예외 클래스 이름과 HTTP 상태는 코드로 확정되지만 그 예외가 던져지려면 이 연동이 서야 한다 — 그 조건은 이 벤더 설정 블록의 환경 키 주입이고 키 값은 저장소 밖에 있다. 이 층의 게이트 종류는 `none` 로 재유도됐고 닫을 경로가 이 PRD 범위 밖이다. 게이트 종류가 `none` 인 것은 이 층의 앵커 파일에서 DB 토큰도 env 토큰도 관측되지 않았다는 뜻이지 미명세가 아니다.

### payment:toss:env_gates

이 벤더 연동이 서려면 설정 블록의 환경 키가 주입돼야 한다는 것이 이 층의 내용이다. 키 **이름**은 런타임 인덱스가 열거하지만 값은 저장소 밖이고 이 문서도 이름을 나열하지 않는다(11-index-code · 12-index-runtime 소유). 이 층의 게이트 종류는 `env` 로 재유도됐고 닫을 경로가 이 PRD 범위 밖이다.

### payment:__env__:mode

결제 모드와 클라이언트 키가 프런트 코드에서 환경 변수로 읽히고 그 값이 저장소 밖이다. **동결 site 트리에 `.env` 계열 파일이 0건이라** 값이 앵커로 결박되지도 않는다 — 게이트는 코드에 있고 값은 밖에 있다는 미판정의 정확한 형태다. 닫을 경로가 이 PRD 범위 밖이다.

## 연혁

릴리스 태그가 존재하지 않는다. 릴리스 열은 `—` 로 두고 커밋 SHA 로 추적한다. 행은
`docs/_meta/index/commits.server.jsonl` 이 이 문서의 앵커 경로 접촉으로 기록한 커밋 집합 안에서
**연동 조건이 바뀐 시점**만 골랐다.

**이 문서의 연혁은 얇다.** 결제 두 벤더의 서비스·예외 파일을 건드린 커밋이 인덱스 기준으로
많지 않고, 그중 연동 조건을 바꾼 것은 더 적다. 얇은 것이 조사 부족이 아니라 **그 축의 코드가
오래 안정돼 있었다는 관측**이며, 그 사실을 행 수로 부풀리지 않는다.

| 릴리스 | 날짜 | SHA | 변경 요약 |
|---|---|---|---|
| — | 2026-01-29 | `f688368c` | 두 결제 벤더가 공통 라이브러리 경로로 합쳐진다 — 예외 형태가 벤더별 클래스로 정착한 시점 |
| — | 2026-07-06 | `3f23349d` | 정기결제 벤더 오류 중 일부만 사용자에게 노출하는 분기가 생긴다 — 노출 집합이 코드 상수가 된 시점 |
