책을 읽으면서 유독 결재파트의 코드와 로직의 설명이 불친절하고, 중간중간 빠진로직, 등이 있어 이를 내식대로 수정하고 이해하며 정리한 것을 기록한 것. 개인의 생각과 배프의 코드가 혼재되어있음을 미리 알림
1판기준 317p~340p의 내용에 관한 글.
카트정보화면에서 check-out
버튼을 클릭한다.
GET method로 접근하고, order/order_create view
를 실행한다.
form
과 cart
인스턴스를 생성하고 created.html
에 전달한다.
form
은 OrderCreateForm
이므로 order
table을 생성할 데이터를 input
으로 받는다.
place order
버튼(order/created.html
)을 누르면 form
에 등록되어있는 submit event
가 발생한다.
submit event
를 바인딩 하던 checkout.js
가 발동하여 ajax function
을 실행한다.
AjaxCreateOrder
함수를 실행하여 order_id
를 반환한다.
order/urls.py
에서 order_create_url
으로 바인딩 된 AjaxCreateOrder
class를 찾는다.
OrderCreateAjaxView
클래스의 post method
로 들어온 요청을 수행한다.
request
의 user
가 로그인 상태인지 검사한다.
cart
인스턴스에 담겨있는 장바구니 정보 불러오기
OrercreateForm
에서 받아온 form
정보 인스턴스화 하기
form
의 validation check
후 order
table에 등록
order = {
first_name,
last_name,
email,
address,
postal_code,
city,
created, // form에 없음
updated, // form에 없음
paid, // form에 없음
coupon, // form에 없음, cart에서 받음
discount, // form에 없음, cart에서 받음
}
만약 cart
에 coupon
정보가 있다면, order에 저장한다. 이때 db
에 지연저장을 위해 아래와 같이 처리한다.
if form.is_valid():
order = form.save(commit=False) # 아직 커밋 하지 말라!
if cart.coupon:
order.coupon = cart.coupon
order.discount = cart.coupon.amount
order = form.save()
- `order` table에 생성된 `order` instance와 관계테이블로 구성된 `OrderItem `table에 `cart`정보(`order_id`, `product`, `price`, `quantity`) 등록
```python
OrderItem.objects.bulk_create(
[OrderItem(
order=order, # order instance를 등록할시 알아서 id로 적용
product=item['product'],
price=item['price'],
qunatity=item['quantity']) for item in cart]
)
```
- `cart` 인스턴스 삭제
- `order_id`를 반환하므로써 `http`통신에 전달하는 데이터 양을 줄이고, 해당 `key`로 저장했던 모든 데이터를 불러올 수 있도록 설계함
1의 과정에서 order_id
를 제대로 반환받았으면, form
에 없었던 amount
와 pay_method
를 html selector
를 이용하여 받아온 뒤 AjaxStoreTransaction(e, order_id, amount, pay_method)
을 실행한다.
order/urls.py
에 order_checkout
으로 바인딩된 OrderCheckoutAjaxView
클래스를 실행한다.
request
의 user
가 로그인 상태인지 검사한다.
order_id
를 통해, order
객체를 불러온다.
order
객체를 통해 amount
값을 찾는다.
order
, amount
를 기준으로 OrderTransaction
table에 등록한다. 이때 추가로 등록되는 데이터는 merchant_order_id
로, iamport
에서 결제에 대한 get
요청을 할때 필요한 key
값이라고 생각하면 된다.
등록할때는 orderTransaction.objects.create_new()
함수를 이용한다.
merchant_order_id = OrderTransaction.objects.create_new(
order=order,
amount=amount,
pay_method=pay_method
)
order/models.py
에는 OrderTransactionManager
클래스를 이용하여 orderTransaction
model의 objects
객체에 추가적인 함수를 등록하도록 한다.
# OrderTransactionManager 전문
class OrderTransactionManager(models.Manager):
def create_new(self, order, amount, pay_method, success=None, transaction_status=None):
if not order:
raise ValueError('주문 오류')
order_hash = hashlib.sha1(str(order.id).encode('utf-8')).hexdigest() # 16진법으로 바꾼 order.id를 utf-8로 인코딩한 해시값
email_hash = str(order.email).split('@')[0] # 이메일의 아이디 부분만 사용
final_hash = hashlib.sha1((order_hash + email_hash).encode('utf-8')).hexdigest()[:10] # 0~9index까지의 16진수만 받자...
merchant_order_id = '%s' % final_hash
# validation기능의 함수 실행
payments_prepare(merchant_order_id, amount)
transaction = self.model(
order=order,
merchant_order_id=merchant_order_id,
amount=int(amount),
pay_method=pay_method,
)
if success is not None:
transaction.success = success
transaction.transaction_status = transaction_status
try:
transaction.save()
except Exception as e:
print('save error: ', e)
return transaction.merchant_order_id
def get_transaction(self, merchant_order_id):
result = find_transcaction(merchant_order_id)
if result['status'] == 'paid':
return result
return None
OrderTransaction
에 저장되는 데이터는 다음과 같다.
orderTransaction = {
order_id, // foreginKey
merchant_order_id,
amount,
pay_method,
transaction_id, // imp_id?? 결재 SDK에서 제공?하는 id인듯 아직 이시점에서는 null값으로 유지된다. OrderImAjaxView에서 등록되는 로직
created, // 생성날짜
}
아직 transaction_id
가 생성되지 않은 상태에서 db
등록과정은 일단락 하고, merchant_order_id
를 반환한다.
현재 submit
event
로 진행된 후 가지고 있는 데이터는 merchant_uid(merchant_order_id), name, order_id, buyer_name, buyer_email, amount
가 있다. 이 데이터로 iamport API
를 이용할 수 있다.
IMP.request_pay()
함수를 실행하면 브라우저에 결제화면이 켜지면서 결제를 하면 된다.
결제가 완료되면, promise
객체를 callback
한다.
promise
에 담겨져 있는 정보와, 우리 db
에 있는 정보가 동일한지 체크하는 작업을 한다.
ImpTransaction(e, order_id, response.merchant_uid, response.imp_uid, response.paid_amount);
를 실행한다.
order/urls.py
에서 order_validation_url
으로 바인딩 된 OrderImpAjaxView
class를 찾는다
class OrderImpAjaxView(View):
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return JsonResponse({'autenticated':False}, status=403)
order_id = request.POST.get('order_id')
order = Order.objects.get(id=order_id)
merchant_id = request.POST.get('merchant_id')
imp_id = request.POST.get('imp_id')
amount = request.POST.get('amount')
여기서 변수에 할당된 값들은 iamport
의 promise
에서 반환된 값들이다.
해당 정보로부터 transaction
인스턴스를 불러온 뒤, transaction_id
에 imp_id
를 저장시킨다.
try:
trans = OrderTransaction.objects.get(
order=order,
merchant_order_id=merchant_id,
amount=amount
)
except:
trans = None
if trans is not None:
trans.transaction_id = imp_id
trans.success = True
trans.save()
order.paid = True
order.save()
이때, trans.save()
로 인해 order/models.py/order_payment_validation()
가 발동되어 자동적으로 거래에 대한 validation
을 평가한다.
문제가 없다면, $(location).attr('href', location.origin+order_complete_url+'?order_id='+order_id)
이 코드로 리다이렉션이 되어야 한다.
false를 반환하며 submit event
에 바인딩된 작업이 종료된다.
file: order/models.py
,class: OderTransactionManager
/ function: create_new
에서 인자로 success
와 success_status
를 None
으로 설정했는데, 코드에서는 쓰질 않는다. 이때 이 인자의 역할이 무엇인지 궁금하다. 설명도 없다.pay_method
도 table에 저장 안했네 책 퀄리티 좋던데 왜 이 부분만 유독 부족한거같지?