This repository has been archived by the owner on Jan 10, 2024. It is now read-only.
/
app.py
139 lines (123 loc) · 5.89 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.db.models import Q
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.views.decorators.http import require_POST
from ..core.app import SatchlessApp, view
from ..order import handler
from ..order.signals import order_pre_confirm
from ..payment import PaymentFailure, ConfirmationFormNeeded
from ..contrib.order.partitioner.simple import SimplePartitioner
class CheckoutApp(SatchlessApp):
app_name = 'checkout'
namespace = 'checkout'
order_session_key = 'checkout-order'
Order = None
confirmation_templates = [
'satchless/checkout/confirmation.html',
]
def __init__(self, cart_app, *args, **kwargs):
self.cart_app = cart_app
self.delivery_queue = kwargs.pop('delivery_provider',
handler.DeliveryQueue(*getattr(settings, 'SATCHLESS_DELIVERY_PROVIDERS', [])))
self.payment_queue = kwargs.pop('payment_provider',
handler.PaymentQueue(*getattr(settings, 'SATCHLESS_PAYMENT_PROVIDERS', [])))
self.delivery_partitioner = kwargs.pop('delivery_partitioner',
handler.PartitionerQueue(*getattr(settings, 'SATCHLESS_ORDER_PARTITIONERS',
[SimplePartitioner])))
super(CheckoutApp, self).__init__(*args, **kwargs)
assert self.Order, ('You need to subclass CheckoutApp and provide Order')
def get_order(self, request, order_token):
try:
return self.Order.objects.get(token=order_token)
except self.Order.DoesNotExist:
return
def redirect_order(self, order):
if not order or order.is_empty():
return self.cart_app.redirect('details')
elif order.status == 'checkout':
return self.redirect('checkout',
order_token=order.token)
elif order.status == 'payment-pending':
return self.redirect('confirmation',
order_token=order.token)
return redirect('order:details', order_token=order.token)
def partition_cart(self, cart, order, **pricing_context):
delivery_groups, remaining_items = self.delivery_partitioner.partition(
cart, cart.get_all_items())
if remaining_items:
raise ImproperlyConfigured('Unhandled items remaining in cart.')
for delivery_group in filter(None, delivery_groups):
order_delivery_group = order.create_delivery_group(delivery_group)
for cartitem in delivery_group:
price = self.cart_app.pricing_handler.get_variant_price(
cartitem.variant.get_subtype_instance(), currency=cart.currency,
quantity=cartitem.quantity, cart=cartitem.cart,
cartitem=cartitem, **pricing_context)
order_delivery_group.add_item(cartitem.variant, cartitem.quantity, price)
def get_order_from_cart(self, request, cart, order=None):
if not order:
order = self.Order.objects.create(cart=cart, user=cart.owner,
currency=cart.currency)
elif order.is_empty():
order.groups.all().delete()
self.partition_cart(cart, order)
previous_orders = self.Order.objects.filter(
Q(cart=cart) & Q(status='checkout') & ~Q(pk=order.pk))
previous_orders.delete()
return order
@view(r'^prepare-order/$', name='prepare-order')
@method_decorator(require_POST)
def prepare_order(self, request):
cart = self.cart_app.get_cart_for_request(request)
if cart.is_empty():
return self.cart_app.redirect('details')
order_pk = request.session.get(self.order_session_key)
order = None
if order_pk:
try:
order = self.Order.objects.get(pk=order_pk, cart=cart,
status='checkout')
except self.Order.DoesNotExist:
pass
if not order or order.is_empty():
order = self.get_order_from_cart(request, cart)
if request.user.is_authenticated() and order.user != request.user:
order.user = request.user
order.save()
request.session[self.order_session_key] = order.pk
return self.redirect('checkout', order_token=order.token)
@view(r'^(?P<order_token>\w+)/reactivate/$', name='reactivate-order')
@method_decorator(require_POST)
def reactivate_order(self, request, order_token):
order = self.get_order(request, order_token)
if not order or order.status != 'payment-failed':
return self.redirect_order(order)
order.set_status('checkout')
return self.redirect('checkout', order_token=order.token)
@view(r'^(?P<order_token>\w+)/confirmation/$', name='confirmation')
def confirmation(self, request, order_token):
"""
Checkout confirmation
The final summary, where user is asked to review and confirm the order.
Confirmation will redirect to the payment gateway.
"""
order = self.get_order(request, order_token)
if not order or order.status != 'payment-pending':
return self.redirect_order(order)
order_pre_confirm.send(sender=self.Order, instance=order,
request=request)
try:
self.payment_queue.confirm(order=order)
except ConfirmationFormNeeded, e:
return TemplateResponse(request, self.confirmation_templates, {
'formdata': e,
'order': order,
})
except PaymentFailure:
order.set_status('payment-failed')
else:
order.set_status('payment-complete')
return redirect('order:details', order_token=order.token)