이웃 블로거 까누님의 블로그를 보다가 쿠팡 파트너스 API를 활용하는 것을 알게 되었습니다.
그래서 프로그램을 잘하지는 못 하지만, 프로그램 공부를 할 겸해서 한번 파이썬 프로그램으로 쿠팡 파트너스 API를 활용해 보기로 했습니다.
쿠팡 파트너스 API는 쿠팡 파트너스 홈페이지에서 가이드라인을 제공해줍니다.
https://partners.coupang.com/#help/open-api
쿠팡 파트너스 API를 사용하기 위해서는 쿠팡 파트너스 광고를 이용하여 한번이라도 수익이 있어야 한다고 합니다.
쿠팡 파트너스 API를 활용하기 위해서는 HMAC 서명을 해 줘야 합니다.
HMAC는 Hash-based Message Authentication Code의 줄임말로 암호화 키를 의미합니다.
자세히는 아직 공부를 안해서 모르기도 하고 그것을 설명하려면 너무 길어질 거 같아서 간단히 명시했습니다.
다음에 기회가 되면 HMAC에 대해서 자세히 다루도록 하겠습니다.
이번 블로그의 주 내용은 HMAC 서명시 나오는 ERROR에 대해서 다루었습니다.
그럼 쿠팡 파트너스 API에서 제공하는 HMAC 서명을 위한 알아보겠습니다.
저는 파이썬 UI 프로그램을 하기 위해서 PyQt5를 사용하기 위해서 파이썬 3.7 Version을 사용하였습니다.
쿠팡 파트너스 API 샘플 코드를 제가 사용하는 파이썬 IDE PyCharm에서 실행했습니다.
import hmac
import hashlib
import binascii
import os
import time
import requests
import json
REQUEST_METHOD = "POST"
DOMAIN = "https://api-gateway.coupang.com"
URL = "/v2/providers/affiliate_open_api/apis/openapi/v1/deeplink"
# Replace with your own ACCESS_KEY and SECRET_KEY
ACCESS_KEY = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
SECRET_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
REQUEST = { "coupangUrls": [
"https://www.coupang.com/np/search?component=&q=good&channel=user",
"https://www.coupang.com/np/coupangglobal"
]}
def generateHmac(method, url, secretKey, accessKey):
path, *query = url.split("?")
os.environ["TZ"] = "GMT+0"
datetime = time.strftime('%y%m%d')+'T'+time.strftime('%H%M%S')+'Z'
message = datetime + method + path + (query[0] if query else "")
signature = hmac.new(bytes(secretKey, "utf-8"),
message.encode("utf-8"),
hashlib.sha256).hexdigest()
return "CEA algorithm=HmacSHA256, access-key={}, signed-date={}, signature={}".format(accessKey, datetime, signature)
authorization = generateHmac(REQUEST_METHOD, URL, SECRET_KEY, ACCESS_KEY)
url = "{}{}".format(DOMAIN, URL)
resposne = requests.request(method=REQUEST_METHOD, url=url,
headers={
"Authorization": authorization,
"Content-Type": "application/json"
},
data=json.dumps(REQUEST)
)
print(resposne.json())
ACCESS_KEY와 SECRET_KEY에는 개인 할당받은 내용을 넣습니다.
그리고 실행해 보았습니다.
실행 결과는 'ERROR'였습니다.
쿠팡 파트너스 API 가이드에는 자주 발생하는 문제에 대한 에러 코드를 설명해 줍니다.
에러 코드를 찾아보니 HMAC의 서명 관련 문제였습니다.
하하~
다시 쿠팡 파트너스 사이트에서 HMAC 생성 가이드의 유무를 확인해 보았습니다.
쿠팡 파트너스 API의 FAQ에는 HMAC 생성 가이드가 있었습니다.
하지만 공백만 나오더라고요.... ㄷㄷㄷ
허허... 이러면 안되는데...
그래서 구글링을 시작했습니다.
Coupang Developers 홈페이지에서 HMAC 생성 가이드를 찾았습니다.
Python Example가 제공되어서 아래 소스를 실행해 보았습니다.
import os
import time
import hmac, hashlib
import urllib.parse
import urllib.request
import ssl
import json
os.environ['TZ'] = 'GMT+0'
datetime=time.strftime('%y%m%d')+'T'+time.strftime('%H%M%S')+'Z'
method = "POST"
path = "/v2/providers/seller_api/apis/api/v1/marketplace/seller-products"
message = datetime+method+path
#replace with your own accesskey
accesskey = "****"
#replace with your own secretKey
secretkey = "****"
#********************************************************#
#authorize, demonstrate how to generate hmac signature here
signature=hmac.new(secretkey.encode('utf-8'),message.encode('utf-8'),hashlib.sha256).hexdigest()
authorization = "CEA algorithm=HmacSHA256, access-key="+accesskey+", signed-date="+datetime+", signature="+signature
#print out the hmac key
#print(authorization)
#********************************************************#
# ************* SEND THE REQUEST *************
#url = "http://api-gateway-it-ext.coupang.com"+path
url = "https://api-gateway.coupang.com"+path
true=True
false=False
null = None
strjson={
"displayCategoryCode": 56137,
"sellerProductName": "상품등록_example",
"vendorId": "A00012345",
"saleStartedAt": "2018-08-13T00:00:00",
"saleEndedAt": "2099-01-01T23:59:59",
"displayProductName": "해피바스 솝베리 클렌징 오일",
"brand": "해피바스",
"generalProductName": "솝베리 클렌징 오일",
"productGroup": "클렌징 오일",
"deliveryMethod": "SEQUENCIAL",
"deliveryCompanyCode": "KGB",
"deliveryChargeType": "FREE",
"deliveryCharge": 0,
"freeShipOverAmount": 0,
"deliveryChargeOnReturn": 2500,
"remoteAreaDeliverable": "N",
"unionDeliveryType": "UNION_DELIVERY",
"returnCenterCode": "1000274592",
"returnChargeName": "반품지_1",
"companyContactNumber": "02-1234-678",
"returnZipCode": "135-090",
"returnAddress": "서울특별시 강남구 삼성동",
"returnAddressDetail": "333",
"returnCharge": 2500,
"returnChargeVendor": "N",
"afterServiceInformation": "A/S안내 1544-1255",
"afterServiceContactNumber": "1544-1255",
"outboundShippingPlaceCode": "74010",
"vendorUserId": "user01",
"requested": false,
"items": [
{
"itemName": "200ml_1개",
"originalPrice": 13000,
"salePrice": 10000,
"maximumBuyCount": "100",
"maximumBuyForPerson": "0",
"outboundShippingTimeDay": "1",
"maximumBuyForPersonPeriod": "1",
"unitCount": 1,
"adultOnly": "EVERYONE",
"taxType": "TAX",
"parallelImported": "NOT_PARALLEL_IMPORTED",
"overseasPurchased": "NOT_OVERSEAS_PURCHASED",
"pccNeeded": "false",
"externalVendorSku": "0001",
"barcode": "",
"emptyBarcode": true,
"emptyBarcodeReason": "상품확인불가_바코드없음사유",
"modelNo": "1717171",
"extraProperties": null,
"certifications": [
{
"certificationType": "NOT_REQUIRED",
"certificationCode": ""
}
],
"searchTags": [
"검색어1",
"검색어2"
],
"images": [
{
"imageOrder": 0,
"imageType": "REPRESENTATION",
"vendorPath": "http://image11.coupangcdn.com/image/product/image/vendoritem/2018/06/25/3719529368/27a6b898-ff3b-4a27-b1e4-330a90c25e9c.jpg"
},
{
"imageOrder": 1,
"imageType": "DETAIL",
"vendorPath": "http://image11.coupangcdn.com/image/product/image/vendoritem/2017/02/21/3000169918/34b79649-d625-4f49-a260-b78bf7a573a8.jpg"
},
{
"imageOrder": 2,
"imageType": "DETAIL",
"vendorPath": "http://image11.coupangcdn.com/image/product/image/vendoritem/2018/06/28/3000169918/5716aa61-70bd-47cd-8f3d-f3d49e7f496d.jpg"
}
],
"notices": [
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "용량(중량)",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "제품 주요 사양",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "사용기한 또는 개봉 후 사용기간",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "사용방법",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "제조업자 및 제조판매업자",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "제조국",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "화장품법에 따라 기재, 표시하여야 하는 모든 성분",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "식품의약품안전처 심사 필 유무",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "사용할 때 주의사항",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "품질보증기준",
"content": "제품 이상 시 공정거래위원회 고시 소비자분쟁해결기준에 의거 보상합니다."
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "소비자상담관련 전화번호",
"content": "상세페이지 참조"
}
],
"attributes": [
{
"attributeTypeName": "수량",
"attributeValueName": "1개"
},
{
"attributeTypeName": "개당 용량",
"attributeValueName": "200ml"
}
],
"contents": [
{
"contentsType": "TEXT",
"contentDetails": [
{
"content": "<img src='http://image11.coupangcdn.com/image/product/content/vendorItem/2018/06/26/196713/738d905f-ed80-4fd8-ad21-ed87b195a19e.jpg' />",
"detailType": "TEXT"
}
]
}
],
"offerCondition": "NEW",
"offerDescription": ""
},
{
"itemName": "200ml_2개",
"originalPrice": 26000,
"salePrice": 20000,
"maximumBuyCount": "100",
"maximumBuyForPerson": "0",
"outboundShippingTimeDay": "2",
"maximumBuyForPersonPeriod": "1",
"unitCount": 1,
"adultOnly": "EVERYONE",
"taxType": "TAX",
"parallelImported": "NOT_PARALLEL_IMPORTED",
"overseasPurchased": "NOT_OVERSEAS_PURCHASED",
"pccNeeded": "false",
"externalVendorSku": "0002",
"barcode": "",
"emptyBarcode": true,
"emptyBarcodeReason": "상품확인불가_바코드없음사유",
"modelNo": "1717172",
"extraProperties": {
"GOODS_SEQ":"1",
"optuid":"18788597"
},
"certifications": [
{
"certificationType": "NOT_REQUIRED",
"certificationCode": ""
}
],
"searchTags": [
"검색어1",
"검색어2"
],
"images": [
{
"imageOrder": 0,
"imageType": "REPRESENTATION",
"vendorPath": "http://image11.coupangcdn.com/image/product/image/vendoritem/2018/06/26/3001519145/74100e2a-d1ad-4b50-9c78-840c12a3e10d.jpg"
},
{
"imageOrder": 1,
"imageType": "DETAIL",
"vendorPath": "http://image11.coupangcdn.com/image/product/image/vendoritem/2017/02/21/3000169918/34b79649-d625-4f49-a260-b78bf7a573a8.jpg"
},
{
"imageOrder": 2,
"imageType": "DETAIL",
"vendorPath": "http://image11.coupangcdn.com/image/product/image/vendoritem/2018/06/28/3000169918/5716aa61-70bd-47cd-8f3d-f3d49e7f496d.jpg"
}
],
"notices": [
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "용량(중량)",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "제품 주요 사양",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "사용기한 또는 개봉 후 사용기간",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "사용방법",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "제조업자 및 제조판매업자",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "제조국",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "화장품법에 따라 기재, 표시하여야 하는 모든 성분",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "식품의약품안전처 심사 필 유무",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "사용할 때 주의사항",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "품질보증기준",
"content": "상세페이지 참조"
},
{
"noticeCategoryName": "화장품",
"noticeCategoryDetailName": "소비자상담관련 전화번호",
"content": "상세페이지 참조"
}
],
"attributes": [
{
"attributeTypeName": "수량",
"attributeValueName": "2개"
},
{
"attributeTypeName": "개당 용량",
"attributeValueName": "200ml"
}
],
"contents": [
{
"contentsType": "TEXT",
"contentDetails": [
{
"content": "<img src='http://image11.coupangcdn.com/image/product/content/vendorItem/2018/06/26/196713/738d905f-ed80-4fd8-ad21-ed87b195a19e.jpg' />",
"detailType": "TEXT"
}
]
}
],
"offerCondition": "NEW",
"offerDescription": ""
}
],
"requiredDocuments": [
{
"templateName": "기타인증서류",
"vendorDocumentPath": "http://image11.coupangcdn.com/image/product/content/vendorItem/2018/07/02/41579010/eebc0c30-8f35-4a51-8ffd-808953414dc1.jpg"
}
],
"extraInfoMessage": "",
"manufacture": "아모레퍼시픽"}
data = json.dumps(strjson).encode("utf-8")
print('BEGIN REQUEST++++++++++++++++++++++++++++++++++++')
req = urllib.request.Request(url)
req.add_header("Content-type","application/json;charset=UTF-8")
req.add_header("Authorization",authorization)
req.get_method = lambda: method
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
print('RESPONSE++++++++++++++++++++++++++++++++++++')
try:
resp = urllib.request.urlopen(req, data, context=ctx)
except urllib.request.HTTPError as e:
print(e.code)
print(e.reason)
except urllib.request.URLError as e:
print(e.errno)
print(e.reason)
else:
# 200
body = resp.read().decode(resp.headers.get_content_charset())
print(body)
해당 소스는 판매자의 API 샘플을 보여주는 것 같은데요. 일단 'Python POST Request Example'라고 명시가 되어 있어서 서명을 확인할 수 있을 듯하여 실행해 보았습니다.
401이 리턴이 되었습니다.
쿠팡 파트너스에서 다시 검색을 하니 'Specified signature ex expired' 오류라고 합니다.
하하 여기서 난관에 부탁 쳤어요. 그래서 쿠팡 파트너스 문의하기에 메일을 보냈습니다.
메일 내용은 간단히 줄여서 말하자면 '쿠팡 파트너스에서 제공하는 소스를 적용해 보았다. 그러나 동작이 되지 않았다. 왜 그런 건가요?'라는 내용의 메일이었습니다.
그러나 답변에서 답을 구할 수는 없었어요.
그래서 열심히 구글링을 하기 시작했습니다.
구글링을 열심히 했지만, 이거에 대한 명쾌한 해답은 구하지 못했습니다.
그렇지만 힌트를 얻을 만한 말을 얻었습니다.
쿠팡 파트너스에서 제공하는 소스가 Python 3.8로 제작된 소스라는 말이었습니다.
그래서 제가 작성한 소스의 컴파일러를 Python 3.7에서 Python 3.8로 변경하였습니다.
동작을 시켜보니 정상 동작이 되었습니다.
'POST'에 대한 응답을 쿠팡 파트너스 API 문서에서 확인을 했습니다.
'orginalUrl', 'shortenUrl', 'landingUrl'이 모두 정상적으로 응답하는 것을 확인하였습니다.
저는 PyQt5를 사용하여, QT Designer를 사용하기 위해서 Python 3.7을 사용을 원했습니다.
Python 3.8에는 아직 PyQt5가 지원이 안 된다는 사실을 알고 둘의 차이를 찾아보았습니다.
hamc - 메세지 인증을 위한 키 해싱 - Python 3.8.2rc2 문서에서는 다음과 내용의 기능이 변경되었습니다.
버전 3.4부터는 사용이 되지 않았으며, 버전 3.8에서는 제거된 기능이 있었습니다.
하하~ 아직 초보인 저한테는 어려운 내용이네요. HMAC를 좀 더 공부해서 파악을 해봐야겠어요.
혹시 고수분이 있으시면 친절하게 알려주시면 감사하겠습니다^^
앞으로 공부해야 할 것도 많고 할 일들이 많이 남아 있네요. ㅎㅎㅎ
프로그램의 덩치가 커지면서 여러 소스를 모듈로 만들어서 붙이고 있는데요.
하하 매번 모듈을 붙일 때마다 발생하는 문제가 쿠팡 파트너스 API의 요청이 거부되는 문제였어요.
그래서 이것이 원칙적인 문제 해결 방법은 아니라는 것을 인지하게 되었고, 이 문제를 해결하기 위해서 고민 고민했어요.
그러다가 문제를 찾고 해결하게 되어서 내용을 UPDATE 하게 되었습니다.
정확한 문제는 타임존에 있었어요.
쿠팡 파트너스에서는 HAMC 승인을 받기 위해 날짜와 시간을 만드는 부분이 있는데요.
아래 코드와 같이 GMT 시간대를 사용하는 것을 알 수 있어요.
os.environ["TZ"] = "GMT+0"
datetime = time.strftime('%y%m%d') + 'T' + time.strftime('%H%M%S') + 'Z'
하지만 이렇게 하고 다른 모듈을 붙여 나가니 정상적으로 GMT 시간을 가져오던 것이 현재 시간으로 변경이 되어서 시간대가 차이가 나는 것을 알게 되었어요.
현재 시간 | date = 200311, time = 154048 |
GMT 시간 | date = 200311, time = 063951 |
그래서 time 모듈에서 GMT 시간을 받아와서 직접 처리했어요.
dateGMT = strftime('%y%m%d', gmtime())
timeGMT = strftime('%H%M%S', gmtime())
datetime = dateGMT + 'T' + timeGMT + 'Z'
이렇게 처리를 하니 모듈화 된 소스를 가져다 붙여도 문제가 없는 것을 확인했어요^^
이 정보가 도움이 되었으면 좋겠어요^^
파이썬 관련 글 |
[ProgramStudy/Development tool] - Python 설치 하기! (3.8.1 Version, 64bit) |
저의 글을 읽어 주셔서 감사합니다. 오늘도 즐거운 하루 보내세요.
저의 글이 조금이나마 도움이 되셨다면 로그인이 필요 없는 공감♥ 한번 꾸욱 눌러주세요 하하~
[혼자서 공부해본 파이썬] 자료형 - 리스트 생성, 인덱싱, 슬라이싱, 연산 (12) | 2020.04.09 |
---|---|
[혼자서 공부해본 파이썬] 자료형 - 정수형, 실수형, 복소수, 논리형, 문자열 (6) | 2020.04.09 |
[혼자서 공부해본 파이썬] 파이썬이란? (16) | 2020.04.08 |
댓글 영역