결제 게이트웨이 통합과 관리자 기능 확장
앞서 구축한 전자상거래 플랫폼에 결제 처리 기능을 추가하고, 운영 편의성을 높이기 위해 관리자 인터페이스를 확장하는 방법을 다룹니다. 실제 신용카드 결제 연동부터 주문 데이터 내보내기, PDF 영수증 자동 생성까지 실무에서 필요한 핵심 기능들을 구현합니다.
Braintree를 이용한 결제 시스템 구축
온라인 결제는 민감한 개인정보를 다루기 때문에 보안이 매우 중요합니다. PCI DSS(지불 카드 산업 보안 표준)를 준수하기 위해 직접 카드 정보를 처리하지 않고, 외부 결제 서비스 제공업체를 활용하는 것이 일반적입니다. 여기서는 Braintree를 사용하여 안전하게 결제를 처리하는 방법을 설명합니다.
먼저 개발 및 테스트용으로 Braintree 샌드박스 계정을 생성합니다. 회원 가입 후 발급받은 Merchant ID, Public Key, Private Key를 Django 설정 파일에 등록합니다:
# settings.py
BRAINTREE_MERCHANT_ID = 'your_merchant_id'
BRAINTREE_PUBLIC_KEY = 'your_public_key'
BRAINTREE_PRIVATE_KEY = 'your_private_key'
import braintree
braintree.Configuration.configure(
braintree.Environment.Sandbox,
BRAINTREE_MERCHANT_ID,
BRAINTREE_PUBLIC_KEY,
BRAINTREE_PRIVATE_KEY
)
주문 모델에 결제 관련 필드를 추가합니다:
# orders/models.py
class Order(models.Model):
paid = models.BooleanField(default=False)
transaction_id = models.CharField(max_length=100, blank=True)
결제 프로세스는 다음과 같은 순서로 진행됩니다:
- 사용자가 주문을 완료하면 세션에 주문 ID를 저장
- 결제 페이지로 리디렉션
- 브라우저에서 Hosted Fields를 통해 카드 정보 입력
- 임시 토큰 생성 후 백엔드로 전송
- 서버에서 최종 결제 요청 실행
결제 뷰는 GET 요청 시 클라이언트용 토큰을 생성하고, POST 요청 시 실제 거래를 실행합니다:
# payment/views.py
def process_payment(request):
order_id = request.session.get('order_id')
order = get_object_or_404(Order, id=order_id)
if request.method == 'POST':
nonce = request.POST.get('payment_method_nonce')
result = braintree.Transaction.sale({
'amount': f"{order.total_cost():.2f}",
'payment_method_nonce': nonce,
'options': {'submit_for_settlement': True}
})
if result.is_success:
order.paid = True
order.transaction_id = result.transaction.id
order.save()
return redirect('payment:success')
else:
return redirect('payment:failure')
else:
client_token = braintree.ClientToken.generate()
return render(request, 'payment/process.html', {
'order': order,
'client_token': client_token
})
프론트엔드에서는 Braintree JavaScript SDK를 사용해 안전한 입력 필드를 렌더링합니다:
<form id="payment-form">
<div id="card-number" class="field"></div>
<div id="cvv" class="field"></div>
<div id="expiration-date" class="field"></div>
<input type="hidden" name="payment_method_nonce" />
<button type="submit">결제하기</button>
</form>
<script src="https://js.braintreegateway.com/web/3.x/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.x/js/hosted-fields.min.js"></script>
<script>
braintree.client.create({authorization: '{{ client_token }}'}})
.then(function (clientInstance) {
return braintree.hostedFields.create({
client: clientInstance,
fields: {
number: {selector: '#card-number'},
cvv: {selector: '#cvv'},
expirationDate: {selector: '#expiration-date'}
}
});
})
.then(function (hostedFieldsInstance) {
document.getElementById('payment-form').addEventListener('submit', function (event) {
event.preventDefault();
hostedFieldsInstance.tokenize().then(function (payload) {
document.querySelector('[name="payment_method_nonce"]').value = payload.nonce;
this.submit();
}.bind(this));
});
});
</script>
CSV 형식으로 주문 데이터 내보내기
관리자는 특정 조건의 주문 목록을 CSV 파일로 추출해야 할 때가 있습니다. Django 관리자 액션을 커스터마이징하여 이 기능을 쉽게 추가할 수 있습니다.
# orders/admin.py
import csv
from django.http import HttpResponse
def export_orders_csv(modeladmin, request, queryset):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="orders.csv"'
writer = csv.writer(response)
writer.writerow(['ID', '성명', '이메일', '주소', '총액', '결제여부', '생성일'])
for order in queryset:
writer.writerow([
order.id,
f"{order.first_name} {order.last_name}",
order.email,
f"{order.address}, {order.postal_code} {order.city}",
order.total_cost(),
'완료' if order.paid else '대기',
order.created.strftime('%Y-%m-%d')
])
return response
export_orders_csv.short_description = "선택된 주문을 CSV로 내보내기"
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
actions = [export_orders_csv]
맞춤형 관리자 뷰와 PDF 문서 생성
기본 관리자 인터페이스 외에 특수한 요구사항을 충족시키기 위해 커스텀 뷰를 만들 수 있습니다. 예를 들어 주문 상세 정보를 인쇄용 형태로 보여주는 뷰를 만들어 보겠습니다.
@staff_member_required
def admin_order_detail(request, order_id):
order = get_object_or_404(Order, id=order_id)
return render(request, 'admin/orders/detail.html', {'order': order})
@staff_member_required
def admin_order_pdf(request, order_id):
order = get_object_or_404(Order, id=order_id)
html = render_to_string('orders/pdf.html', {'order': order})
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = f'filename=order_{order.id}.pdf'
weasyprint.HTML(string=html).write_pdf(response,
stylesheets=[weasyprint.CSS(settings.STATIC_ROOT + '/css/pdf.css')])
return response
PDF 생성에는 WeasyPrint 라이브러리를 사용합니다. HTML 템플릿과 CSS 스타일을 조합하여 전문적인 문서를 만들 수 있습니다. 특히 정적인 정적 파일 경로를 올바르게 설정해야 폰트나 이미지가 정상적으로 포함됩니다.
# settings.py
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
생성된 PDF 문서는 이메일 첨부파일로도 전송할 수 있습니다:
if result.is_success:
order.paid = True
order.save()
# PDF 생성 및 메일 전송
subject = f"My Shop - 주문서 #{order.id}"
body = "귀하의 최근 구매 건에 대한 영수증을 확인하세요."
email = EmailMessage(subject, body, 'no-reply@myshop.com', [order.email])
html = render_to_string('orders/pdf.html', {'order': order})
out = BytesIO()
weasyprint.HTML(string=html).write_pdf(out)
email.attach(f'order_{order.id}.pdf', out.getvalue(), 'application/pdf')
email.send()
return redirect('payment:success')
이처럼 Django는 외부 API 연동부터 문서 생성, 이메일 발송까지 다양한 업무 자동화 기능을 간결한 코드로 구현할 수 있도록 지원합니다. 특히 관리자 인터페이스의 유연한 확장성은 운영 효율성을 크게 높이는 데 기여합니다.