pharmacy-pos-qr-system/backend/SOOIN_API_REVERSE_ENGINEERING.md
thug0bin c1596a6d35 feat: 도매상 API 통합 및 스키마 업데이트
- wholesale 패키지 연동 (SooinSession, GeoYoungSession)
- Flask Blueprint 분리 (sooin_api.py, geoyoung_api.py)
- order_context 스키마 확장 (wholesaler_id, internal_code 등)
- 수인약품 개별 취소 기능 (cancel_item, restore_item)
- 문서 추가: WHOLESALE_API_INTEGRATION.md
- 테스트 스크립트들
2026-03-06 11:50:46 +09:00

278 lines
6.9 KiB
Markdown

# 수인약품 API 리버스 엔지니어링 문서
## 개요
수인약품 웹 주문 시스템의 API 구조를 분석한 문서입니다.
지오영 API와 같은 하이브리드 방식 (Playwright 로그인 → requests 직접 호출)으로 구현합니다.
## 기본 정보
- **Base URL**: `http://sooinpharm.co.kr`
- **인코딩**: EUC-KR (한글 파라미터 인코딩 시 주의)
- **거래처 코드**: `50911` (청춘약국)
- **세션 관리**: 쿠키 기반 (ASP 세션)
---
## 1. 로그인
### 로그인 페이지
- **URL**: `/Homepage/intro.asp`
- **Method**: POST (JavaScript 함수 `chkLogin()` 호출)
### 필드
| 필드명 | 설명 | 예시 |
|--------|------|------|
| tx_id | 아이디 | thug0bin |
| tx_pw | 비밀번호 | @Trajet6640 |
### 인증 쿠키
로그인 성공 시 ASP 세션 쿠키가 발급됨:
- `ASPSESSIONID*` (세션 ID)
### 로그인 성공 확인
- 로그인 후 페이지에 "로그아웃" 링크 존재 여부로 확인
- 로그인 후 자동으로 `/Service/Order/Order.asp`로 리다이렉트
---
## 2. 제품 검색 API
### URL
```
GET /Service/Order/Order.asp
```
### 파라미터
| 파라미터 | 필수 | 설명 | 값 예시 |
|----------|------|------|---------|
| so | N | 제품분류 | 0=전체, 1=전문, 2=일반 |
| so2 | N | 주문분류 | 0=전체, 1=다빈도, 2=관심, 3=재주문 |
| so3 | N | 검색타입 | **1=제품명, 2=KD코드, 3=표준코드** |
| tx_maker | N | 제조사 | 한독 |
| tx_physic | N | 검색어 | 073100220 (KD코드) |
| tx_ven | Y | 거래처코드 | 50911 |
| currVenNm | Y | 약국명 | 청춘약국 (URL인코딩) |
| sDate | N | 시작일 | 20260306 |
| eDate | N | 종료일 | 20260306 |
| sa | N | 정렬 | phy=제품명순, ven=제조사순 |
| Page | N | 페이지번호 | 1 |
| tx_StockLoc | N | 재고위치 | '00001' |
| df | N | 기간필터 | t=3개월 |
### KD코드 검색 예시 URL
```
/Service/Order/Order.asp?so=0&so2=0&so3=2&tx_physic=073100220&tx_ven=50911&currVenNm=%EC%B2%AD%EC%B6%98%EC%95%BD%EA%B5%AD&sDate=20260306&eDate=20260306&df=t
```
### 응답 (HTML)
HTML 테이블 형식으로 반환. BeautifulSoup로 파싱 필요.
#### 테이블 구조
```html
<tr class="ln_physic">
<td>073100220</td> <!-- KD코드 -->
<td>한국오가논</td> <!-- 제조사 -->
<td>
<a href="./PhysicInfo.asp?pc=32495&...">
(오가논)코자정 50mg(PTP)
</a>
</td> <!-- 제품명 (pc=내부코드) -->
<td>30T</td> <!-- 규격 -->
<td>보험전문</td> <!-- 구분 -->
<td>14,220</td> <!-- 단가 -->
<td>238</td> <!-- 재고 -->
<td>
<input name="qty_0"> <!-- 수량입력 -->
<input type="hidden" name="pc_0" value="32495"> <!-- 내부코드 -->
<input type="hidden" name="stock_0" value="238">
<input type="hidden" name="price_0" value="14220">
</td>
</tr>
```
#### 핵심 필드 추출
- **KD코드**: 첫 번째 td
- **제조사**: 두 번째 td
- **제품명**: 세 번째 td의 a 태그 텍스트
- **내부코드(pc)**: a 태그 href에서 `pc=xxxxx` 추출
- **규격**: 네 번째 td
- **단가**: 여섯 번째 td (콤마 제거 후 int)
- **재고**: 일곱 번째 td
---
## 3. 장바구니 추가 API
### URL
```
POST /Service/Order/BagOrder.asp
```
### Content-Type
```
application/x-www-form-urlencoded
```
### 파라미터 (각 제품당)
| 파라미터 | 설명 | 예시 |
|----------|------|------|
| qty_N | 수량 | 1 |
| pc_N | 내부 제품코드 | 32495 |
| stock_N | 현재 재고 | 238 |
| saleqty_N | 판매수량 | 0 |
| price_N | 단가 | 14220 |
| soldout_N | 품절여부 | N |
| ordunitqty_N | 주문단위수량 | 1 |
| bidqty_N | 입찰수량 | 0 |
| outqty_N | 출고수량 | 0 |
| overqty_N | 초과수량 | 0 |
| manage_N | 관리여부 | N |
| prodno_N | 제품번호 | (빈값) |
| termdt_N | 종료일자 | (빈값) |
> N은 0부터 시작하는 행 인덱스
### 요청 예시
```
qty_0=1&pc_0=32495&stock_0=238&saleqty_0=0&price_0=14220&soldout_0=N&ordunitqty_0=1&bidqty_0=0&outqty_0=0&overqty_0=0&manage_0=N&prodno_0=&termdt_0=
```
### 응답
HTML (장바구니 iframe 내용)
---
## 4. 장바구니 비우기 API
### URL
```
GET /Service/Order/BagOrder.asp?kind=del&currVenCd=50911&currMkind=&currRealVenCd=
```
### 파라미터
| 파라미터 | 설명 | 값 |
|----------|------|-----|
| kind | 동작 | del |
| currVenCd | 거래처코드 | 50911 |
| currMkind | 종류 | (빈값) |
| currRealVenCd | 실제거래처코드 | (빈값) |
---
## 5. 장바구니 조회 API
### URL
```
GET /Service/Order/BagOrder.asp?currVenCd=50911
```
### 응답 (HTML)
```html
<table class="tbl_list">
<tr>
<td>건별취소</td>
<td>제품명</td>
<td>수량</td>
<td>금액</td>
</tr>
<tr>
<td><a href="...">X</a></td>
<td>(오가논)코자정 50mg(PTP)</td>
<td>1</td>
<td>14,220</td>
</tr>
</table>
<div>
<dt>주문품목</dt><dd>1개</dd>
<dt>주문금액</dt><dd>14,220원</dd>
</div>
```
---
## 6. 주문 전송 API
### URL (추정)
```
POST /Service/Order/BagOrder.asp
```
### 파라미터
| 파라미터 | 설명 |
|----------|------|
| kind | order (추정) |
| memo | 주문메모 |
| currVenCd | 거래처코드 |
> 실제 주문 전송은 iframe 내 버튼 클릭으로 수행됨
> 정확한 API 파라미터는 추가 분석 필요
---
## 7. 제품 상세 정보 API
### URL
```
GET /Service/Order/PhysicInfo.asp
```
### 파라미터
| 파라미터 | 설명 | 예시 |
|----------|------|------|
| pc | 내부제품코드 | 32495 |
| ln | 행번호 | 0 |
| currVenCd | 거래처코드 | 50911 |
| currLoc | 재고위치 | '00001' |
---
## 구현 전략
### 지오영 API 패턴 적용
1. **Playwright 로그인**
- 초기 로그인만 Playwright 사용
- 쿠키 획득 후 requests 세션에 복사
- 세션 30분 유효 (재로그인 필요 시 자동 갱신)
2. **requests 직접 호출**
- 검색: GET /Service/Order/Order.asp
- 장바구니 추가: POST /Service/Order/BagOrder.asp
- 장바구니 비우기: GET /Service/Order/BagOrder.asp?kind=del
- 장바구니 조회: GET /Service/Order/BagOrder.asp
3. **HTML 파싱**
- BeautifulSoup 사용
- 테이블 행에서 제품 정보 추출
- 내부코드(pc) 추출 (장바구니 추가용)
### 예상 성능
- 기존 Playwright: ~30초/주문
- requests 직접 호출: **~1초/주문**
---
## 주의사항
1. **EUC-KR 인코딩**
- 한글 파라미터는 EUC-KR로 인코딩
- `urllib.parse.quote(text.encode('euc-kr'))`
2. **세션 관리**
- ASP 세션 쿠키 유지 필수
- 장시간 미사용 시 세션 만료
3. **동시 접속**
- 동일 계정 동시 접속 시 세션 충돌 가능
4. **재고 실시간성**
- 검색 시점의 재고 정보
- 주문 전 재고 재확인 권장
---
## 작성일
- 2026-03-06
- 리버스 엔지니어링 by Claude