|
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 |
7 | 14 | from robokassa.conf import USE_POST
|
8 |
| -from robokassa.forms import ResultURLForm, SuccessRedirectForm, FailRedirectForm |
9 | 15 | from robokassa.models import SuccessNotification
|
| 16 | +from robokassa.forms import ResultURLForm, SuccessRedirectForm, FailRedirectForm |
10 | 17 | from robokassa.signals import result_received, success_page_visited, fail_page_visited
|
11 | 18 |
|
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