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.
532 lines
22 KiB
532 lines
22 KiB
from decimal import Decimal
|
|
import short_url
|
|
import json
|
|
import logging
|
|
|
|
from datetime import timedelta
|
|
from urllib.parse import urlsplit, urlencode
|
|
|
|
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/payment_success.html'
|
|
|
|
def get(self, request, payment_id, *args, **kwargs):
|
|
payment = CoursePayment.objects.get(pk=payment_id)
|
|
course = payment.course
|
|
return self.render_to_response(context={'course': course, 'payment': payment})
|
|
|
|
|
|
@method_decorator(login_required, name='dispatch')
|
|
class SchoolBuySuccessView(TemplateView):
|
|
template_name = 'payment/payment_success.html'
|
|
|
|
def get(self, request, payment_id, is_camp=False, *args, **kwargs):
|
|
if is_camp:
|
|
payment = DrawingCampPayment.objects.get(pk=payment_id)
|
|
else:
|
|
payment = SchoolPayment.objects.get(pk=payment_id)
|
|
context = {
|
|
'duration': request.GET.get('duration'),
|
|
'camp': is_camp,
|
|
'school': not is_camp,
|
|
'payment': payment,
|
|
}
|
|
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_payment.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_payment.id])),
|
|
'failure_url': host + str(reverse_lazy('payment-error')),
|
|
}
|
|
)
|
|
attrs = {}
|
|
if request.user_agent.is_mobile:
|
|
attrs['width'] = '100%'
|
|
attrs['height'] = '600'
|
|
return self.render_to_response(context={
|
|
'widget': widget.get_html_code(attrs),
|
|
'payment': course_payment,
|
|
'course': course,
|
|
})
|
|
|
|
|
|
@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()
|
|
prev_payment = SchoolPayment.objects.paid().filter(
|
|
user=self.request.user,
|
|
date_end__gte=now().date(),).last()
|
|
if prev_payment and prev_payment.date_end > date_start:
|
|
date_start = prev_payment.date_end + timedelta(1)
|
|
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', args=[school_payment.id])), 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', args=[school_payment.id])) + '?duration=%s' % duration,
|
|
'failure_url': host + str(reverse_lazy('payment-error')),
|
|
}
|
|
)
|
|
if self.request.is_ajax():
|
|
self.template_name = 'payment/paymentwall_widget.html'
|
|
attrs = {'height': '400'}
|
|
if request.user_agent.is_mobile:
|
|
attrs['width'] = '100%'
|
|
attrs['height'] = '600'
|
|
return self.render_to_response(context={
|
|
'widget': widget.get_html_code(attrs),
|
|
'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', args=[camp_payment.id]))
|
|
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', args=[camp_payment.id])),
|
|
'failure_url': host + str(reverse_lazy('payment-error')),
|
|
}
|
|
)
|
|
attrs = {}
|
|
if request.user_agent.is_mobile:
|
|
attrs['width'] = '100%'
|
|
attrs['height'] = '600'
|
|
return self.render_to_response(context={
|
|
'widget': widget.get_html_code(attrs),
|
|
'payment': camp_payment,
|
|
'camp': True,
|
|
})
|
|
|
|
|
|
@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)
|
|
use_bonuses = request.GET.get('use_bonuses')
|
|
gift_certificate_payment = GiftCertificatePayment.objects.create(
|
|
user=request.user,
|
|
gift_certificate=gift_certificate,
|
|
roistat_visit=roistat_visit,)
|
|
if use_bonuses and request.user.bonus:
|
|
if request.user.bonus >= gift_certificate_payment.amount:
|
|
bonus = UserBonus.objects.create(amount=-gift_certificate_payment.amount, user=request.user,
|
|
payment=gift_certificate_payment)
|
|
gift_certificate_payment.amount = 0
|
|
gift_certificate_payment.status = Pingback.PINGBACK_TYPE_REGULAR
|
|
else:
|
|
bonus = UserBonus.objects.create(amount=-request.user.bonus, user=request.user,
|
|
payment=gift_certificate_payment)
|
|
gift_certificate_payment.amount -= request.user.bonus
|
|
gift_certificate_payment.bonus = bonus
|
|
gift_certificate_payment.save()
|
|
if gift_certificate_payment.is_paid():
|
|
return redirect(reverse_lazy('gift-certificate-payment-success', args=[gift_certificate_payment.id]))
|
|
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')),
|
|
}
|
|
)
|
|
attrs = {}
|
|
if request.user_agent.is_mobile:
|
|
attrs['width'] = '100%'
|
|
attrs['height'] = '600'
|
|
context['widget'] = widget.get_html_code(attrs)
|
|
context['payment'] = gift_certificate_payment
|
|
context['gift_certificate'] = gift_certificate
|
|
return self.render_to_response(context)
|
|
|
|
|
|
@method_decorator(login_required, name='dispatch')
|
|
class GiftCertificateBuySuccessView(TemplateView):
|
|
template_name = 'payment/payment_success.html'
|
|
|
|
def get(self, request, payment_id, *args, **kwargs):
|
|
try:
|
|
payment = GiftCertificatePayment.objects.get(id=payment_id)
|
|
except:
|
|
raise Http404()
|
|
return self.render_to_response(context={
|
|
'gift_certificate': payment.gift_certificate,
|
|
'payment': payment,
|
|
})
|
|
|
|
|
|
@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)
|
|
|
|
|