Django - import 아임포트 결제 시스템 구현하기
2023. 11. 24. 12:29ㆍDjango
Frontend
1. 포트원 SDK 설치
npm i @portone/browser-sdk
2. index.html head 안에 아임포트 sdk 라이브러리 추가
//index.html
<!-- 포트원 JavaScript SDK 로드 -->
<script src="https://cdn.iamport.kr/v1/iamport.js"></script>
3. 결제 요청하기
src/components/ 아래에 PaymentComponents.js 파일을 만들어 결제 요청 코드를 작성했다.
function 안에 PortOne.requestPayment 하위 내용은 결제정보를 다듬어야 한다. (특히 storeId는 우리걸로 변경해야함)
import React from 'react';
import * as PortOne from '@portone/browser-sdk/v2';
function PaymentComponent() {
const requestPayment = () => {
PortOne.requestPayment({
storeId: 'store-4ff4af41-85e3-4559-8eb8-0d08a2c6ceec',
paymentId: `paymentId_${Date.now()}`,
orderName: '나무프레임',
totalAmount: 1000,
currency: 'CURRENCY_KRW',
pgProvider: 'PG_PROVIDER_TOSSPAYMENTS',
payMethod: 'CARD'
});
};
return (
<div>
<h1>Your Payment Page</h1>
<button onClick={requestPayment}>Request Payment</button>
</div>
);
}
export default PaymentComponent;
Backend
1. 결제검증 API 구현하기
portone에 나와있는 예시는 아래의 첫번째 코드와 같이 Node.js 버전으로 작성되어 있다. 우리는 Django를 사용하고 있기 때문에 Chat GPT의 도움을 받아 예시를 받았고 두번째 코드에서 확인할 수 있다.
#Express.js
// bodyParser 등을 통해 body의 JSON 데이터를 파싱할 수 있는지 확인해주세요.
app.use(bodyParser.json());
// POST 요청을 받는 /payments/complete
app.post("/payments/complete", async (req, res) => {
try {
// 요청의 body로 SDK의 응답 중 txId와 paymentId가 오기를 기대합니다.
const { txId, paymentId } = req.body;
// 1. 포트원 API를 사용하기 위해 액세스 토큰을 발급받습니다.
const signinResponse = await axios({
url: "https://api.portone.io/v2/signin/api-key",
method: "post",
headers: { "Content-Type": "application/json" },
data: {
api_key: PORTONE_API_KEY, // 포트원 API Key
},
});
const { access_token } = signinResponse.data;
// 2. 포트원 결제내역 단건조회 API 호출
const paymentResponse = await axios({
url: `https://api.portone.io/v2/payments/${paymentId}`,
method: "get",
// 1번에서 발급받은 액세스 토큰을 Bearer 형식에 맞게 넣어주세요.
headers: { "Authorization": "Bearer " + access_token },
});
const { payment: { id, transactions } } = paymentResponse.data;
// 대표 트랜잭션(승인된 트랜잭션)을 선택합니다.
const transaction = transactions.find(tx => tx.is_primary === true);
// 3. 가맹점 내부 주문 데이터의 가격과 실제 지불된 금액을 비교합니다.
const order = await OrderService.findById(id);
if (order.amount === transaction.amount.total) {
switch (status) {
case "VIRTUAL_ACCOUNT_ISSUED": {
const { virtual_account } = transaction.payment_method_detail;
// 가상 계좌가 발급된 상태입니다.
// 계좌 정보(virtual_account)를 이용해 원하는 로직을 구성하세요.
break;
}
case "PAID": {
// 모든 금액을 지불했습니다! 완료 시 원하는 로직을 구성하세요.
break;
}
}
} else {
// 결제 금액이 불일치하여 위/변조 시도가 의심됩니다.
}
} catch (e) {
// 결제 검증에 실패했습니다.
res.status(400).send(e);
}
});
# views.py
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
import requests
@csrf_exempt
def complete_payment(request):
try:
# 요청의 body로 SDK의 응답 중 txId와 paymentId가 오기를 기대합니다.
data = request.POST # 또는 request.body를 사용하여 데이터를 추출
# 1. 포트원 API를 사용하기 위해 액세스 토큰을 발급받습니다.
signin_response = requests.post(
"https://api.portone.io/v2/signin/api-key",
headers={"Content-Type": "application/json"},
json={"api_key": "PORTONE_API_KEY"} # 포트원 API Key
)
access_token = signin_response.json()["access_token"]
# 2. 포트원 결제내역 단건조회 API 호출
payment_response = requests.get(
f"https://api.portone.io/v2/payments/{data['paymentId']}",
headers={"Authorization": f"Bearer {access_token}"}
)
payment_data = payment_response.json()["payment"]
transactions = payment_data["transactions"]
# 대표 트랜잭션(승인된 트랜잭션)을 선택합니다.
transaction = next(tx for tx in transactions if tx["is_primary"])
# 3. 가맹점 내부 주문 데이터의 가격과 실제 지불된 금액을 비교합니다.
order = OrderService.find_by_id(payment_data["id"])
if order.amount == transaction["amount"]["total"]:
if transaction["status"] == "VIRTUAL_ACCOUNT_ISSUED":
virtual_account = transaction["payment_method_detail"]["virtual_account"]
# 가상 계좌가 발급된 상태입니다.
# 계좌 정보(virtual_account)를 이용해 원하는 로직을 구성하세요.
elif transaction["status"] == "PAID":
# 모든 금액을 지불했습니다! 완료 시 원하는 로직을 구성하세요.
return JsonResponse({"success": True})
else:
# 결제 금액이 불일치하여 위/변조 시도가 의심됩니다.
return JsonResponse({"error": "Invalid payment amount"}, status=400)
except Exception as e:
# 결제 검증에 실패했습니다.
return JsonResponse({"error": str(e)}, status=400)
// 이 코드는 Django에서 외부 API 호출을 위해 requests 라이브러리를 사용하고 있습니다. Django에서는 CSRF 보호를 기본으로 사용하고 있기 때문에 @csrf_exempt 데코레이터를 사용하여 CSRF 보호를 비활성화하였습니다. 이는 보안상 주의해야 할 점이므로 사용 시 유의하시기 바랍니다.
또한, 이 코드를 사용하기 위해서는 필요한 Django 모델 및 서비스(OrderService)에 대한 정의가 필요합니다. 이 부분은 프로젝트의 구조와 데이터 모델에 따라 적절하게 조정되어야 합니다.
'Django' 카테고리의 다른 글
| [Django] What is Serialization? (0) | 2023.10.31 |
|---|---|
| [Django] Connecting Frontend and Backend and Distribution 백엔드와 프런트엔드의 연결과 배포 (1) | 2023.10.31 |
| [Django] Postman - followings/followers serializers 설정 (0) | 2023.10.04 |
| [Django] Postman - refresh token 이용해서 login 하기 (0) | 2023.10.02 |
| [Django] postman 이용하여 수정하기 API 만들기 (0) | 2023.09.25 |