k-skill/express-bus-booking/references/kobus-http-flow.md
Jeffrey (Dongkyu) Kim 5da1f0e240 Make KOBUS seat holds reproducible for checkout handoff
Capture the verified KOBUS non-member flow in a reusable helper that searches schedules, creates a temporary seat hold, saves an official payment-page autosubmit helper, and records cancellation fields.

Constraint: KOBUS requires session-backed POST fields and returns pcpyNoAll/satsNoAll from setPcpy.ajax before checkout entry.
Rejected: Opening payment page by URL alone | stplcfmpym.do requires the selected schedule, fare, seat, and hold POST body.
Confidence: high
Scope-risk: narrow
Directive: Never submit card/payment fields automatically; cancel abandoned holds with cancPcpy.ajax.
Tested: python3 -m py_compile express-bus-booking/scripts/kobus_express_booking.py; live 서울 센트럴시티(021)→광주 유스퀘어(500) 20260520 --hold-first-seat returned MSG_CD=S0000 pcpyNoAll and rendered payment-info page; /mrs/cancPcpy.ajax returned MSG_CD=S0000; ./scripts/validate-skills.sh
Not-tested: final payment submission, mobile in-app browser behavior, mixed passenger discounts
Co-authored-by: OpenAI Codex <codex@openai.com>
Co-authored-by: OmX <omx@oh-my-codex.local>
2026-05-13 16:37:10 +09:00

3.2 KiB

KOBUS HTTP/API Probe Notes

Session-proven on 2026-05-08. Goal: avoid browser automation where possible.

Base

https://www.kobus.co.kr

Use a desktop User-Agent, HTTP/1.1 if needed, a cookie jar, and referers.

Tested Flow

Route / Terminal Candidates

POST /mrs/readRotLinInf.ajax

Observed JSON keys:

tfrLen
tfrInfList
len
codeYn
rotInfList

One probe returned about 1,208 route records in rotInfList.

Timetable

POST /mrs/alcnSrch.do

Example tested route/date:

서울경부(010) -> 부산(700), 2026-05-09

Observed result:

42 schedule links/cards
25 selectable seat snippets
first selectable: 00:30 / 천일고속 / 심야우등 / 10 seats

Typical POST fields:

deprCd=010
arvlCd=700
pathDvs=sngl
pathStep=1
deprDtm=YYYYMMDD
busClsCd=0
rtrpChc=1
timeLinkMin=00
timeLinkMax=23

Parse fnSatsChc(...) onclick values for the next step. Example:

fnSatsChc('20260509','003000','003000','010','700','3','07','0','Y','N','010','700','N','N','N','N')

Seat / Fare Stage

POST /mrs/satschc.do

Send the original alcnSrchFrm hidden fields plus selected values from fnSatsChc(...), including values such as:

deprTime=003000
alcnDeprTime=003000
alcnDeprTrmlNo=010
alcnArvlTrmlNo=700
indVBusClsCd=3
cacmCd=07
dcDvsCd=0
prvtBbizEmpAcmtRt=N
chldSftySatsYn=N
dsprSatsYn=N

Observed response contained form#satsChcFrm and hidden values:

{
  "deprTime": "003000",
  "alcnDeprTrmlNo": "010",
  "alcnArvlTrmlNo": "700",
  "adltFee": "47600",
  "rmnSatsNum": "10",
  "totSatsNum": "28"
}

Temporary Hold

POST /mrs/setPcpy.ajax

Observed successful response markers:

MSG_CD=S0000
pcpyNoAll
satsNoAll
ESTM_AMT
DC_AMT
TISSU_AMT

Hold Cancellation

POST /mrs/cancPcpy.ajax

Observed success marker:

MSG_CD=S0000

2026-05-13 서울→광주 re-verification: 센트럴시티(서울)(021) -> 광주(유·스퀘어)(500), 2026-05-20 00:45 중앙고속 심야우등, seat 1. /mrs/setPcpy.ajax returned MSG_CD=S0000, pcpyNoAll, satsNoAll=01, TISSU_AMT=36900; /mrs/stplcfmpym.do?keep=/mrs/pay rendered the official payment-information page; /mrs/cancPcpy.ajax returned MSG_CD=S0000.

Checkout Entry

POST /mrs/stplcfmpym.do

The POST body must include the selected schedule/seat form values plus temporary hold identifiers and fare amounts. A helper page can auto-submit this form to the official KOBUS endpoint.

For mobile browsers, use:

POST /mrs/stplcfmpym.do?keep=/mrs/pay

This preserves the POST body while placing /mrs/pay in location.href, which avoids a KOBUS client-side mobile redirect condition observed in the common JavaScript.

Interpretation

  • Login was not required for route lookup, timetable lookup, seat-selection-page entry, temporary hold, or checkout-entry page display in the tested flow.
  • Page HTML can include login or grecaptchaToken forms, but these did not block the tested lookup/seat-stage path.
  • Final payment should remain a manual, explicitly confirmed stage.
  • KOBUS mobile behavior is less stable than desktop because common JavaScript can redirect narrow screens to the mobile main page.