You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

299 lines
12 KiB

from decimal import Decimal
import arrow
import json
import logging
from datetime import timedelta
from urllib.parse import urlsplit
import datetime
import calendar
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.shortcuts import redirect, get_object_or_404
from django.views.generic import View, TemplateView
from django.views.decorators.csrf import csrf_exempt
from django.urls import reverse_lazy
from django.utils.decorators import method_decorator
from django.utils.timezone import now
from django.conf import settings
from paymentwall import Pingback, Product, Widget
from apps.course.models import Course
from apps.school.models import SchoolSchedule
from apps.payment.tasks import transaction_to_mixpanel, product_payment_to_mixpanel, transaction_to_roistat
from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment, UserBonus
logger = logging.getLogger('django')
class DisallowedPingbackHost(Exception):
pass
@method_decorator(login_required, name='dispatch')
class CourseBuySuccessView(TemplateView):
template_name = 'payment/course_payment_success.html'
def get(self, request, pk=None, *args, **kwargs):
course = get_object_or_404(Course, pk=pk)
return self.render_to_response(context={'course': course})
@method_decorator(login_required, name='dispatch')
class SchoolBuySuccessView(TemplateView):
template_name = 'payment/payment_success.html'
def get(self, request, pk=None, *args, **kwargs):
return self.render_to_response(context={'school': True})
@method_decorator(login_required, name='dispatch')
class CourseBuyView(TemplateView):
template_name = 'payment/paymentwall_widget.html'
def get(self, request, pk=None, *args, **kwargs):
use_bonuses = request.GET.get('use_bonuses')
host = urlsplit(self.request.META.get('HTTP_REFERER'))
host = str(host[0]) + '://' + str(host[1])
course = Course.objects.get(id=pk)
roistat_visit = request.COOKIES.get('roistat_visit', None)
if request.user == course.author:
messages.error(request, 'Вы не можете приобрести свой курс.')
return redirect(reverse_lazy('course', args=[course.id]))
course_payment = CoursePayment.objects.create(
user=request.user,
course=course,
roistat_visit=roistat_visit,
)
if use_bonuses:
if request.user.bonus >= course_payment.amount:
bonus = UserBonus.objects.create(amount= -course_payment.amount, user=request.user, payment=course_payment)
course_payment.amount = 0
course_payment.status = Pingback.PINGBACK_TYPE_REGULAR
else:
bonus = UserBonus.objects.create(amount= -request.user.bonus, user=request.user,
payment=course_payment)
course_payment.amount -= request.user.bonus
course_payment.bonus = bonus
course_payment.save()
if course_payment.is_paid():
return redirect(reverse_lazy('course_payment_success', args=[course.id]))
product = Product(
f'course_{course_payment.id}',
course_payment.amount,
'RUB',
f'Курс "{course.title}"',
)
widget = Widget(
str(request.user.id),
'p1_1',
[product],
extra_params={
'lang': 'ru',
'evaluation': 1,
'demo': 1,
'test_mode': 1,
'success_url': host + str(reverse_lazy('course_payment_success', args=[course.id])),
'failure_url': host + str(reverse_lazy('payment-error')),
}
)
return self.render_to_response(context={'widget': widget.get_html_code()})
@method_decorator(login_required, name='dispatch')
class SchoolBuyView(TemplateView):
template_name = 'payment/paymentwall_widget.html'
def get(self, request, *args, **kwargs):
host = urlsplit(self.request.META.get('HTTP_REFERER'))
host = str(host[0]) + '://' + str(host[1])
weekdays = set(request.GET.getlist('weekdays', []))
use_bonuses = request.GET.get('use_bonuses')
roistat_visit = request.COOKIES.get('roistat_visit', None)
date_start = request.GET.get('date_start')
date_start = date_start and datetime.datetime.strptime(date_start, '%Y-%m-%d') or now().date()
if not weekdays:
messages.error(request, 'Выберите несколько дней недели.')
return redirect('school:school')
try:
weekdays = [int(weekday) for weekday in weekdays]
except ValueError:
messages.error(request, 'Ошибка выбора дней недели.')
return redirect('school:school')
prev_school_payment = SchoolPayment.objects.filter(
user=request.user,
date_start__lte=date_start,
date_end__gte=date_start,
add_days=False,
status__in=[
Pingback.PINGBACK_TYPE_REGULAR,
Pingback.PINGBACK_TYPE_GOODWILL,
Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED,
],
).last()
add_days = bool(prev_school_payment)
if add_days:
school_payment = SchoolPayment.objects.create(
user=request.user,
weekdays=weekdays,
date_start=date_start,
date_end=prev_school_payment.date_end,
add_days=True,
roistat_visit=roistat_visit,
)
# Если произойдет ошибка и оплату бонусами повторят еще раз на те же дни, то вернет ошибку
if school_payment.amount <= 0:
messages.error(request, 'Выбранные дни отсутствуют в оставшемся периоде подписки')
return redirect(reverse_lazy('school:school'))
else:
school_payment = SchoolPayment.objects.create(
user=request.user,
weekdays=weekdays,
roistat_visit=roistat_visit,
date_start=date_start,
date_end=Payment.add_months(date_start),
)
if use_bonuses:
if request.user.bonus >= school_payment.amount:
bonus = UserBonus.objects.create(amount= -school_payment.amount, user=request.user, payment=school_payment)
school_payment.amount = 0
school_payment.status = Pingback.PINGBACK_TYPE_REGULAR
else:
bonus = UserBonus.objects.create(amount= -request.user.bonus, user=request.user,
payment=school_payment)
school_payment.amount -= request.user.bonus
school_payment.bonus = bonus
school_payment.save()
if school_payment.is_paid():
return redirect(reverse_lazy('payment-success'))
product = Product(
f'school_{school_payment.id}',
school_payment.amount,
'RUB',
'Школа',
)
widget = Widget(
str(request.user.id),
'p1_1',
[product],
extra_params={
'lang': 'ru',
'evaluation': 1,
'demo': 1,
'test_mode': 1,
'success_url': host + str(reverse_lazy('payment-success')),
'failure_url': host + str(reverse_lazy('payment-error')),
}
)
return self.render_to_response(context={'widget': widget.get_html_code()})
@method_decorator(csrf_exempt, name='dispatch')
class PaymentwallCallbackView(View):
def get_request_ip(self):
x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = self.request.META.get('REMOTE_ADDR')
return ip
def get(self, request, *args, **kwargs):
payment_raw_data = request.GET.copy()
pingback = Pingback(payment_raw_data, self.get_request_ip())
if pingback.validate():
product_type_name, payment_id = pingback.get_product().get_id().split('_')
if product_type_name == 'course':
product_payment_class = CoursePayment
elif product_type_name == 'school':
product_payment_class = SchoolPayment
else:
return HttpResponse(status=403)
try:
payment = product_payment_class.objects.get(pk=payment_id)
except product_payment_class.DoesNotExist:
return HttpResponse(status=403)
logger.info(
json.dumps(payment_raw_data),
)
payment.status = pingback.get_type()
payment.data = payment_raw_data
if pingback.is_deliverable():
effective_amount = payment_raw_data.get('effective_price_amount')
if effective_amount:
payment.amount = Decimal(effective_amount)
transaction_to_mixpanel.delay(
payment.user.id,
payment.amount,
now().strftime('%Y-%m-%dT%H:%M:%S'),
product_type_name,
)
if product_type_name == 'course':
properties = {
'payment_id': payment.id,
'amount': payment.amount,
'status': payment.status,
'course': payment.course.id,
'created_at': payment.created_at,
'update_at': payment.update_at,
}
elif product_type_name == 'school':
properties = {
'payment_id': payment.id,
'amount': payment.amount,
'status': payment.status,
'weekdays': payment.weekdays,
'add_days': payment.add_days,
'date_start': payment.date_start,
'date_end': payment.date_end,
'created_at': payment.created_at,
'update_at': payment.update_at,
}
payment.save()
product_payment_to_mixpanel.delay(
payment.user.id,
f'{product_type_name.title()} payment',
now().strftime('%Y-%m-%dT%H:%M:%S'),
properties,
)
transaction_to_roistat.delay(
payment.user.id,
payment.id,
f'{product_type_name.title()} payment',
payment.amount,
now().strftime('%Y-%m-%d %H:%M:%S'),
pingback.get_type(),
product_type_name,
payment.roistat_visit,
)
author_balance = getattr(payment, 'author_balance', None)
if author_balance and author_balance.type == AuthorBalance.IN:
if pingback.is_deliverable():
payment.author_balance.status = AuthorBalance.ACCEPTED
elif pingback.is_under_review():
payment.author_balance.status = AuthorBalance.PENDING
else:
payment.author_balance.status = AuthorBalance.DECLINED
payment.author_balance.save()
return HttpResponse('OK')
else:
raise DisallowedPingbackHost
return HttpResponse(status=403)