Merge pull request #553 from pcoder/task/3799/dg_monthly_subscription
Task/3799/dg monthly subscription
This commit is contained in:
		
				commit
				
					
						9c513e667f
					
				
			
		
					 6 changed files with 183 additions and 39 deletions
				
			
		| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					# Generated by Django 1.9.4 on 2017-12-23 22:56
 | 
				
			||||||
 | 
					from __future__ import unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ('digitalglarus', '0024_bookingcancellation'),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name='membershiporder',
 | 
				
			||||||
 | 
					            name='stripe_subscription_id',
 | 
				
			||||||
 | 
					            field=models.CharField(max_length=100, null=True),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import calendar
 | 
					import calendar
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from datetime import datetime, date, timedelta
 | 
					from datetime import datetime, date, timedelta
 | 
				
			||||||
from dateutil.relativedelta import relativedelta
 | 
					from dateutil.relativedelta import relativedelta
 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
| 
						 | 
					@ -59,6 +59,17 @@ class MembershipType(models.Model):
 | 
				
			||||||
        return "{} - {}".format(datetime.strftime(start_date, "%b, %d %Y"),
 | 
					        return "{} - {}".format(datetime.strftime(start_date, "%b, %d %Y"),
 | 
				
			||||||
                                datetime.strftime(end_date, "%b, %d %Y"))
 | 
					                                datetime.strftime(end_date, "%b, %d %Y"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @cached_property
 | 
				
			||||||
 | 
					    def next_month_in_sec_since_epoch(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        First day of the next month expressed in seconds since the epoch time
 | 
				
			||||||
 | 
					        :return: Time in seconds
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        start_date, end_date = self.first_month_range
 | 
				
			||||||
 | 
					        first_day_next_month = end_date + timedelta(days=1)
 | 
				
			||||||
 | 
					        epoch_time = int(time.mktime(first_day_next_month.timetuple()))
 | 
				
			||||||
 | 
					        return epoch_time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Membership(models.Model):
 | 
					class Membership(models.Model):
 | 
				
			||||||
    type = models.ForeignKey(MembershipType)
 | 
					    type = models.ForeignKey(MembershipType)
 | 
				
			||||||
| 
						 | 
					@ -71,12 +82,12 @@ class Membership(models.Model):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def get_current_membership(cls, user):
 | 
					    def get_current_membership(cls, user):
 | 
				
			||||||
 | 
					        has_order_current_month = Q(
 | 
				
			||||||
        has_order_current_month = Q(membershiporder__customer__user=user,
 | 
					            membershiporder__customer__user=user,
 | 
				
			||||||
                                    membershiporder__created_at__month=datetime.today().month)
 | 
					            membershiporder__created_at__month=datetime.today().month
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        # import pdb;pdb.set_trace()
 | 
					        # import pdb;pdb.set_trace()
 | 
				
			||||||
        return cls.objects.\
 | 
					        return cls.objects.filter(has_order_current_month).last()
 | 
				
			||||||
            filter(has_order_current_month).last()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # def get_current_active_membership(cls, user):
 | 
					    # def get_current_active_membership(cls, user):
 | 
				
			||||||
    #     membership = cls.get_current_membership(user)
 | 
					    #     membership = cls.get_current_membership(user)
 | 
				
			||||||
| 
						 | 
					@ -84,8 +95,7 @@ class Membership(models.Model):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def get_by_user(cls, user):
 | 
					    def get_by_user(cls, user):
 | 
				
			||||||
        return cls.objects.\
 | 
					        return cls.objects.filter(membershiporder__customer__user=user).last()
 | 
				
			||||||
            filter(membershiporder__customer__user=user).last()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def create(cls, data):
 | 
					    def create(cls, data):
 | 
				
			||||||
| 
						 | 
					@ -96,18 +106,23 @@ class Membership(models.Model):
 | 
				
			||||||
    def activate_or_crete(cls, data, user):
 | 
					    def activate_or_crete(cls, data, user):
 | 
				
			||||||
        membership = cls.get_by_user(user)
 | 
					        membership = cls.get_by_user(user)
 | 
				
			||||||
        membership_id = membership.id if membership else None
 | 
					        membership_id = membership.id if membership else None
 | 
				
			||||||
        obj, created = cls.objects.update_or_create(id=membership_id, defaults=data)
 | 
					        obj, created = cls.objects.update_or_create(
 | 
				
			||||||
 | 
					            id=membership_id, defaults=data
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        return obj
 | 
					        return obj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def is_digitalglarus_active_member(cls, user):
 | 
					    def is_digitalglarus_active_member(cls, user):
 | 
				
			||||||
        # past_month = (datetime.today() - relativedelta(months=1)).month
 | 
					        # past_month = (datetime.today() - relativedelta(months=1)).month
 | 
				
			||||||
        has_order_current_month = Q(membershiporder__customer__user=user,
 | 
					        has_order_current_month = Q(
 | 
				
			||||||
                                    membershiporder__created_at__month=datetime.today().month)
 | 
					            membershiporder__customer__user=user,
 | 
				
			||||||
 | 
					            membershiporder__created_at__month=datetime.today().month
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        # has_order_past_month = Q(membershiporder__customer__user=user,
 | 
					        # has_order_past_month = Q(membershiporder__customer__user=user,
 | 
				
			||||||
        #  membershiporder__created_at__month=past_month)
 | 
					        #  membershiporder__created_at__month=past_month)
 | 
				
			||||||
        active_membership = Q(active=True)
 | 
					        active_membership = Q(active=True)
 | 
				
			||||||
        # return cls.objects.filter(has_order_past_month | has_order_current_month).\
 | 
					        # return cls.objects.filter(
 | 
				
			||||||
 | 
					        # has_order_past_month | has_order_current_month).\
 | 
				
			||||||
        return cls.objects.filter(has_order_current_month).\
 | 
					        return cls.objects.filter(has_order_current_month).\
 | 
				
			||||||
            filter(active_membership).exists()
 | 
					            filter(active_membership).exists()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -129,6 +144,7 @@ class MembershipOrder(Ordereable, models.Model):
 | 
				
			||||||
    membership = models.ForeignKey(Membership)
 | 
					    membership = models.ForeignKey(Membership)
 | 
				
			||||||
    start_date = models.DateField()
 | 
					    start_date = models.DateField()
 | 
				
			||||||
    end_date = models.DateField()
 | 
					    end_date = models.DateField()
 | 
				
			||||||
 | 
					    stripe_subscription_id = models.CharField(max_length=100, null=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def current_membership_dates(cls, user):
 | 
					    def current_membership_dates(cls, user):
 | 
				
			||||||
| 
						 | 
					@ -172,10 +188,12 @@ class MembershipOrder(Ordereable, models.Model):
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def create(cls, data):
 | 
					    def create(cls, data):
 | 
				
			||||||
        stripe_charge = data.pop('stripe_charge', None)
 | 
					        stripe_charge = data.pop('stripe_charge', None)
 | 
				
			||||||
 | 
					        stripe_subscription_id = data.pop('stripe_subscription_id', None)
 | 
				
			||||||
        instance = cls.objects.create(**data)
 | 
					        instance = cls.objects.create(**data)
 | 
				
			||||||
        instance.stripe_charge_id = stripe_charge.id
 | 
					        instance.stripe_charge_id = stripe_charge.id
 | 
				
			||||||
        instance.last4 = stripe_charge.source.last4
 | 
					        instance.last4 = stripe_charge.source.last4
 | 
				
			||||||
        instance.cc_brand = stripe_charge.source.brand
 | 
					        instance.cc_brand = stripe_charge.source.brand
 | 
				
			||||||
 | 
					        instance.stripe_subscription_id = stripe_subscription_id
 | 
				
			||||||
        instance.save()
 | 
					        instance.save()
 | 
				
			||||||
        return instance
 | 
					        return instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -95,12 +95,9 @@
 | 
				
			||||||
                  <a class="btn btn-primary btn-grey btn-deactivate print" href="{% url 'digitalglarus:membership_deactivate' %}">Deactivate</a>
 | 
					                  <a class="btn btn-primary btn-grey btn-deactivate print" href="{% url 'digitalglarus:membership_deactivate' %}">Deactivate</a>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
              {% elif not current_membership.active %}
 | 
					              {% elif not current_membership.active %}
 | 
				
			||||||
                <form method="POST" action="{% url 'digitalglarus:membership_reactivate' %}">
 | 
					 | 
				
			||||||
                  {% csrf_token %} 
 | 
					 | 
				
			||||||
                  <div class="edit-button">
 | 
					                  <div class="edit-button">
 | 
				
			||||||
                    <button type="submit" class="btn btn-primary btn-grey btn-deactivate print" href="{% url 'digitalglarus:membership_reactivate' %}">Reactivate</button>
 | 
					                    <a class="btn btn-primary btn-grey btn-deactivate" href="{% url 'digitalglarus:membership_pricing' %}">Reactivate</a>
 | 
				
			||||||
                  </div>
 | 
					                  </div>
 | 
				
			||||||
                </form>
 | 
					 | 
				
			||||||
              {% endif %}
 | 
					              {% endif %}
 | 
				
			||||||
            {% else %}
 | 
					            {% else %}
 | 
				
			||||||
              <div class="edit-button">
 | 
					              <div class="edit-button">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
from model_mommy import mommy
 | 
					from model_mommy import mommy
 | 
				
			||||||
from unittest import mock
 | 
					from unittest import mock, skipIf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.test import TestCase
 | 
					from django.test import TestCase
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
| 
						 | 
					@ -126,6 +126,11 @@ class MembershipPaymentViewTest(BaseTestCase):
 | 
				
			||||||
        self.assertEqual(response.context['membership_type'],
 | 
					        self.assertEqual(response.context['membership_type'],
 | 
				
			||||||
                         self.membership_type)
 | 
					                         self.membership_type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @skipIf(
 | 
				
			||||||
 | 
					        settings.STRIPE_API_PRIVATE_KEY_TEST is None or
 | 
				
			||||||
 | 
					        settings.STRIPE_API_PRIVATE_KEY_TEST is "",
 | 
				
			||||||
 | 
					        """Stripe details unavailable, so skipping CeleryTaskTestCase"""
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    @mock.patch('utils.stripe_utils.StripeUtils.create_customer')
 | 
					    @mock.patch('utils.stripe_utils.StripeUtils.create_customer')
 | 
				
			||||||
    def test_post(self, stripe_mocked_call):
 | 
					    def test_post(self, stripe_mocked_call):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,5 @@
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.shortcuts import render
 | 
					from django.shortcuts import render
 | 
				
			||||||
from django.http import HttpResponseRedirect
 | 
					from django.http import HttpResponseRedirect
 | 
				
			||||||
| 
						 | 
					@ -9,25 +11,34 @@ from django.utils.translation import get_language
 | 
				
			||||||
from djangocms_blog.models import Post
 | 
					from djangocms_blog.models import Post
 | 
				
			||||||
from django.contrib import messages
 | 
					from django.contrib import messages
 | 
				
			||||||
from django.views.generic import DetailView, ListView
 | 
					from django.views.generic import DetailView, ListView
 | 
				
			||||||
from .models import Supporter
 | 
					 | 
				
			||||||
from .mixins import ChangeMembershipStatusMixin
 | 
					 | 
				
			||||||
from utils.forms import ContactUsForm
 | 
					from utils.forms import ContactUsForm
 | 
				
			||||||
from utils.mailer import BaseEmail
 | 
					from utils.mailer import BaseEmail
 | 
				
			||||||
from django.views.generic.edit import FormView
 | 
					from django.views.generic.edit import FormView
 | 
				
			||||||
from membership.models import StripeCustomer
 | 
					from membership.models import StripeCustomer
 | 
				
			||||||
from utils.views import LoginViewMixin, SignupViewMixin, \
 | 
					from utils.views import (
 | 
				
			||||||
    PasswordResetViewMixin, PasswordResetConfirmViewMixin
 | 
					    LoginViewMixin, SignupViewMixin, PasswordResetViewMixin,
 | 
				
			||||||
from utils.forms import PasswordResetRequestForm, UserBillingAddressForm, EditCreditCardForm
 | 
					    PasswordResetConfirmViewMixin
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from utils.forms import (
 | 
				
			||||||
 | 
					    PasswordResetRequestForm, UserBillingAddressForm, EditCreditCardForm
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from utils.stripe_utils import StripeUtils
 | 
					from utils.stripe_utils import StripeUtils
 | 
				
			||||||
from utils.models import UserBillingAddress
 | 
					from utils.models import UserBillingAddress
 | 
				
			||||||
 | 
					from utils.tasks import send_plain_email_task
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .forms import LoginForm, SignupForm, MembershipBillingForm, BookingDateForm,\
 | 
					from .forms import (
 | 
				
			||||||
 | 
					    LoginForm, SignupForm, MembershipBillingForm, BookingDateForm,
 | 
				
			||||||
    BookingBillingForm, CancelBookingForm
 | 
					    BookingBillingForm, CancelBookingForm
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from .models import (
 | 
				
			||||||
 | 
					    MembershipType, Membership, MembershipOrder, Booking, BookingPrice,
 | 
				
			||||||
 | 
					    BookingOrder, BookingCancellation, Supporter
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from .mixins import (
 | 
				
			||||||
 | 
					    MembershipRequiredMixin, IsNotMemberMixin, ChangeMembershipStatusMixin
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .models import MembershipType, Membership, MembershipOrder, Booking, BookingPrice,\
 | 
					logger = logging.getLogger(__name__)
 | 
				
			||||||
    BookingOrder, BookingCancellation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from .mixins import MembershipRequiredMixin, IsNotMemberMixin
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class IndexView(TemplateView):
 | 
					class IndexView(TemplateView):
 | 
				
			||||||
| 
						 | 
					@ -271,7 +282,6 @@ class BookingPaymentView(LoginRequiredMixin, MembershipRequiredMixin, FormView):
 | 
				
			||||||
        booking_data = {
 | 
					        booking_data = {
 | 
				
			||||||
            'start_date': start_date,
 | 
					            'start_date': start_date,
 | 
				
			||||||
            'end_date': end_date,
 | 
					            'end_date': end_date,
 | 
				
			||||||
            'start_date': start_date,
 | 
					 | 
				
			||||||
            'free_days': free_days,
 | 
					            'free_days': free_days,
 | 
				
			||||||
            'price': normal_price,
 | 
					            'price': normal_price,
 | 
				
			||||||
            'final_price': final_price,
 | 
					            'final_price': final_price,
 | 
				
			||||||
| 
						 | 
					@ -355,16 +365,21 @@ class MembershipPaymentView(LoginRequiredMixin, IsNotMemberMixin, FormView):
 | 
				
			||||||
            membership_type = data.get('membership_type')
 | 
					            membership_type = data.get('membership_type')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Get or create stripe customer
 | 
					            # Get or create stripe customer
 | 
				
			||||||
            customer = StripeCustomer.get_or_create(email=self.request.user.email,
 | 
					            customer = StripeCustomer.get_or_create(
 | 
				
			||||||
                                                    token=token)
 | 
					                email=self.request.user.email, token=token
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            if not customer:
 | 
					            if not customer:
 | 
				
			||||||
                form.add_error("__all__", "Invalid credit card")
 | 
					                form.add_error("__all__", "Invalid credit card")
 | 
				
			||||||
                return self.render_to_response(self.get_context_data(form=form))
 | 
					                return self.render_to_response(
 | 
				
			||||||
 | 
					                    self.get_context_data(form=form)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Make stripe charge to a customer
 | 
					            # Make stripe charge to a customer
 | 
				
			||||||
            stripe_utils = StripeUtils()
 | 
					            stripe_utils = StripeUtils()
 | 
				
			||||||
            charge_response = stripe_utils.make_charge(amount=membership_type.first_month_price,
 | 
					            charge_response = stripe_utils.make_charge(
 | 
				
			||||||
                                                       customer=customer.stripe_id)
 | 
					                amount=membership_type.first_month_price,
 | 
				
			||||||
 | 
					                customer=customer.stripe_id
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            charge = charge_response.get('response_object')
 | 
					            charge = charge_response.get('response_object')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Check if the payment was approved
 | 
					            # Check if the payment was approved
 | 
				
			||||||
| 
						 | 
					@ -373,6 +388,58 @@ class MembershipPaymentView(LoginRequiredMixin, IsNotMemberMixin, FormView):
 | 
				
			||||||
                    'paymentError': charge_response.get('error'),
 | 
					                    'paymentError': charge_response.get('error'),
 | 
				
			||||||
                    'form': form
 | 
					                    'form': form
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
 | 
					                email_to_admin_data = {
 | 
				
			||||||
 | 
					                    'subject': "Could not create charge for Digital Glarus "
 | 
				
			||||||
 | 
					                               "user: {user}".format(
 | 
				
			||||||
 | 
					                                    user=self.request.user.email
 | 
				
			||||||
 | 
					                                ),
 | 
				
			||||||
 | 
					                    'from_email': 'info@digitalglarus.ch',
 | 
				
			||||||
 | 
					                    'to': ['info@ungleich.ch'],
 | 
				
			||||||
 | 
					                    'body': "\n".join(
 | 
				
			||||||
 | 
					                        ["%s=%s" % (k, v) for (k, v) in
 | 
				
			||||||
 | 
					                         charge_response.items()]),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                send_plain_email_task.delay(email_to_admin_data)
 | 
				
			||||||
 | 
					                return render(request, self.template_name, context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Subscribe the customer to dg plan from the next month onwards
 | 
				
			||||||
 | 
					            stripe_plan = stripe_utils.get_or_create_stripe_plan(
 | 
				
			||||||
 | 
					                amount=membership_type.price,
 | 
				
			||||||
 | 
					                name='Digital Glarus {sub_type_name} Subscription'.format(
 | 
				
			||||||
 | 
					                    sub_type_name=membership_type.name
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                stripe_plan_id='dg-{sub_type_name}'.format(
 | 
				
			||||||
 | 
					                    sub_type_name=membership_type.name
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            subscription_result = stripe_utils.subscribe_customer_to_plan(
 | 
				
			||||||
 | 
					                customer.stripe_id,
 | 
				
			||||||
 | 
					                [{"plan": stripe_plan.get('response_object').stripe_plan_id}],
 | 
				
			||||||
 | 
					                trial_end=membership_type.next_month_in_sec_since_epoch
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            stripe_subscription_obj = subscription_result.get(
 | 
				
			||||||
 | 
					                'response_object'
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            # Check if call to create subscription was ok
 | 
				
			||||||
 | 
					            if (stripe_subscription_obj is None or
 | 
				
			||||||
 | 
					                (stripe_subscription_obj.status != 'active' and
 | 
				
			||||||
 | 
					                 stripe_subscription_obj.status != 'trialing')):
 | 
				
			||||||
 | 
					                context.update({
 | 
				
			||||||
 | 
					                    'paymentError': subscription_result.get('error'),
 | 
				
			||||||
 | 
					                    'form': form
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                email_to_admin_data = {
 | 
				
			||||||
 | 
					                    'subject': "Could not create Stripe subscription for "
 | 
				
			||||||
 | 
					                               "Digital Glarus user: {user}".format(
 | 
				
			||||||
 | 
					                                    user=self.request.user.email
 | 
				
			||||||
 | 
					                                ),
 | 
				
			||||||
 | 
					                    'from_email': 'info@digitalglarus.ch',
 | 
				
			||||||
 | 
					                    'to': ['info@ungleich.ch'],
 | 
				
			||||||
 | 
					                    'body': "\n".join(
 | 
				
			||||||
 | 
					                        ["%s=%s" % (k, v) for (k, v) in
 | 
				
			||||||
 | 
					                         subscription_result.items()]),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                send_plain_email_task.delay(email_to_admin_data)
 | 
				
			||||||
                return render(request, self.template_name, context)
 | 
					                return render(request, self.template_name, context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            charge = charge_response.get('response_object')
 | 
					            charge = charge_response.get('response_object')
 | 
				
			||||||
| 
						 | 
					@ -412,6 +479,7 @@ class MembershipPaymentView(LoginRequiredMixin, IsNotMemberMixin, FormView):
 | 
				
			||||||
                'customer': customer,
 | 
					                'customer': customer,
 | 
				
			||||||
                'billing_address': billing_address,
 | 
					                'billing_address': billing_address,
 | 
				
			||||||
                'stripe_charge': charge,
 | 
					                'stripe_charge': charge,
 | 
				
			||||||
 | 
					                'stripe_subscription_id': stripe_subscription_obj.id,
 | 
				
			||||||
                'amount': membership_type.first_month_price,
 | 
					                'amount': membership_type.first_month_price,
 | 
				
			||||||
                'start_date': membership_start_date,
 | 
					                'start_date': membership_start_date,
 | 
				
			||||||
                'end_date': membership_end_date
 | 
					                'end_date': membership_end_date
 | 
				
			||||||
| 
						 | 
					@ -481,8 +549,43 @@ class MembershipDeactivateView(LoginRequiredMixin, UpdateView):
 | 
				
			||||||
    def post(self, *args, **kwargs):
 | 
					    def post(self, *args, **kwargs):
 | 
				
			||||||
        membership = self.get_object()
 | 
					        membership = self.get_object()
 | 
				
			||||||
        membership.deactivate()
 | 
					        membership.deactivate()
 | 
				
			||||||
 | 
					        messages.add_message(
 | 
				
			||||||
        messages.add_message(self.request, messages.SUCCESS, self.success_message)
 | 
					            self.request, messages.SUCCESS, self.success_message
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        # cancel Stripe subscription
 | 
				
			||||||
 | 
					        stripe_utils = StripeUtils()
 | 
				
			||||||
 | 
					        membership_order = MembershipOrder.objects.filter(
 | 
				
			||||||
 | 
					            customer__user=self.request.user
 | 
				
			||||||
 | 
					        ).last()
 | 
				
			||||||
 | 
					        if membership_order:
 | 
				
			||||||
 | 
					            if membership_order.stripe_subscription_id:
 | 
				
			||||||
 | 
					                result = stripe_utils.unsubscribe_customer(
 | 
				
			||||||
 | 
					                    subscription_id=membership_order.stripe_subscription_id
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                stripe_subscription_obj = result.get('response_object')
 | 
				
			||||||
 | 
					                # Check if the subscription was canceled
 | 
				
			||||||
 | 
					                if (stripe_subscription_obj is None or
 | 
				
			||||||
 | 
					                        stripe_subscription_obj.status != 'canceled'):
 | 
				
			||||||
 | 
					                    error_msg = result.get('error')
 | 
				
			||||||
 | 
					                    logger.error(
 | 
				
			||||||
 | 
					                        "Could not cancel Digital Glarus subscription. "
 | 
				
			||||||
 | 
					                        "Reason: {reason}".format(
 | 
				
			||||||
 | 
					                            reason=error_msg
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                logger.error(
 | 
				
			||||||
 | 
					                    "User {user} may have Stripe subscriptions created "
 | 
				
			||||||
 | 
					                    "manually. Please check.".format(
 | 
				
			||||||
 | 
					                        user=self.request.user.name
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            logger.error(
 | 
				
			||||||
 | 
					                "MembershipOrder for {user} not found".format(
 | 
				
			||||||
 | 
					                            user=self.request.user.name
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return HttpResponseRedirect(self.success_url)
 | 
					        return HttpResponseRedirect(self.success_url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -210,12 +210,14 @@ class StripeUtils(object):
 | 
				
			||||||
        return return_value
 | 
					        return return_value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @handleStripeError
 | 
					    @handleStripeError
 | 
				
			||||||
    def subscribe_customer_to_plan(self, customer, plans):
 | 
					    def subscribe_customer_to_plan(self, customer, plans, trial_end=None):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Subscribes the given customer to the list of given plans
 | 
					        Subscribes the given customer to the list of given plans
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        :param customer: The stripe customer identifier
 | 
					        :param customer: The stripe customer identifier
 | 
				
			||||||
        :param plans: A list of stripe plans.
 | 
					        :param plans: A list of stripe plans.
 | 
				
			||||||
 | 
					        :param trial_end: An integer representing when the Stripe subscription
 | 
				
			||||||
 | 
					               is supposed to end
 | 
				
			||||||
        Ref: https://stripe.com/docs/api/python#create_subscription-items
 | 
					        Ref: https://stripe.com/docs/api/python#create_subscription-items
 | 
				
			||||||
              e.g.
 | 
					              e.g.
 | 
				
			||||||
                    plans = [
 | 
					                    plans = [
 | 
				
			||||||
| 
						 | 
					@ -227,8 +229,7 @@ class StripeUtils(object):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        subscription_result = self.stripe.Subscription.create(
 | 
					        subscription_result = self.stripe.Subscription.create(
 | 
				
			||||||
            customer=customer,
 | 
					            customer=customer, items=plans, trial_end=trial_end
 | 
				
			||||||
            items=plans,
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        return subscription_result
 | 
					        return subscription_result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue