Skip to content

Commit 4bfe3fe

Browse files
heehoclaude
andcommitted
feat(daemon): P1-02 reject taxonomy enum + 분기 정밀화
`BROKER_ACK_OK` + `BROKER_REJECTED_*` 8건 (`_INSUFFICIENT_BALANCE`, `_INSUFFICIENT_QUANTITY`, `_INVALID_PRICE`, `_MARKET_CLOSED`, `_AUTH_REQUIRED`, `_DUPLICATE_ORDER`, `_TIMEOUT`, `_HTTP_ERROR`, `_UNKNOWN`) 모듈-레벨 enum + `BROKER_ACK_REJECT_CODES` frozenset 정의. `classify_broker_reject(message, status_code, error)` 함수 신규 — error 존재 → TIMEOUT, status_code 408/504 → TIMEOUT, 401/403 → AUTH_REQUIRED, ≥500 → HTTP_ERROR, fallback UNKNOWN. 한글 message → 의미 enum 매핑은 P0-03 backlog (Phase 6 supervised 자연 capture 후 점진 추가). `_run_broker_create` 분기 정밀화: P1-01 의 `result.get("status")` 필드명 오타 수정 (실제 `_fetch_many` 응답은 `status_code`), timeout RuntimeError try/except 추가, reject 분기에서 `classify_broker_reject` 결과 사용. baseline 12 tests PASS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 05c15e7 commit 4bfe3fe

1 file changed

Lines changed: 92 additions & 14 deletions

File tree

src/toss_browser_bridge/daemon.py

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,47 @@
5858
KST = ZoneInfo("Asia/Seoul")
5959
FINAL_SUBMIT_ENABLE_ENV = "TOSS_BRIDGE_ENABLE_FINAL_SUBMIT"
6060
FINAL_SUBMIT_TEST_BYPASS_ENV = "TOSS_BRIDGE_ALLOW_TEST_FINAL_SUBMIT"
61+
62+
BROKER_ACK_OK = "OK"
63+
BROKER_REJECTED_INSUFFICIENT_BALANCE = "BROKER_REJECTED_INSUFFICIENT_BALANCE"
64+
BROKER_REJECTED_INSUFFICIENT_QUANTITY = "BROKER_REJECTED_INSUFFICIENT_QUANTITY"
65+
BROKER_REJECTED_INVALID_PRICE = "BROKER_REJECTED_INVALID_PRICE"
66+
BROKER_REJECTED_MARKET_CLOSED = "BROKER_REJECTED_MARKET_CLOSED"
67+
BROKER_REJECTED_AUTH_REQUIRED = "BROKER_REJECTED_AUTH_REQUIRED"
68+
BROKER_REJECTED_DUPLICATE_ORDER = "BROKER_REJECTED_DUPLICATE_ORDER"
69+
BROKER_REJECTED_TIMEOUT = "BROKER_REJECTED_TIMEOUT"
70+
BROKER_REJECTED_HTTP_ERROR = "BROKER_REJECTED_HTTP_ERROR"
71+
BROKER_REJECTED_UNKNOWN = "BROKER_REJECTED_UNKNOWN"
72+
73+
BROKER_ACK_REJECT_CODES = frozenset({
74+
BROKER_REJECTED_INSUFFICIENT_BALANCE,
75+
BROKER_REJECTED_INSUFFICIENT_QUANTITY,
76+
BROKER_REJECTED_INVALID_PRICE,
77+
BROKER_REJECTED_MARKET_CLOSED,
78+
BROKER_REJECTED_AUTH_REQUIRED,
79+
BROKER_REJECTED_DUPLICATE_ORDER,
80+
BROKER_REJECTED_TIMEOUT,
81+
BROKER_REJECTED_HTTP_ERROR,
82+
BROKER_REJECTED_UNKNOWN,
83+
})
84+
85+
86+
def classify_broker_reject(message: str | None, status_code: int, error: str | None) -> str:
87+
"""Map broker create error response to BROKER_REJECTED_* enum.
88+
89+
Skeleton implementation — Phase 0 P0-02 capture had no reject responses.
90+
Concrete Korean message → enum mappings will be appended as supervised
91+
Phase 6 captures surface real reject payloads (P0-03 backlog).
92+
"""
93+
if error:
94+
return BROKER_REJECTED_TIMEOUT
95+
if status_code in (408, 504):
96+
return BROKER_REJECTED_TIMEOUT
97+
if status_code in (401, 403):
98+
return BROKER_REJECTED_AUTH_REQUIRED
99+
if status_code >= 500:
100+
return BROKER_REJECTED_HTTP_ERROR
101+
return BROKER_REJECTED_UNKNOWN
61102
SUMMARY_ENDPOINTS = [
62103
{
63104
"name": "account_overview",
@@ -2090,9 +2131,6 @@ def _run_broker_create(
20902131
"include_app_version": True,
20912132
"body": create_payload,
20922133
}
2093-
results = self._fetch_many([create_request])
2094-
context = self._make_context(results)
2095-
result = results[0]
20962134

20972135
ordered_at = now_kst()
20982136
base_ack = {
@@ -2103,32 +2141,72 @@ def _run_broker_create(
21032141
"order_type": order_type_label,
21042142
}
21052143

2106-
if not result.get("ok"):
2107-
error = result.get("error") or {}
2144+
try:
2145+
results = self._fetch_many([create_request])
2146+
except RuntimeError as exc:
21082147
return (
21092148
{
21102149
**base_ack,
21112150
"status": "broker_rejected",
2112-
"code": "BROKER_REJECTED_UNKNOWN",
2113-
"message": str(error.get("message") or "broker create request failed"),
2151+
"code": BROKER_REJECTED_TIMEOUT,
2152+
"message": str(exc),
21142153
"ordered_at": ordered_at,
2115-
"http_status": result.get("status"),
2154+
"http_status": 0,
21162155
},
2117-
context,
2156+
self._make_context([]),
21182157
)
21192158

2159+
context = self._make_context(results)
2160+
result = results[0]
2161+
status_code = int(result.get("status_code") or 0)
21202162
body = result.get("json") or {}
21212163
broker_result = body.get("result") or {}
2164+
2165+
if not result.get("ok"):
2166+
fetch_error = result.get("error")
2167+
response_message = (
2168+
broker_result.get("message")
2169+
or body.get("message")
2170+
or fetch_error
2171+
or "broker create request failed"
2172+
)
2173+
code = classify_broker_reject(
2174+
message=str(response_message),
2175+
status_code=status_code,
2176+
error=fetch_error,
2177+
)
2178+
return (
2179+
{
2180+
**base_ack,
2181+
"status": "broker_rejected",
2182+
"code": code,
2183+
"message": str(response_message),
2184+
"ordered_at": ordered_at,
2185+
"http_status": status_code,
2186+
},
2187+
context,
2188+
)
2189+
21222190
order_id = str(broker_result.get("orderId") or "").strip()
21232191
if not order_id:
2192+
response_message = (
2193+
broker_result.get("message")
2194+
or body.get("message")
2195+
or "broker create response missing orderId"
2196+
)
2197+
code = classify_broker_reject(
2198+
message=str(response_message),
2199+
status_code=status_code,
2200+
error=None,
2201+
)
21242202
return (
21252203
{
21262204
**base_ack,
21272205
"status": "broker_rejected",
2128-
"code": "BROKER_REJECTED_UNKNOWN",
2129-
"message": str(broker_result.get("message") or body.get("message") or "broker create response missing orderId"),
2206+
"code": code,
2207+
"message": str(response_message),
21302208
"ordered_at": ordered_at,
2131-
"http_status": result.get("status"),
2209+
"http_status": status_code,
21322210
},
21332211
context,
21342212
)
@@ -2137,14 +2215,14 @@ def _run_broker_create(
21372215
{
21382216
**base_ack,
21392217
"status": "submitted",
2140-
"code": "OK",
2218+
"code": BROKER_ACK_OK,
21412219
"message": str(broker_result.get("message") or ""),
21422220
"broker_order_id": order_id,
21432221
"order_no": broker_result.get("orderNo"),
21442222
"order_date": broker_result.get("orderDate"),
21452223
"is_reserved": bool(broker_result.get("isReserved") or False),
21462224
"ordered_at": ordered_at,
2147-
"http_status": result.get("status"),
2225+
"http_status": status_code,
21482226
},
21492227
context,
21502228
)

0 commit comments

Comments
 (0)