5858KST = ZoneInfo ("Asia/Seoul" )
5959FINAL_SUBMIT_ENABLE_ENV = "TOSS_BRIDGE_ENABLE_FINAL_SUBMIT"
6060FINAL_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
61102SUMMARY_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