Skip to content

Commit 7cb6b5b

Browse files
committed
initial commit
1 parent 7acbab7 commit 7cb6b5b

File tree

7 files changed

+213
-85
lines changed

7 files changed

+213
-85
lines changed

.gitignore

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
*.swp
2+
*.pyc
3+
*.orig
4+
*~
5+
6+
#setup files
7+
build/
8+
dist/
9+
.build/
10+
MANIFEST
11+
django_robokassa.egg-info
12+
.tox
13+

CHANGES.rst

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11

2-
История изменений
3-
=================
2+
История изменений версия для Oscar
3+
=================================
4+
5+
0.1.0 (2013-11-23)
6+
-----------------
7+
Первая версия
8+
9+
10+
История изменений оригинал
11+
==========================
412

513
1.1 (2013-04-12)
614
------------------

LICENSE.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
Copyright (c) 2010 Mikhail Korobov
2+
Copyright (c) 2013 Maxim Perov
23

34
Permission is hereby granted, free of charge, to any person obtaining a copy
45
of this software and associated documentation files (the "Software"), to deal
@@ -16,4 +17,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1617
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1718
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1819
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19-
THE SOFTWARE.
20+
THE SOFTWARE.

robokassa/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@
2222
FORM_TARGET = u'http://test.robokassa.ru/Index.aspx'
2323

2424
# список пользовательских параметров ("shp" к ним приписывать не нужно)
25-
EXTRA_PARAMS = sorted(getattr(settings, 'ROBOKASSA_EXTRA_PARAMS', []))
25+
EXTRA_PARAMS = sorted(getattr(settings, 'ROBOKASSA_EXTRA_PARAMS', ['order_num',]))

robokassa/facade.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# set encoding=utf-8
2+
""" this is oscar frontend for using communication routines from
3+
django-robokassa"""
4+
5+
from oscar.core.loading import get_class
6+
7+
8+
from robokassa.forms import RobokassaForm
9+
from robokassa.conf import EXTRA_PARAMS
10+
RedirectRequired = get_class('oscar.apps.payment.exceptions','RedirectRequired')
11+
12+
def pre_auth(basket_num, amount, **kwargs):
13+
""" This will be called from PaymentDetailsView.handle_payment,
14+
it supposed to generate url for Robokassa, inject it into RedirectRequired
15+
error and raise it
16+
"""
17+
initial={'OutSum': amount, 'InvId': basket_num}
18+
for key in kwargs:
19+
if key in EXTRA_PARAMS:
20+
initial[key] = kwargs[key]
21+
22+
form = RobokassaForm(initial=initial)
23+
err = RedirectRequired(form.get_redirect_url())
24+
raise err
25+
26+
27+
28+

robokassa/urls.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
1-
#coding: utf-8
2-
from django.conf.urls.defaults import *
1+
from django.conf.urls import patterns, url
2+
from django.views.decorators.csrf import csrf_exempt
3+
from robokassa import views
34

4-
urlpatterns = patterns('robokassa.views',
5+
urlpatterns = patterns('',
56
url(
67
r'^result/$',
7-
'receive_result',
8+
csrf_exempt(views.ResultResponseView.as_view()),
89
name='robokassa_result'
910
),
1011
url(
1112
r'^success/$',
12-
'success',
13+
csrf_exempt(views.SuccessResponseView.as_view()),
1314
name='robokassa_success'
1415
),
1516
url(
1617
r'^fail/$',
17-
'fail',
18+
csrf_exempt(views.FailResponseView.as_view()),
1819
name='robokassa_fail'
1920
),
2021
)

robokassa/views.py

+152-75
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,156 @@
1-
#coding: utf-8
2-
3-
from django.http import HttpResponse
4-
from django.template.response import TemplateResponse
5-
from django.views.decorators.csrf import csrf_exempt
6-
1+
# set encoding=utf-8
2+
3+
from logging import getLogger
4+
log = getLogger(__name__)
5+
6+
from django.views.generic import RedirectView, View
7+
from django.shortcuts import get_object_or_404
8+
from django.db.models import get_model
9+
from django.core.urlresolvers import reverse
10+
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound
11+
from django.contrib import messages
12+
from oscar.core.loading import get_class
13+
from oscar.apps.payment.models import SourceType, Source
714
from robokassa.conf import USE_POST
8-
from robokassa.forms import ResultURLForm, SuccessRedirectForm, FailRedirectForm
915
from robokassa.models import SuccessNotification
16+
from robokassa.forms import ResultURLForm, SuccessRedirectForm, FailRedirectForm
1017
from robokassa.signals import result_received, success_page_visited, fail_page_visited
1118

12-
@csrf_exempt
13-
def receive_result(request):
14-
""" обработчик для ResultURL. """
15-
data = request.POST if USE_POST else request.GET
16-
form = ResultURLForm(data)
17-
if form.is_valid():
18-
id, sum = form.cleaned_data['InvId'], form.cleaned_data['OutSum']
19-
20-
# сохраняем данные об успешном уведомлении в базе, чтобы
21-
# можно было выполнить дополнительную проверку на странице успешного
22-
# заказа
23-
notification = SuccessNotification.objects.create(InvId = id, OutSum = sum)
24-
25-
# дополнительные действия с заказом (например, смену его статуса) можно
26-
# осуществить в обработчике сигнала robokassa.signals.result_received
27-
result_received.send(sender = notification, InvId = id, OutSum = sum,
28-
extra = form.extra_params())
29-
30-
return HttpResponse('OK%s' % id)
31-
return HttpResponse('error: bad signature')
32-
33-
34-
@csrf_exempt
35-
def success(request, template_name='robokassa/success.html', extra_context=None,
36-
error_template_name = 'robokassa/error.html'):
37-
""" обработчик для SuccessURL """
38-
39-
data = request.POST if USE_POST else request.GET
40-
form = SuccessRedirectForm(data)
41-
if form.is_valid():
42-
id, sum = form.cleaned_data['InvId'], form.cleaned_data['OutSum']
43-
44-
# в случае, когда не используется строгая проверка, действия с заказом
45-
# можно осуществлять в обработчике сигнала robokassa.signals.success_page_visited
46-
success_page_visited.send(sender = form, InvId = id, OutSum = sum,
47-
extra = form.extra_params())
48-
49-
context = {'InvId': id, 'OutSum': sum, 'form': form}
50-
context.update(form.extra_params())
51-
context.update(extra_context or {})
52-
return TemplateResponse(request, template_name, context)
53-
54-
return TemplateResponse(request, error_template_name, {'form': form})
55-
56-
57-
@csrf_exempt
58-
def fail(request, template_name='robokassa/fail.html', extra_context=None,
59-
error_template_name = 'robokassa/error.html'):
60-
""" обработчик для FailURL """
61-
62-
data = request.POST if USE_POST else request.GET
63-
form = FailRedirectForm(data)
64-
if form.is_valid():
65-
id, sum = form.cleaned_data['InvId'], form.cleaned_data['OutSum']
66-
67-
# дополнительные действия с заказом (например, смену его статуса для
68-
# разблокировки товара на складе) можно осуществить в обработчике
69-
# сигнала robokassa.signals.fail_page_visited
70-
fail_page_visited.send(sender = form, InvId = id, OutSum = sum,
71-
extra = form.extra_params())
72-
73-
context = {'InvId': id, 'OutSum': sum, 'form': form}
74-
context.update(form.extra_params())
75-
context.update(extra_context or {})
76-
return TemplateResponse(request, template_name, context)
77-
78-
return TemplateResponse(request, error_template_name, {'form': form})
79-
19+
PaymentDetailsView = get_class('oscar.apps.checkout.views',
20+
'PaymentDetailsView')
21+
Basket = get_model('basket', 'Basket')
22+
23+
24+
class ProcessData(object):
25+
def get_data(self, request):
26+
if request.method == 'GET' and not USE_POST:
27+
return request.GET
28+
elif request.method == 'POST' and USE_POST:
29+
return request.POST
30+
31+
def process_data(self, data):
32+
""" here is where all the checking occurs """
33+
self.robokassa_cleaned_data = None
34+
self.robokassa_extra_params = {}
35+
form = self.form(data)
36+
if form.is_valid():
37+
self.robokassa_cleaned_data = form.cleaned_data
38+
self.robokassa_extra_params = form.extra_params()
39+
40+
@property
41+
def basket_num(self):
42+
return self.robokassa_cleaned_data.get('InvId')
43+
44+
@property
45+
def robokassa_amount(self):
46+
return self.robokassa_cleaned_data.get('OutSum')
47+
48+
@property
49+
def order_num(self):
50+
return self.robokassa_extra_params.get('OrderNum', None)
51+
52+
53+
class SuccessResponseView(PaymentDetailsView, ProcessData):
54+
""" Landing page for succesfull redirects from ROBOKASSA
55+
Should check the parameters and if OK render the thankyou page
56+
"""
57+
form = SuccessRedirectForm
58+
59+
def dispatch(self, request, *args, **kwargs):
60+
data = self.get_data(request)
61+
if data is None:
62+
return self.http_method_not_allowed(request, *args, **kwargs)
63+
self.process_data(data)
64+
if self.robokassa_cleaned_data is None:
65+
messages.error(
66+
self.request,
67+
(u"Возникли ошибки при обработке Вашего платежа, пожалуйста, "
68+
u"свяжитесь с нами по телефону"))
69+
log.error("SuccessRedirect error: bad data")
70+
return HttpResponseRedirect(reverse('basket:summary'))
71+
72+
# lets find the basket
73+
try:
74+
self.basket = Basket.objects.get(id=self.basket_num,
75+
status=Basket.FROZEN)
76+
except Basket.DoesNotExist:
77+
messages.error(
78+
self.request,
79+
u"Данному платежу не соответствует ни одна корзина")
80+
return HttpResponseRedirect(reverse('basket:summary'))
81+
82+
# keep this for legacy
83+
success_page_visited.send(sender = self,
84+
InvId = self.basket_num, OutSum = self.robokassa_amount,
85+
extra = self.robokassa_extra_params)
86+
# if everything OK finish order placement
87+
return self.submit(self.basket)
88+
89+
def generate_order_number(self, basket):
90+
""" we already have an order_number, just return it """
91+
return self.order_num if self.order_num else \
92+
super(SuccessResponseView, self).generate_order_number(basket)
93+
94+
def handle_payment(self, order_number, total_incl_tax, **kwargs):
95+
"""
96+
finalize robokassa payment
97+
"""
98+
99+
amount_allocated = self.robokassa_amount
100+
# Record payment source and event
101+
source_type, is_created = SourceType.objects.get_or_create(name='Robokassa')
102+
source = Source(source_type=source_type, amount_debited=amount_allocated)
103+
self.add_payment_source(source)
104+
self.add_payment_event('settled', amount_allocated)
105+
106+
107+
class FailResponseView(RedirectView, ProcessData):
108+
permanent = False
109+
form = FailRedirectForm
110+
111+
def dispatch(self, request, *args, **kwargs):
112+
data = self.get_data(request)
113+
if self.data is None:
114+
return self.http_method_not_allowed(request, *args, **kwargs)
115+
self.process_data(data)
116+
if self.robokassa_cleaned_data is None:
117+
return HttpResponseNotFound
118+
basket = get_object_or_404(Basket, id=self.basket_num,
119+
status=Basket.FROZEN)
120+
basket.thaw()
121+
122+
# keep this for legacy
123+
fail_page_visited.send(sender = basket,
124+
InvId = self.basket_num, OutSum = self.robokassa_amount,
125+
extra = self.robokassa_extra_params)
126+
127+
return super(FailResponseView, self).dispatch(request,
128+
*args, **kwargs)
129+
130+
def get_redirect_url(self, **kwargs):
131+
messages.error(self.request, u"Платеж через Робокассу отменен")
132+
return reverse('basket:summary')
133+
134+
135+
class ResultResponseView(View, ProcessData):
136+
form = ResultURLForm
137+
138+
def dispatch(self, request, *args, **kwargs):
139+
data = self.get_data(request)
140+
if self.data is None:
141+
return self.http_method_not_allowed(request, *args, **kwargs)
142+
self.process_data(data)
143+
if self.robokassa_cleaned_data is None:
144+
return HttpResponse('error: bad signature')
145+
basket = get_object_or_404(Basket, id=self.basket_num,
146+
status=Basket.FROZEN)
147+
148+
# checking complete: create notification and send confirmation
149+
SuccessNotification.objects.create(
150+
InvId = self.basket_num, OutSum = self.robokassa_amount)
151+
# keeping this for legacy
152+
result_received.send(sender = basket,
153+
InvId = self.basket_num, OutSum = self.robokassa_amount,
154+
extra = self.robokassa_extra_params)
155+
156+
return HttpResponse('OK%s' % self.basket_num)

0 commit comments

Comments
 (0)