@마크다운
해외결제를 붙여야 할 일이 생겨서 찾아보던중 eximbay라는 결제모듈을 찾았다.
eximbay를 선택한 이유는 해외결제 대상이 동남아쪽(태국)인데 동남아쪽은 카드발급율이 엄청나게 낮다고 한다. (10%이하)
그럼 인터넷 결제를 어떻게 하냐?
MOLPAY라는 동남아쪽 PG에서 주문을 하고 입금을 ATM기기로 MOLPAY측에 입금을 한다고 한다.
마치 무통장 입금같은 느낌으로
아무튼 위와같은 과정들을 진행하려면 상당히 까다로운 과정들을 거쳐야하는데 이 과정을 eximbay를 통해서 진행하면 좀 낫다.
eximbay에서는 당연?하게도 샘플코드를 자바와php만을 지원을 하고 있으며 나는 python이기 때문에 포팅을 하여 사용을 해야한다.
그러나 eximbay측 기술문서가 상당히 잘되어있어 몇번의 문의(????)만으로 구현을 할 수 있다.
여담이지만 이번에 eximbay를 달면서 iamport에 있는 eximbay도 같이 사용해 보았으나 생각보다 완성도가 높지않아 사용할 수 없었다.
(MOLPAY 지원이라 적혀있으나 태국이 결제 지원이 안되었다[지금은 됩니다.], 테스트를 거치지 않은 날것 같은 느낌의 에러를 뿜어내기도 했다.)
기술문서를 읽어보면 알겠지만 엑심베이는 요청 파라미터들을 알파벳 순으로 sort하여 secret_key 와 "?"로 연결하여 sha256으로 해시한 값을 따로담아서 같이 보내줘야한다.
# code
```
class Eximbay():
def __init__(self, secret_key=TEST_SECRET_KEY, mid=TEST_MID, mod="TEST"):
self.secret_key = secret_key
self.mid = mid
if mod == "REAL": # real
self.url = "https://secureapi.eximbay.com/Gateway/BasicProcessor.krp"
else:
self.url = "https://secureapi.test.eximbay.com/Gateway/BasicProcessor.krp"
# 파라미터 정렬
@staticmethod
def _sorting_params(**kwargs):
"""
:param kwargs:
:return:
"""
return_data = sorted(kwargs.items(), key=operator.itemgetter(0)) # 키값으로 소팅
params = ""
for k, v in return_data:
params += str(k) + "=" + str(v) + "&"
params = params[:-1]
return params
def make_fgkey(self, **kwargs):
"""
fgkey는
A. 모든 요청/응답 파라미터를 알파벳순서로 sorting하여 데이터를 생성 B. secretkey와 A의 데이터를 "?"로 연결
C. B의 결과를 SHA256 함수를 통해 Hashing 하여 생성합니다.
"""
params = self._sorting_params(**kwargs)
secret_key = self.secret_key
secret_add_params = secret_key + "?" + params
# params 과 시크릿키를 더한 후 단방향해시 후 str변환
fgkey = hashlib.sha256(secret_add_params.encode("utf-8")).hexdigest()
return fgkey
# 결제 요청
def payment(self, **kwargs):
# cur : 통화 , amt : 금액, buyer : 구매자 이름, email : 이메일, lang : 언어팩, returnurl : 결제완료 or 취소시 리턴
# statusurl : 결제 요청시 request 보내는곳 ,paymethod : 태국 MOLPAY 171
required_param_list = ['cur', 'amt', 'buyer', 'email', 'lang', 'returnurl', 'statusurl', 'paymethod']
# 요청 필수 값
for key in required_param_list:
if key not in kwargs:
raise KeyError("Parameter is missing!: {}".format(key))
# 결제 공통부분
payment_params = {
'ver': 230, # 버전
'txntype': "PAYMENT", # 간편결제
'charset': "UTF-8",
'mid': self.mid, # 가맹점 아이디
'displaytype': "P"
}
payment_params.update(kwargs)
payment_params['fgkey'] = self.make_fgkey(**payment_params)
template = Template("""
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" charset=UTF-8">
</head>
<body leftmargin="0" topmargin="0" align="center" onload="javascript:document.regForm.submit();">
{% if data %}
<form name="regForm" method="post" action="{{ reqUrl }}" accept-charset="utf-8">
{% for key, value in data.items() %}
<input type="hidden" name="{{ key }}" value="{{ value }}" >
{% endfor %}
</form>
{% else %}
<h1>No Data!!!</h1>
{% endif %}
</body>
<script>
document.regForm.submit()
</script>
</html>
""")
return template.render(reqUrl=self.url, data=payment_params)
```
제일 헤메던 부분이 데이터를 엑심베이측으로 넘겨줘야하는 부분인데
자꾸 이부분에서 인코딩이 박살이 나는 것이었다.
코드상의 문제는 암만봐도 없는것 같은데 인코딩이 박살이 나니 여기저기 쑤시고 문의를 보냈다.
엑심베이의 기술지원문의는 상당히 깔끔하게 답변을 잘해주신다.
>KRP 김종민님께 감사드립니다. 매번 깔끔한 답변 감사합니다.
답변중 엑심베이의 로그 데이터를 보여주셨는데 그 데이터가 다음과 같았다.
```
buyer:บุษ&#
```
나는 분명 utf-8로 인코딩해서 보냈는데 받는쪽에서 이렇게 박살이 나는것 아닌가?
뭔가 삘이 왔다
[string conversion](http://coderstoolbox.net/string/#!encoding=xml&action=encode&charset=us_ascii)
여기로가서 내가 보내려고했던 문자열을 입력하고 똑같이 박살나는 녀석을 찾기 시작했다.
그 결과 ISO-8859-1이라는 놈이랑 정확하게 일치했다.
원인은 알았으니 이제 해결만 하면 된다.
대체 ISO-8859-1이라는놈이 뭔가?
찾아보니 html4의 디폴트 인코딩 타입이라고 한다.
왜 이녀석이 말썽을 부리는건지 도저히 1도 모르겠지만
```
<meta http-equiv="X-UA-Compatible" charset=UTF-8">
```
이코드를 집어넣고 다시 돌려봤다.
안된다.
다시 멘붕에 빠져있을때, 혹시 form에서 문제를 일으키는건 아닐까 라는 생각이들어 form에서 인코딩을 바꿀수 있는 요소를 찾기 시작했다.
[코드쓰는사람](https://taegon.kim/archives/1692)
다음과 같은 게시물을 발견했고 바로 적용했다.
이 모든 과정을 거쳐서 만들어진게 상기의 코드이며 2018-06-18 현재 정상작동이 되고 있음을 확인했다.
이렇게 쓰고보니 상당히 간단한 과정이지만 삽질을 반복하던 나로써는 엄청난 발전을 한것 같았다.
물론 지금의 코드를 그대로 쓰면 결제가 진행만 된다.
서비스로직에 붙이는건 더 고단한 일이다. ㅠㅠ
아무튼.. 누군가에겐 도움이 되었으면 좋겠다.
'notes' 카테고리의 다른 글
python pycrypto 사용하기 (1) | 2017.12.16 |
---|---|
git commit 100MB이상 파일 지우기 (0) | 2017.12.07 |
rest API에 페이스북 로그인 (소셜 로그인)하기 (0) | 2017.09.19 |
docker안에서 인코딩타입, 시간 설정 바꾸기 (1) | 2017.09.12 |
django generic view 사용시 method마다 다른 serializer사용하고 싶을 때 (0) | 2017.09.12 |