결제 버튼 클릭 시 자바스크립트 처리
사용자가 '결제하기' 버튼을 클릭하면, AJAX 요청을 통해 서버에 결제 요청을 보낸다. 서버는 알리페이 결제 페이지의 URL을 반환하며, 이는 window.open(data.pay_url)를 통해 브라우저에서 새 탭으로 열린다.
결제 페이지로 이동한 후, 지속적으로 결제 상태를 확인하는 요청을 보내며, 결제가 완료되면 alert('결제 성공') 메시지를 표시하고 페이지를 새로 고친다.
<script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
<script type="text/javascript">
// 주문 상태에 따라 버튼 텍스트 변경
$('.oper_btn').each(function() {
const status = $(this).attr('order_status');
let displayText = '';
switch(status) {
case '1': displayText = '결제하기'; break;
case '4': displayText = '평점 남기기'; break;
default: displayText = $(this).attr('status_name'); break;
}
$(this).text(displayText);
});
// 결제 버튼 클릭 이벤트
$('.oper_btn').click(function() {
const orderId = $(this).attr('order_id');
const payMethod = $(this).attr('pay_method');
if (payMethod === '3') {
sendPaymentRequest(orderId);
} else if (payMethod === '4') {
location.href = '/order/comment/' + orderId;
}
});
function sendPaymentRequest(orderId) {
const csrfToken = $('input[name="csrfmiddlewaretoken"]').val();
const data = {
order_id: orderId,
csrfmiddlewaretoken: csrfToken
};
$.post('/order/alipay/', data, function(response) {
if (response.status === 'S') {
window.open(response.pay_url);
// 결제 상태 확인 요청
checkPaymentStatus(orderId);
} else {
alert(response.errmsg);
}
});
}
function checkPaymentStatus(orderId) {
const csrfToken = $('input[name="csrfmiddlewaretoken"]').val();
const data = {
order_id: orderId,
csrfmiddlewaretoken: csrfToken
};
setInterval(function() {
$.post('/order/check/', data, function(res) {
if (res.status === 'S') {
alert('결제 완료!');
location.reload();
} else {
console.log('결제 상태 확인 중:', res.errmsg);
}
});
}, 5000); // 5초마다 재시도
}
</script>
알리페이 결제 인터페이스 연동 절차
알리페이 개발자 플랫폼에서 제공하는 샌드박스 환경을 활용해 결제 기능을 구현한다. 공식 문서에서는 다음과 같은 단계를 권장한다.
1. 애플리케이션 생성 및 설정
샌드박스 환경에 접속하여, 기본적으로 제공되는 애플리케이션을 사용할 수 있다. 필요한 경우 별도의 앱을 등록하고, 승인 절차를 거쳐야 한다.
2. 키 쌍 생성
세 가지 핵심 키가 필요하다:
- 앱 개인키: 요청 데이터를 자체 개인키로 서명한다.
- 앱 공개키: 서명된 데이터와 함께 전송되며, 알리페이에서 해독에 사용된다.
- 알리페이 공개키: 알리페이에서 응답을 암호화할 때 사용된다.
키 생성은 [Alipay Open Platform Developer Helper](https://opendocs.alipay.com/open/291/105971) 도구를 통해 가능하며, 형식은 PKCS1로 설정해야 한다.
3. SDK 설치
Python용 공식 SDK를 설치한다.
pip install alipay-sdk-python==3.3.398
4. 서버 측 코드 작성
알리페이의 alipay.trade.page.pay API를 사용하여 결제 페이지의 URL을 발급받는다. 이는 사용자가 직접 로그인하여 결제를 진행하도록 유도한다.
4-1. 모듈 임포트 및 초기화
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.domain.AlipayTradePagePayModel import AlipayTradePagePayModel
from alipay.aop.api.request.AlipayTradePagePayRequest import AlipayTradePagePayRequest
def init_alipay_client():
config = AlipayClientConfig()
config.server_url = settings.ALIPAY_SERVER_URL
config.app_id = settings.ALIPAY_APP_ID
config.app_private_key = settings.ALIPAY_PRIVATE_KEY
config.alipay_public_key = settings.ALIPAY_PUBLIC_KEY
return DefaultAlipayClient(config)
4-2. 설정 파일 구성 (settings.py)
ALIPAY_SERVER_URL = 'https://openapi.alipaydev.com/gateway.do'
ALIPAY_APP_ID = '2016102200739747'
ALIPAY_PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----'
ALIPAY_PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----'
ALIPAY_EXPRESS = '10m' # 거래 유효 시간 (분)
ALIPAY_RETURN_URL = 'http://localhost:8000/order/return/'
ALIPAY_NOTIFY_URL = 'http://localhost:8000/order/notify/'
4-3. 결제 요청 생성 및 실행
def create_payment_url(order):
client = init_alipay_client()
model = AlipayTradePagePayModel()
model.out_trade_no = order.order_num
model.total_amount = str(order.total_amount)
model.subject = "천천히 생선"
model.product_code = "FAST_INSTANT_TRADE_PAY"
model.timeout_express = settings.ALIPAY_EXPRESS
request = AlipayTradePagePayRequest(biz_model=model)
request.return_url = settings.ALIPAY_RETURN_URL
request.notify_url = settings.ALIPAY_NOTIFY_URL
return client.page_execute(request, http_method='GET')
4-4. 결제 처리 뷰 클래스
class OrderAlipayView(View):
def post(self, request):
context = {'status': 'E', 'errmsg': ''}
user = request.user
if not user.is_authenticated:
context['errmsg'] = '로그인이 필요합니다.'
return JsonResponse(context)
order_id = int(request.POST.get('order_id'))
try:
order = OrderInfo.objects.get(id=order_id)
except OrderInfo.DoesNotExist:
context['errmsg'] = '주문 정보가 존재하지 않습니다.'
return JsonResponse(context)
try:
pay_url = create_payment_url(order)
context['status'] = 'S'
context['pay_url'] = pay_url
except Exception as e:
context['errmsg'] = f'결제 준비 실패: {str(e)}'
return JsonResponse(context)
결제 상태 조회 기능 구현
사용자의 실제 결제 완료 여부를 확인하기 위해 반복적인 상태 체크가 필요하다.
from alipay.aop.api.domain.AlipayTradeQueryModel import AlipayTradeQueryModel
from alipay.aop.api.request.AlipayTradeQueryRequest import AlipayTradeQueryRequest
def query_payment_status(order, context):
client = init_alipay_client()
model = AlipayTradeQueryModel()
model.out_trade_no = order.order_num
model.product_code = 'FAST_INSTANT_TRADE_PAY'
request = AlipayTradeQueryRequest(biz_model=model)
while True:
response = client.execute(request)
result = eval(response) # 문자열을 딕셔너리로 변환
code = result.get('code')
sub_code = result.get('sub_code')
trade_status = result.get('trade_status')
if code == '10000' and trade_status == 'TRADE_SUCCESS':
context['status'] = 'S'
order.trade_no = result.get('trade_no')
order.order_status = 4 # 평가 대기 상태
order.save()
break
elif sub_code in ['ACQ.TRADE_NOT_EXIST', 'UNKNOWN']:
time.sleep(5)
continue
else:
context['errmsg'] = f'결제 실패: {sub_code} - {result.get("sub_msg")}'
break
class OrderCheckView(View):
def post(self, request):
context = {'status': 'E', 'errmsg': ''}
user = request.user
if not user.is_authenticated:
context['errmsg'] = '로그인이 필요합니다.'
return JsonResponse(context)
order_id = int(request.POST.get('order_id'))
try:
order = OrderInfo.objects.get(id=order_id)
except Exception:
context['errmsg'] = '주문이 존재하지 않습니다.'
return JsonResponse(context)
query_payment_status(order, context)
return JsonResponse(context)
핵심 동작 설명
- 결제 상태는 비동기 방식으로 확인되며,
setInterval또는 반복 요청을 통해 5초 간격으로 조회된다. - 응답은 문자열 형태이므로
eval()로 딕셔너리로 변환해야 한다. - 반환 코드 의미:
10000: 성공40004,ACQ.TRADE_NOT_EXIST: 거래 없음 (사용자 미로그인)WAIT_BUYER_PAY: 결제 대기 중TRADE_SUCCESS: 결제 완료