Wave G: 실제 매입사 경제모델 고도화 (딥리서치 갭분석 기반)

딥리서치(95 에이전트, OCC/Fed/Berlin Group 등 1차 출처, 검증된 9 finding)로
실제 카드 매입사 아키텍처를 조사(docs/GAP_ANALYSIS.md)하고, 최우선 갭 3개를
스키마+시드+런타임서비스+화면으로 메웠다.

메운 갭 (실제 매입사 핵심 메커니즘)
1. 수수료 3층 분해: 기존 flat MDR → interchange(발급사)+scheme fee(카드망)+
   markup(매입사 수익). 실측 결과 interchange=MDR의 86% (리서치 "70~90%"와 일치).
2. 클리어링 ≠ 정산 분리: 승인·정산과 별도인 프레젠트먼트(클리어링) 단계 추가.
3. 발급사↔매입사 순액정산: BIN/ICA 기준 다자 순액(한국 BC카드 컨소시엄형).
4. 펀딩 항등식: 총매출−환불−채그백−수수료−리저브=입금 (2,924건 전부 항등 성립).
5. 채그백/분쟁 라이프사이클: FIRST_CB→REPRESENT→PRE_ARB→ARBITRATION 상태기계.

스키마 (db/schema.d/70-econ.sql, 96-econ-seed.sql)
- 신규 9테이블: st_interchange_rate, st_scheme_fee, st_fee_component, cl_presentment,
  st_member_bank, st_net_settlement, st_reserve, st_merchant_funding, ac_dispute.
- 기존 정산건 소급 분해(요율표 카드종류×채널 차등) + 순액정산 배치 + 펀딩 계산.
- 시드 계산검증: 3층 항등식·펀딩 항등식 100% 성립(내가 채그백 일수중복 버그 1건 수정).

런타임 서비스 (전부 게이트웨이로 실호출 검증)
- st: ST_FEEDECOMP(ic+sf+markup=mdr), ST_NETSETTLE(BIN별 순액), ST_FUNDING(항등식)
- ac: ACQ_DISPUTE_OPEN, ACQ_DISPUTE_ADVANCE(상태기계-부적법 전이 거부),
  ACQ_PRESENT(클리어링). 각 120줄+, ECPG/XA 격리 규율 준수.

화면 (포털 AR0003 "정산 경제", #econ)
- 수수료 3층 색상바 + 펀딩 항등식 워터폴 + 발급사별 순액정산 + 분쟁 라이프사이클
  + 클리어링. 전부 실측 데이터 조회.

검증
- 통합 빌드 error 0, 도메인 재기동 5,879 서비스 AVAIL
- 6개 신규 서비스 실호출 정상(수수료 3층 84=74+6+4, 상태기계 부적법 전이 거부)
- 기존 6단 매입 체인 COMMIT OK 유지, 미결 2PC 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hyeongwoo 2026-07-22 14:03:35 +09:00
parent 46ff41b633
commit 438ae8992d
20 changed files with 1500 additions and 4 deletions

148
db/schema.d/70-econ.sql Normal file
View file

@ -0,0 +1,148 @@
-- ===========================================================================
-- 70-econ.sql : 실제 매입사 경제모델 (딥리서치 갭분석 고도화)
-- 수수료 3층 분해(interchange+scheme fee+acquirer markup),
-- 클리어링/프레젠트먼트(정산과 분리), 발급사↔매입사 순액정산(BIN/ICA·회원사),
-- 정산 항등식(총매출−환불−채그백−수수료−리저브=입금), 채그백/분쟁 라이프사이클.
-- 기존 flat MDR 은 markup 으로 재해석하여 하위호환.
-- ===========================================================================
-- ---------------------------------------------------------------------------
-- 1. Interchange 요율표 (발급사 귀속분). 카드종류×MCC×채널×거래유형 차등.
-- 실제 매입사에서 요율이 갈리는 축을 그대로 반영(interchange qualification).
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS st_interchange_rate (
card_type TEXT NOT NULL, -- CREDIT/CHECK/PREPAID
mcc TEXT NOT NULL DEFAULT 'DEFAULT',-- 업종(가맹점 category 매핑)
channel TEXT NOT NULL DEFAULT 'DEFAULT',-- POS/EDC/EDI/FOREIGN
txn_type TEXT NOT NULL DEFAULT 'SALE', -- SALE/CASH/REFUND
rate_bps BIGINT NOT NULL DEFAULT 0, -- basis points (1bp=0.01%)
fixed_fee BIGINT NOT NULL DEFAULT 0, -- 건당 고정수수료(원)
eff_date DATE NOT NULL DEFAULT current_date,
PRIMARY KEY (card_type, mcc, channel, txn_type)
);
-- ---------------------------------------------------------------------------
-- 2. Scheme fee (카드망 assessment). 스킴×거래유형.
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS st_scheme_fee (
scheme TEXT NOT NULL, -- BC/VISA/MASTER/LOCAL
txn_type TEXT NOT NULL DEFAULT 'SALE',
rate_bps BIGINT NOT NULL DEFAULT 0,
fixed_fee BIGINT NOT NULL DEFAULT 0,
PRIMARY KEY (scheme, txn_type)
);
-- ---------------------------------------------------------------------------
-- 3. 거래별 수수료 분해 (MDR = interchange + scheme_fee + markup).
-- 매입사 손익: markup 만 매입사 수익, interchange 는 발급사, scheme_fee 는 카드망.
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS st_fee_component (
purchase_id BIGINT PRIMARY KEY,
amount BIGINT NOT NULL,
interchange BIGINT NOT NULL DEFAULT 0, -- 발급사 귀속
scheme_fee BIGINT NOT NULL DEFAULT 0, -- 카드망 귀속
markup BIGINT NOT NULL DEFAULT 0, -- 매입사 수익(협상대상)
mdr_total BIGINT NOT NULL DEFAULT 0, -- = interchange+scheme_fee+markup
card_type TEXT NOT NULL DEFAULT 'CREDIT',
scheme TEXT NOT NULL DEFAULT 'BC',
computed_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- ---------------------------------------------------------------------------
-- 4. 프레젠트먼트(클리어링). 승인·정산과 분리된 단계.
-- 클리어링=데이터 교환(발급사↔매입사 채무 확정). 정산=자금이동.
-- ---------------------------------------------------------------------------
CREATE SEQUENCE IF NOT EXISTS cl_present_seq START 700000;
CREATE TABLE IF NOT EXISTS cl_presentment (
presentment_id BIGINT PRIMARY KEY,
purchase_id BIGINT NOT NULL,
scheme TEXT NOT NULL DEFAULT 'BC',
msg_class TEXT NOT NULL DEFAULT 'FIRST_PRESENT', -- FIRST_PRESENT/REPRESENT/CHARGEBACK
clearing_amt BIGINT NOT NULL, -- 클리어링 대상 금액(net of interchange)
present_date DATE NOT NULL,
cycle TEXT NOT NULL DEFAULT 'T+1', -- 정산주기
status TEXT NOT NULL DEFAULT 'CLEARED', -- CLEARED/REJECTED/PENDING
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- ---------------------------------------------------------------------------
-- 5. 회원사(정산 참가 은행/발급사) — 한국 BC카드형 다자간 순액정산.
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS st_member_bank (
bank_code TEXT PRIMARY KEY, -- BC/KB/SHINHAN/...
bank_name TEXT NOT NULL,
bin TEXT NOT NULL, -- 스킴 회원식별자(Visa BIN / MC ICA 대응)
role TEXT NOT NULL DEFAULT 'BOTH' -- ISSUER/ACQUIRER/BOTH
);
-- ---------------------------------------------------------------------------
-- 6. 순액정산 배치 (발급사↔매입사). BIN/ICA 기준 차대변 합산 → 단일 순액.
-- ---------------------------------------------------------------------------
CREATE SEQUENCE IF NOT EXISTS st_netset_seq START 800000;
CREATE TABLE IF NOT EXISTS st_net_settlement (
net_id BIGINT PRIMARY KEY,
scheme TEXT NOT NULL DEFAULT 'BC',
member_bin TEXT NOT NULL, -- 상대 회원사 BIN/ICA
biz_date DATE NOT NULL,
txn_count BIGINT NOT NULL DEFAULT 0,
gross_amt BIGINT NOT NULL DEFAULT 0,
interchange_amt BIGINT NOT NULL DEFAULT 0,
scheme_fee_amt BIGINT NOT NULL DEFAULT 0,
net_amt BIGINT NOT NULL DEFAULT 0, -- 실제 이체 순액(부호=방향)
direction TEXT NOT NULL DEFAULT 'RECEIVE', -- RECEIVE(수취)/PAY(지급)
status TEXT NOT NULL DEFAULT 'SETTLED',
settled_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (scheme, member_bin, biz_date)
);
-- ---------------------------------------------------------------------------
-- 7. 롤링 리저브(유보금).
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS st_reserve (
merchant_id TEXT NOT NULL,
biz_date DATE NOT NULL,
reserve_bps BIGINT NOT NULL DEFAULT 0, -- 유보율
reserve_amt BIGINT NOT NULL DEFAULT 0, -- 이번 회차 유보액
release_date DATE, -- 유보 해제 예정일(롤링)
released BOOLEAN NOT NULL DEFAULT false,
PRIMARY KEY (merchant_id, biz_date)
);
-- ---------------------------------------------------------------------------
-- 8. 가맹점 펀딩 항등식: 총매출 환불 채그백 수수료 리저브 = 입금.
-- ---------------------------------------------------------------------------
CREATE SEQUENCE IF NOT EXISTS st_funding_seq START 900000;
CREATE TABLE IF NOT EXISTS st_merchant_funding (
funding_id BIGINT PRIMARY KEY,
merchant_id TEXT NOT NULL,
biz_date DATE NOT NULL,
gross_sales BIGINT NOT NULL DEFAULT 0,
refunds BIGINT NOT NULL DEFAULT 0,
chargebacks BIGINT NOT NULL DEFAULT 0,
fees BIGINT NOT NULL DEFAULT 0, -- MDR 합계
reserve BIGINT NOT NULL DEFAULT 0,
deposit BIGINT NOT NULL DEFAULT 0, -- 실제 입금액
funding_model TEXT NOT NULL DEFAULT 'NET_DAILY', -- NET_DAILY/MONTHLY_DISCOUNT
status TEXT NOT NULL DEFAULT 'FUNDED',
funded_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (merchant_id, biz_date)
);
-- ---------------------------------------------------------------------------
-- 9. 채그백/분쟁 라이프사이클: 1차CB → representment → pre-arb → arbitration.
-- ---------------------------------------------------------------------------
CREATE SEQUENCE IF NOT EXISTS ac_dispute_seq START 600000;
CREATE TABLE IF NOT EXISTS ac_dispute (
dispute_id BIGINT PRIMARY KEY,
purchase_id BIGINT NOT NULL,
merchant_id TEXT NOT NULL,
reason_code TEXT NOT NULL, -- 10.4(사기) / 13.1(상품미수취) / 12.6(중복) ...
amount BIGINT NOT NULL,
stage TEXT NOT NULL DEFAULT 'FIRST_CB', -- FIRST_CB/REPRESENT/PRE_ARB/ARBITRATION
status TEXT NOT NULL DEFAULT 'OPEN', -- OPEN/WON/LOST/ACCEPTED
opened_at TIMESTAMPTZ NOT NULL DEFAULT now(),
resolved_at TIMESTAMPTZ,
memo TEXT
);
CREATE INDEX IF NOT EXISTS ix_dispute_pid ON ac_dispute(purchase_id);
CREATE INDEX IF NOT EXISTS ix_present_pid ON cl_presentment(purchase_id);