Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

결제기능 추가 #249

Merged
merged 11 commits into from
Sep 26, 2023
1 change: 1 addition & 0 deletions SRT/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,5 @@
"ticket_info": f"{SRT_MOBILE}/ard/selectListArd02017_n.do?",
"cancel": f"{SRT_MOBILE}/ard/selectListArd02045_n.do",
"standby_option": f"{SRT_MOBILE}/ata/selectListAta01135_n.do",
"payment": f"{SRT_MOBILE}/ata/selectListAta09036_n.do",
}
84 changes: 84 additions & 0 deletions SRT/srt.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import re
from datetime import datetime, timedelta

Expand Down Expand Up @@ -126,6 +127,7 @@ def login(self, srt_id: str | None = None, srt_pw: str | None = None):
raise SRTLoginError(r.json()["MSG"])

self.is_login = True
self.membership_number = json.loads(r.text).get("userMap").get("MB_CRD_NO")

return True

Expand All @@ -144,6 +146,8 @@ def logout(self) -> bool:
raise SRTResponseError(r.text)

self.is_login = False
Monsteel marked this conversation as resolved.
Show resolved Hide resolved
self.membership_number = None

return True

def search_train(
Expand Down Expand Up @@ -571,3 +575,83 @@ def cancel(self, reservation: SRTReservation | int) -> bool:
self._log(parser.message())

return True

def payment(
self,
reservation: SRTReservation,
card_number: str,
card_password: str,
card_validation_number: str,
card_expire_date: str,
card_installment: int = 0,
card_type: str = "J",
Monsteel marked this conversation as resolved.
Show resolved Hide resolved
) -> bool:
"""결제합니다.

>>> reservation = srt.reserve(train)
>>> srt.payment(reservation, "1234567890123456", "12", "981204", "2309", 0, "J")

Args:
reservation (:class:`SRTReservation`): 예약 내역
card_number (str): 결제신용카드번호 (하이픈(-) 제외)
card_password (str): 카드비밀번호 앞 2자리
card_validation_number (str): 생년월일 (card_type이 J인 경우) || 사업자번호 (card_type이 S인 경우)
card_expire_date (str): 카드유효기간(YYMM)
card_installment (int): 할부선택 (0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 24)
card_type (str): 카드타입 (J : 개인, S : 법인)

Returns:
bool: 결제 성공 여부
"""
if not self.is_login:
raise SRTNotLoggedInError()

url = constants.API_ENDPOINTS["payment"]

data = {
"stlDmnDt": datetime.now().strftime("%Y%m%d"), # 날짜 (yyyyMMdd)
"mbCrdNo": self.membership_number, # 회원번호
"stlMnsSqno1": "1", # 결제수단 일련번호1 (1고정값인듯)
"ststlGridcnt": "1", # 결제수단건수 (1고정값인듯)
"totNewStlAmt": reservation.total_cost, # 총 신규 결제금액
"athnDvCd1": card_type, # 카드타입 (J : 개인, S : 법인)
"vanPwd1": card_password, # 카드비밀번호 앞 2자리
"crdVlidTrm1": card_expire_date, # 카드유효기간(YYMM)
"stlMnsCd1": "02", # 결제수단코드1: (02:신용카드, 11:전자지갑, 12:포인트)
"rsvChgTno": "0", # 예약변경번호 (0 고정값인듯)
"chgMcs": "0", # 변경마이크로초 (0고정값인듯)
"ismtMnthNum1": card_installment, # 할부선택 (0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 24)
"ctlDvCd": "3102", # 조정구분코드(3102 고정값인듯)
"cgPsId": "korail", # korail 고정
"pnrNo": reservation.reservation_number, # 예약번호
"totPrnb": reservation.seat_count, # 승차인원
"mnsStlAmt1": reservation.total_cost, # 결제금액1
"crdInpWayCd1": "@", # 카드입력방식코드 (@: 신용카드/ok포인트, "": 전자지갑)
"athnVal1": card_validation_number, # 생년월일/사업자번호
"stlCrCrdNo1": card_number, # 결제신용카드번호1
"jrnyCnt": "1", # 여정수(1 고정)
"strJobId": "3102", # 업무구분코드(3102 고정값인듯)
"inrecmnsGridcnt": "1", # 1 고정값인듯
"dptTm": reservation.dep_time, # 출발시간
"arvTm": reservation.arr_time, # 도착시간
"dptStnConsOrdr2": "000000", # 출발역구성순서2 (000000 고정)
"arvStnConsOrdr2": "000000", # 도착역구성순서2 (000000 고정)
"trnGpCd": "300", # 열차그룹코드(300 고정)
"pageNo": "-", # 페이지번호(- 고정)
"rowCnt": "-", # 한페이지당건수(- 고정)
"pageUrl": "", # 페이지URL (빈값 고정)
}

r = self._session.post(url=url, data=data)

parser = json.loads(r.text)

if (
parser.get("outDataSets").get("dsOutput0")[0].get("strResult")
== RESULT_FAIL
):
raise SRTResponseError(
parser.get("outDataSets").get("dsOutput0")[0].get("msgTxt")
)

return True
27 changes: 27 additions & 0 deletions docs/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ SRT 로그인, 승차표 찾기 설명은 생략합니다.
Highly inspired by [@dotaitch](https://github.com/dotaitch).

예시) 어른 2명, 어린이 1명 예약

```python
>>> from SRT.passenger import Adult, Child
>>> srt.reserve(trains[0], passengers=[Adult(), Adult(), Child()])
Expand All @@ -29,6 +30,7 @@ Highly inspired by [@dotaitch](https://github.com/dotaitch).
## 일반실 / 특실 좌석 옵션 선택하기

예시) 일반실 우선 예약

```python
>>> from SRT import SeatType
>>> srt.reserve(self, trains[0], special_seat=SeatType.GENERAL_FIRST)
Expand All @@ -38,3 +40,28 @@ Highly inspired by [@dotaitch](https://github.com/dotaitch).
- SeatType.GENERAL_ONLY : 일반실만
- SeatType.SPECIAL_FIRST : 특실 우선
- SeatType.SPECIAL_ONLY : 특실만

## 결제하기

**⚠️ 주의: 이 API는 실제로 결제를 수행합니다**<br>
해당 결제 API는 비공식적인 API를 사용하기 때문에 언제든지 제대로 동작하지 않을 수 있습니다.<br>
이에 따른 문제 발생에 대한 책임은 API 사용자 본인에게 있음을 알립니다.<br>

---

```python
>>> reservation = srt.reserve(trains[0])
>>> srt.payment(reservation, "1234567890123456", "12", "981204", "2309", 0, "J")
```

### 각 파라미터 기입요령

| 순서 | 변수명 | 설명 | 예시 | 기본 값 |
| ---- | ---------------------- | ----------------------------------------------------------------------------------------------------------- | -------------------- | ------- |
| 1 | reservation | **결제대상 예약내역** <br>_\* `SRTReservation` 타입을 준수_ | - | - |
| 2 | card_number | **결제 카드번호** <br>_\* 하이픈(-) 제외_ | `"1234000056780000"` | - |
| 3 | card_password | **카드비밀번호 앞 2자리** | `"12"` | - |
| 4 | card_validation_number | **개인(`J`)인 경우: 생년월일<br>법인(`S`)인 경우: 사업자번호** | `"981204"` | - |
| 5 | card_expire_date | **카드유효기간** <br>_\* YYMM(연도+월) 형식의 만료일을 입력<br>\*카드 표현방식 MMYY(월+연도)형식과 착오에 주의_ | `"2309"` | - |
| 6 | card_installment | **할부선택** <br>_\* 할부 개월 수 입력, 0의 경우 일시불.<br> \* 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 24 개월 선택 가능_ | `0` | `0` |
| 7 | card_type | **카드 유형** <br>_\* J : 개인, S : 법인_ | `"J"` | `"J"` |
22 changes: 22 additions & 0 deletions tests/mock_responses/payment_fail_bad_request.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"ERROR_MSG": "",
"ERROR_CODE": "0",
"outDataSets": {
"dsOutput0": [
{
"msgCd": "WZZ000001",
"strResult": "FAIL",
"msgTxt": "% 입력이 잘못되었습니다."
}
]
},
"dsResultMap": {
"dsOutput0": [
{
"msgCd": "WZZ000001",
"strResult": "FAIL",
"msgTxt": "% 입력이 잘못되었습니다."
}
]
}
}
22 changes: 22 additions & 0 deletions tests/mock_responses/payment_fail_cant_installment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"ERROR_MSG": "",
"ERROR_CODE": "0",
"outDataSets": {
"dsOutput0": [
{
"msgCd": "WRTV07574",
"strResult": "FAIL",
"msgTxt": "할부불가카드"
}
]
},
"dsResultMap": {
"dsOutput0": [
{
"msgCd": "WRTV07574",
"strResult": "FAIL",
"msgTxt": "할부불가카드"
}
]
}
}
22 changes: 22 additions & 0 deletions tests/mock_responses/payment_fail_card_password.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"ERROR_MSG": "",
"ERROR_CODE": "0",
"outDataSets": {
"dsOutput0": [
{
"msgCd": "WRTV06003",
"strResult": "FAIL",
"msgTxt": "비밀번호오류<br><br>사용하신 각 신용카드사의 고객센터로 문의 바랍니다."
}
]
},
"dsResultMap": {
"dsOutput0": [
{
"msgCd": "WRTV06003",
"strResult": "FAIL",
"msgTxt": "비밀번호오류<br><br>사용하신 각 신용카드사의 고객센터로 문의 바랍니다."
}
]
}
}
22 changes: 22 additions & 0 deletions tests/mock_responses/payment_fail_expired_card.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"ERROR_MSG": "",
"ERROR_CODE": "0",
"outDataSets": {
"dsOutput0": [
{
"msgCd": "WRTV08314",
"strResult": "FAIL",
"msgTxt": "유효기간경과카드"
}
]
},
"dsResultMap": {
"dsOutput0": [
{
"msgCd": "WRTV08314",
"strResult": "FAIL",
"msgTxt": "유효기간경과카드"
}
]
}
}
22 changes: 22 additions & 0 deletions tests/mock_responses/payment_fail_invalid_auth_number.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"ERROR_MSG": "",
"ERROR_CODE": "0",
"outDataSets": {
"dsOutput0": [
{
"msgCd": "WRTV06006",
"strResult": "FAIL",
"msgTxt": "주민번호 또는 사업자번호 오류입니다."
}
]
},
"dsResultMap": {
"dsOutput0": [
{
"msgCd": "WRTV06006",
"strResult": "FAIL",
"msgTxt": "주민번호 또는 사업자번호 오류입니다."
}
]
}
}
22 changes: 22 additions & 0 deletions tests/mock_responses/payment_fail_invalid_card_number.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"ERROR_MSG": "",
"ERROR_CODE": "0",
"outDataSets": {
"dsOutput0": [
{
"msgCd": "WRTV08037",
"strResult": "FAIL",
"msgTxt": "카드번호오류"
}
]
},
"dsResultMap": {
"dsOutput0": [
{
"msgCd": "WRTV08037",
"strResult": "FAIL",
"msgTxt": "카드번호오류"
}
]
}
}
22 changes: 22 additions & 0 deletions tests/mock_responses/payment_fail_invalid_expiration_date.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"ERROR_MSG": "",
"ERROR_CODE": "0",
"outDataSets": {
"dsOutput0": [
{
"msgCd": "WRTV08315",
"strResult": "FAIL",
"msgTxt": "유효기간을 잘못입력하셨습니다."
}
]
},
"dsResultMap": {
"dsOutput0": [
{
"msgCd": "WRTV08315",
"strResult": "FAIL",
"msgTxt": "유효기간을 잘못입력하셨습니다."
}
]
}
}
22 changes: 22 additions & 0 deletions tests/mock_responses/payment_fail_invalid_reservation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"ERROR_MSG": "",
"ERROR_CODE": "0",
"outDataSets": {
"dsOutput0": [
{
"msgCd": "WRT200178",
"strResult": "FAIL",
"msgTxt": "취소된 여정이므로 발매할 수 없습니다.<br>비회원은 다시 예약하셔야합니다."
}
]
},
"dsResultMap": {
"dsOutput0": [
{
"msgCd": "WRT200178",
"strResult": "FAIL",
"msgTxt": "취소된 여정이므로 발매할 수 없습니다.<br>비회원은 다시 예약하셔야합니다."
}
]
}
}
22 changes: 22 additions & 0 deletions tests/mock_responses/payment_fail_over_limit.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"ERROR_MSG": "",
"ERROR_CODE": "0",
"outDataSets": {
"dsOutput0": [
{
"msgCd": "WRTV08326",
"strResult": "FAIL",
"msgTxt": "사용한도초과"
}
]
},
"dsResultMap": {
"dsOutput0": [
{
"msgCd": "WRTV08326",
"strResult": "FAIL",
"msgTxt": "사용한도초과"
}
]
}
}
22 changes: 22 additions & 0 deletions tests/mock_responses/payment_fail_suspension_card.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"ERROR_MSG": "",
"ERROR_CODE": "0",
"outDataSets": {
"dsOutput0": [
{
"msgCd": "WRTV08324",
"strResult": "FAIL",
"msgTxt": "거래정지카드"
}
]
},
"dsResultMap": {
"dsOutput0": [
{
"msgCd": "WRTV08324",
"strResult": "FAIL",
"msgTxt": "거래정지카드"
}
]
}
}
Loading