Skip to content

Commit

Permalink
결제기능 추가 (#249)
Browse files Browse the repository at this point in the history
  • Loading branch information
Monsteel authored Sep 26, 2023
1 parent bca6fb2 commit 75e2ff9
Show file tree
Hide file tree
Showing 16 changed files with 792 additions and 0 deletions.
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
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 pay_with_card(
self,
reservation: SRTReservation,
number: str,
password: str,
card_validation_number: str,
card_expire_date: str,
card_installment: int = 0,
card_type: str = "J",
) -> bool:
"""결제합니다.
>>> reservation = srt.reserve(train)
>>> srt.pay_with_card(reservation, "1234567890123456", "12", "981204", "2309", 0, "J")
Args:
reservation (:class:`SRTReservation`): 예약 내역
number (str): 결제신용카드번호 (하이픈(-) 제외)
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": 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": 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.pay_with_card(reservation, "1234567890123456", "12", "981204", "2309", 0, "J")
```

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

| 순서 | 변수명 | 설명 | 예시 | 기본 값 |
| ---- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------- | ------- |
| 1 | reservation | **결제대상 예약내역** <br>_\* `SRTReservation` 타입을 준수_ | - | - |
| 2 | number | **결제 카드번호** <br>_\* 하이픈(-) 제외_ | `"1234000056780000"` | - |
| 3 | 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/pay_with_card_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/pay_with_card_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/pay_with_card_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/pay_with_card_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/pay_with_card_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/pay_with_card_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": "카드번호오류"
}
]
}
}
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/pay_with_card_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/pay_with_card_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/pay_with_card_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

0 comments on commit 75e2ff9

Please sign in to comment.