[Django]Iamport 연동하기 - 2
[Django]Iamport 연동하기 - 2
기본적으로 결제 모델을 만들 때 가장 중요한 것인 order_id
와 transaction_id
이다. order_id
는 서버 내에서 자동으로 생성하는 주문 번호이고 transaction_id
는 아임포트에서 생성해주는 고유 번호이다. 서버에서 결제가 정상적으로 이루어졌는지 확인하기 위해서 transaction_id
로 통해 조회하게 된다.
이제 아임포트와 통신할 수 있는 모듈을 만들어 보자. billing 앱 안에 iamport.py
을 만들어보자.
#billing/iamport.py
import requests
import json
from django.conf import settings
def get_access_token():
access_data = {
'imp_key': settings.IAMPORT_KEY,
'imp_secret': settings.IAMPORT_SECRET
}
url = "https://api.iamport.kr/users/getToken"
req = requests.post(url, data=access_data)
access_res = req.json()
if access_res['code'] is 0:
return access_res['response']['access_token']
else:
return None
def validation_prepare(merchant_id, amount, *args, **kwargs):
access_token = get_access_token()
if access_token:
access_data = {
'merchant_uid': merchant_id,
'amount': amount
}
url = "https://api.iamport.kr/payments/prepare"
headers = {
'Authorization': access_token
}
req = requests.post(url, data=access_data, headers=headers)
res = req.json()
if res['code'] is not 0:
raise ValueError("API 연결에 문제가 생겼습니다.")
else:
raise ValueError("인증 토큰이 없습니다.")
def get_transaction(merchant_id, *args, **kwargs):
access_token = get_access_token()
if access_token:
url = "https://api.iamport.kr/payments/find/" + merchant_id
headers = {
'Authorization': access_token
}
req = requests.post(url, headers=headers)
res = req.json()
if res['code'] is 0:
context = {
'imp_id': res['response']['imp_uid'],
'merchant_id': res['response']['merchant_uid'],
'amount': res['response']['amount'],
'status': res['response']['status'],
'type': res['response']['pay_method'],
'receipt_url': res['response']['receipt_url']
}
return context
else:
return None
else:
raise ValueError("인증 토큰이 없습니다.")
아임포트와 통신 할 때는 크게 3가지 과정을 나누어 진행된다. get_access_token
은 아임포트 서버에 접근할 수 있는 토큰을 발급 받는 과정으로 발급 받은 토큰으로 유저가 결제한 정보를 가져오게 된다. validation_prepare
은 결제를 검증하는 단계로 유저가 요청한 금액과 아임포트에 있는 결제 금액이 일치하는지 검증하는 단계이다. get_transaction
은 결제가 끝나고 나서 결제에 대한 정보를 가져오는 단계이다.
이제 포인트에 대한 Manager를 만들어보자. model 부분을 수정하자.
# billing/models.py
from django.db import models
# User 모델은 알아서 가져오기
from users.models import MyUser
from billing.iamport import validation_prepare, get_transaction
class Point(models.Model):
user = models.OneToOneField(MyUser)
point = models.PositiveIntegerField(default=0)
created = models.DateTimeField(auto_now_add=True, auto_now=False)
timestamp = models.DateTimeField(auto_now_add=False, auto_now=True)
def __str__(self):
return str(self.point)
class PointTransactionManager(models.Manager):
# 새로운 트랜젝션 생성
def create_new(self, user, amount, type, success=None, transaction_status=None):
if not user:
raise ValueError("유저가 확인되지 않습니다.")
short_hash = hashlib.sha1(str(random.random())).hexdigest()[:2]
time_hash = hashlib.sha1(str(int(time.time()))).hexdigest()[-3:]
base = str(user.email).split("@")[0]
key = hashlib.sha1(short_hash + time_hash + base).hexdigest()[:10]
new_order_id = "%s" % (key)
# 아임포트 결제 사전 검증 단계
validation_prepare(new_order_id, amount)
# 트랜젝션 저장
new_trans = self.model(
user=user,
order_id=new_order_id,
amount=amount,
type=type
)
if success is not None:
new_trans.success = success
new_trans.transaction_status = transaction_status
new_trans.save(using=self._db)
return new_trans.order_id
# 생선된 트랜잭션 검증
def validation_trans(self, merchant_id):
result = get_transaction(merchant_id)
if result['status'] is not 'paid':
return result
else:
return None
def all_for_user(self, user):
return super(PointTransactionManager, self).filter(user=user)
def get_recent_user(self, user, num):
return super(PointTransactionManager, self).filter(user=user)[:num]
class PointTransaction(models.Model):
user = models.ForeignKey(MyUser)
transaction_id = models.CharField(max_length=120, null=True, blank=True)
order_id = models.CharField(max_length=120, unique=True)
amount = models.PositiveIntegerField(default=0)
success = models.BooleanField(default=False)
transaction_status = models.CharField(max_length=220, null=True, blank=True)
type = models.CharField(max_length=120)
created = models.DateTimeField(auto_now_add=True, auto_now=False)
objects = PointTransactionManager()
def __str__(self):
return self.order_id
class Meta:
ordering = ['-created']
그 다음으로는 Transaction이 일어나서 나서 검증하는 post_save를 만들어보자. 같은 model 안에서 작성하면 된다.
# billing/models.py
import time
import random
import hashlib
from django.db.models.signals import post_save
def new_point_trans_validation(sender, instance, created, *args, **kwargs):
if instance.transaction_id:
# 거래 후 아임포트에서 넘긴 결과
v_trans = PointTransaction.objects.validation_trans(
merchant_id=instance.order_id
)
res_merchant_id = v_trans['merchant_id']
res_imp_id = v_trans['imp_id']
res_amount = v_trans['amount']
# 데이터베이스에 실제 결제된 정보가 있는지 체크
r_trans = PointTransaction.objects.filter(
order_id=res_merchant_id,
transaction_id=res_imp_id,
amount=res_amount
).exists()
if not v_trans or not r_trans:
raise ValueError('비정상적인 거래입니다.')
post_save.connect(new_point_trans_validation, sender=PointTransaction)
결제를 위한 model 부분은 거의 다 끝났다. 다음으로 진행 할 것은 view와 template 단으로 실제 유저가 결제를 할 수 있도록 만들어보자.