from decimal import Decimal import short_url import json import logging from datetime import timedelta from urllib.parse import urlsplit import datetime from django.contrib import messages from django.contrib.auth.decorators import login_required from django.http import HttpResponse, Http404 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 paymentwall import Pingback, Product, Widget from apps.content.models import Package from apps.course.models import Course from apps.payment.tasks import transaction_to_mixpanel, product_payment_to_mixpanel, transaction_to_roistat from apps.notification.utils import send_email from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment, UserBonus, GiftCertificate, \ GiftCertificatePayment, UserGiftCertificate, DrawingCampPayment 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, is_camp=False, *args, **kwargs): context = { 'duration': request.GET.get('duration'), 'camp': is_camp, 'school': not is_camp } return self.render_to_response(context=context) @method_decorator(login_required, name='dispatch') class CourseBuyView(TemplateView): template_name = 'payment/pay.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])) prev_payment = CoursePayment.objects.filter(user=request.user, course=course, status__in=Payment.PW_PAID_STATUSES).order_by('-access_expire').first() access_duration = course.access_duration or 90 access_expire = prev_payment.access_expire + timedelta(days=access_duration) if prev_payment \ else now().date() + timedelta(days=access_duration - 1) course_payment = CoursePayment.objects.create( user=request.user, course=course, access_expire=access_expire, roistat_visit=roistat_visit, ) if use_bonuses and request.user.bonus: 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={ 'email': request.user.email, '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/pay.html' def get(self, request, *args, **kwargs): host = urlsplit(self.request.META.get('HTTP_REFERER')) host = str(host[0]) + '://' + str(host[1]) use_bonuses = request.GET.get('use_bonuses') roistat_visit = request.COOKIES.get('roistat_visit', None) date_start = request.GET.get('date_start') duration = request.GET.get('duration') payment_id = request.GET.get('payment_id') package = get_object_or_404(Package, duration=duration) date_start = date_start and datetime.datetime.strptime(date_start, '%Y-%m-%d').date() or now().date() if payment_id: school_payment = get_object_or_404(SchoolPayment, id=payment_id) else: amount_data = SchoolPayment.calc_amount(package=package, user=request.user, date_start=date_start) school_payment = SchoolPayment.objects.create( user=request.user, weekdays=amount_data.get('weekdays'), roistat_visit=roistat_visit, date_start=amount_data.get('date_start'), date_end=amount_data.get('date_end'), package=package, ) if use_bonuses and request.user.bonus: 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: school_payment.amount -= request.user.bonus bonus = UserBonus.objects.create(amount= -request.user.bonus, user=request.user, payment=school_payment) school_payment.bonus = bonus school_payment.save() if school_payment.is_paid(): return redirect('%s?duration=%s' % (str(reverse_lazy('payment-success')), duration)) if payment_id and school_payment.bonus and not use_bonuses: bonus = school_payment.bonus school_payment.amount += school_payment.bonus school_payment.bonus = None bonus.delete() product = Product( f'school_{school_payment.id}', school_payment.amount, 'RUB', 'Подписка', ) widget = Widget( str(request.user.id), 'p1_1', [product], extra_params={ 'email': request.user.email, 'lang': 'ru', 'evaluation': 1, 'demo': 1, 'test_mode': 1, 'success_url': host + str(reverse_lazy('payment-success')) + '?duration=%s' % duration, 'failure_url': host + str(reverse_lazy('payment-error')), } ) if self.request.is_ajax(): self.template_name = 'payment/paymentwall_widget.html' return self.render_to_response(context={ 'widget': widget.get_html_code({'height': '400'}), 'school': True, 'payment': school_payment, }) @method_decorator(login_required, name='dispatch') class DrawingCampBuyView(TemplateView): template_name = 'payment/pay.html' def get(self, request, *args, **kwargs): host = urlsplit(self.request.META.get('HTTP_REFERER')) host = str(host[0]) + '://' + str(host[1]) 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').date() or now().date() date_start, date_end = Payment.get_date_range(date_start, months=1, is_camp=True) prev_payment = DrawingCampPayment.objects.filter( user=request.user, date_start__lte=date_start, date_end__gte=date_start, status__in=[ Pingback.PINGBACK_TYPE_REGULAR, Pingback.PINGBACK_TYPE_GOODWILL, Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, ], ).exists() if prev_payment: return HttpResponse(status=403) camp_payment = DrawingCampPayment.objects.create( user=request.user, roistat_visit=roistat_visit, date_start=date_start, date_end=date_end, ) if use_bonuses and request.user.bonus: if request.user.bonus >= camp_payment.amount: bonus = UserBonus.objects.create(amount=-camp_payment.amount, user=request.user, payment=camp_payment) camp_payment.amount = 0 camp_payment.status = Pingback.PINGBACK_TYPE_REGULAR else: bonus = UserBonus.objects.create(amount=-request.user.bonus, user=request.user, payment=camp_payment) camp_payment.amount -= request.user.bonus camp_payment.bonus = bonus camp_payment.save() if camp_payment.is_paid(): return redirect(reverse_lazy('camp-payment-success')) product = Product( f'drawing_camp_{camp_payment.id}', camp_payment.amount, 'RUB', 'Школа', ) widget = Widget( str(request.user.id), 'p1_1', [product], extra_params={ 'email': request.user.email, 'lang': 'ru', 'evaluation': 1, 'demo': 1, 'test_mode': 1, 'success_url': host + str(reverse_lazy('camp-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(): splitted = pingback.get_product().get_id().split('_') product_type_name, payment_id = '_'.join(splitted[:-1]), splitted[-1] if product_type_name == 'course': product_payment_class = CoursePayment elif product_type_name == 'school': product_payment_class = SchoolPayment elif product_type_name == 'drawing_camp': product_payment_class = DrawingCampPayment elif product_type_name == 'gift_certificate': product_payment_class = GiftCertificatePayment 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, } elif product_type_name == 'drawing_camp': properties = { 'payment_id': payment.id, 'amount': payment.amount, 'status': payment.status, 'date_start': payment.date_start, 'date_end': payment.date_end, 'created_at': payment.created_at, 'update_at': payment.update_at, } elif product_type_name == 'gift_certificate': properties = { 'payment_id': payment.id, 'amount': payment.amount, 'status': payment.status, 'gift_certificate': payment.gift_certificate.id, '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, datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'), pingback.get_type(), product_type_name, payment.roistat_visit, ) if product_type_name == 'course': send_email.delay('Спасибо за покупку!', payment.user.email, 'notification/email/buy_email.html', product_type=product_type_name, url=payment.course.url) elif product_type_name != 'school': send_email.delay('Спасибо за покупку!', payment.user.email, 'notification/email/buy_email.html', product_type=product_type_name) elif product_type_name != 'drawing_camp': send_email.delay('Спасибо за покупку!', payment.user.email, 'notification/email/buy_email.html', product_type=product_type_name, date_start=payment.date_start, date_end=payment.date_end) 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) class GiftCertificatesView(TemplateView): model = GiftCertificate template_name = 'payment/gift_certificates.html' def get(self, request, *args, **kwargs): gift_certificates = GiftCertificate.objects.all() context = self.get_context_data(**kwargs) context['gift_certificates'] = gift_certificates return self.render_to_response(context) @method_decorator(login_required, name='dispatch') class GiftCertificateBuyView(TemplateView): model = GiftCertificate template_name = 'payment/pay.html' def get(self, request, pk, *args, **kwargs): gift_certificate = get_object_or_404(GiftCertificate, pk=pk) roistat_visit = request.COOKIES.get('roistat_visit', None) gift_certificate_payment = GiftCertificatePayment.objects.create( user=request.user, gift_certificate=gift_certificate, roistat_visit=roistat_visit,) context = self.get_context_data(**kwargs) product = Product( f'gift_certificate_{gift_certificate_payment.id}', gift_certificate_payment.amount, 'RUB', 'Подарочный сертификат', ) host = urlsplit(self.request.META.get('HTTP_REFERER')) host = str(host[0]) + '://' + str(host[1]) 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('gift-certificate-payment-success', args=[gift_certificate_payment.id])), 'failure_url': host + str(reverse_lazy('payment-error')), } ) context['widget'] = widget.get_html_code() return self.render_to_response(context) @method_decorator(login_required, name='dispatch') class GiftCertificateBuySuccessView(TemplateView): template_name = 'payment/gift_certificate_payment_success.html' def get(self, request, payment_id=None, *args, **kwargs): try: GiftCertificatePayment.objects.get(id=payment_id) except: raise Http404() return self.render_to_response(context={'gift_certificate': True}) @method_decorator(login_required, name='dispatch') class GiftCertificateGetView(TemplateView): template_name = 'payment/gift_certificate_get.html' def get(self, request, slug, *args, **kwargs): try: ugs = get_object_or_404(UserGiftCertificate, pk=short_url.decode_url(slug)) except: raise Http404() if request.user.bonuses.filter(payment=ugs.payment, referral__isnull=True).exists(): raise Http404() bonuses = UserBonus.objects.create(user=request.user, amount=ugs.gift_certificate.price, payment=ugs.payment) ugs.bonuses_sent = bonuses ugs.save() context = self.get_context_data(**kwargs) return self.render_to_response(context)