Handle IncompleteSubscriptions in webhook
This commit is contained in:
		
					parent
					
						
							
								c4c918d591
							
						
					
				
			
			
				commit
				
					
						17c8f9ca18
					
				
			
		
					 3 changed files with 130 additions and 60 deletions
				
			
		|  | @ -1,6 +1,7 @@ | ||||||
| import logging | import logging | ||||||
| 
 | import json | ||||||
| import stripe | import stripe | ||||||
|  | 
 | ||||||
| from django import forms | from django import forms | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.contrib import messages | from django.contrib import messages | ||||||
|  | @ -19,9 +20,8 @@ from hosting.forms import ( | ||||||
| ) | ) | ||||||
| from hosting.models import ( | from hosting.models import ( | ||||||
|     HostingBill, HostingOrder, UserCardDetail, GenericProduct, UserHostingKey, |     HostingBill, HostingOrder, UserCardDetail, GenericProduct, UserHostingKey, | ||||||
|     StripeTaxRate) |     StripeTaxRate, IncompleteSubscriptions) | ||||||
| from membership.models import CustomUser, StripeCustomer | from membership.models import CustomUser, StripeCustomer | ||||||
| from opennebula_api.models import OpenNebulaManager |  | ||||||
| from opennebula_api.serializers import VMTemplateSerializer | from opennebula_api.serializers import VMTemplateSerializer | ||||||
| from utils.forms import ( | from utils.forms import ( | ||||||
|     BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm, |     BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm, | ||||||
|  | @ -894,6 +894,53 @@ class OrderConfirmationView(DetailView, FormView): | ||||||
|             latest_invoice = stripe.Invoice.retrieve( |             latest_invoice = stripe.Invoice.retrieve( | ||||||
|                 stripe_subscription_obj.latest_invoice) |                 stripe_subscription_obj.latest_invoice) | ||||||
| 
 | 
 | ||||||
|  |             new_user_hosting_key_id = None | ||||||
|  |             card_id = None | ||||||
|  |             generic_payment_type = None | ||||||
|  |             generic_payment_details = None | ||||||
|  |             if 'generic_payment_details' in request.session: | ||||||
|  |                 generic_payment_details = request.session[ | ||||||
|  |                     'generic_payment_details'] | ||||||
|  |             if 'generic_payment_type' in request.session: | ||||||
|  |                 generic_payment_type = request.session['generic_payment_type'] | ||||||
|  |             if 'new_user_hosting_key_id' in self.request.session: | ||||||
|  |                 new_user_hosting_key_id = request.session[ | ||||||
|  |                     'new_user_hosting_key_id'] | ||||||
|  |             if 'card_id' in request.session: | ||||||
|  |                 card_id = request.session.get('card_id') | ||||||
|  |             req = { | ||||||
|  |                 'scheme': self.request.scheme, | ||||||
|  |                 'host': self.request.get_host(), | ||||||
|  |                 'language': get_language(), | ||||||
|  |                 'new_user_hosting_key_id': new_user_hosting_key_id, | ||||||
|  |                 'card_id': card_id, | ||||||
|  |                 'generic_payment_type': generic_payment_type, | ||||||
|  |                 'generic_payment_details': generic_payment_details | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             subscription_status = '' | ||||||
|  |             if stripe_subscription_obj: | ||||||
|  |                 subscription_status = stripe_subscription_obj.status | ||||||
|  | 
 | ||||||
|  |             # Store params so that they can be retrieved later | ||||||
|  |             IncompleteSubscriptions.objects.create( | ||||||
|  |                 subscription_status=subscription_status, | ||||||
|  |                 name=user.get('name'), | ||||||
|  |                 email=user.get('email'), | ||||||
|  |                 request=json.dumps(req), | ||||||
|  |                 stripe_api_cus_id=stripe_api_cus_id, | ||||||
|  |                 card_details_response=json.dumps(card_details_response), | ||||||
|  |                 stripe_subscription_obj=json.dumps(stripe_subscription_obj) if stripe_customer_obj else '', | ||||||
|  |                 stripe_onetime_charge=json.dumps(stripe_onetime_charge) if stripe_onetime_charge else '', | ||||||
|  |                 gp_details=json.dumps(gp_details) if gp_details else '', | ||||||
|  |                 specs=json.dumps(specs) if specs else '', | ||||||
|  |                 vm_template_id=vm_template_id if vm_template_id else 0, | ||||||
|  |                 template=json.dumps(template) if template else '', | ||||||
|  |                 billing_address_data=json.dumps( | ||||||
|  |                     request.session.get('billing_address_data') | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|             # Check if the subscription was approved and is active |             # Check if the subscription was approved and is active | ||||||
|             if (stripe_subscription_obj is None |             if (stripe_subscription_obj is None | ||||||
|                     or (stripe_subscription_obj.status != 'active' |                     or (stripe_subscription_obj.status != 'active' | ||||||
|  | @ -953,6 +1000,7 @@ class OrderConfirmationView(DetailView, FormView): | ||||||
|                             ) |                             ) | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|  |                     clear_all_session_vars(request) | ||||||
|                     return JsonResponse(context) |                     return JsonResponse(context) | ||||||
|                 else: |                 else: | ||||||
|                     logger.debug( |                     logger.debug( | ||||||
|  | @ -962,28 +1010,6 @@ class OrderConfirmationView(DetailView, FormView): | ||||||
|                         "requires_source_action") |                         "requires_source_action") | ||||||
|                     msg = subscription_result.get('error') |                     msg = subscription_result.get('error') | ||||||
|                     return show_error(msg, self.request) |                     return show_error(msg, self.request) | ||||||
|         new_user_hosting_key_id = None |  | ||||||
|         card_id = None |  | ||||||
|         generic_payment_type = None |  | ||||||
|         generic_payment_details = None |  | ||||||
|         if 'generic_payment_details' in request.session: |  | ||||||
|             generic_payment_details = request.session['generic_payment_details'] |  | ||||||
|         if 'generic_payment_type' in request.session: |  | ||||||
|             generic_payment_type = request.session['generic_payment_type'] |  | ||||||
|         if 'new_user_hosting_key_id' in self.request.session: |  | ||||||
|             new_user_hosting_key_id = request.session['new_user_hosting_key_id'] |  | ||||||
|         if 'card_id' in request.session: |  | ||||||
|             card_id = request.session.get('card_id') |  | ||||||
|         req = { |  | ||||||
|             'scheme': self.request.scheme, |  | ||||||
|             'host': self.request.get_host(), |  | ||||||
|             'language': get_language(), |  | ||||||
|             'new_user_hosting_key_id': new_user_hosting_key_id, |  | ||||||
|             'card_id': card_id, |  | ||||||
|             'generic_payment_type': generic_payment_type, |  | ||||||
|             'generic_payment_details': generic_payment_details |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         do_create_vm(req, user, stripe_api_cus_id, |         do_create_vm(req, user, stripe_api_cus_id, | ||||||
|             card_details_response, stripe_subscription_obj, |             card_details_response, stripe_subscription_obj, | ||||||
|             stripe_onetime_charge, gp_details, specs, vm_template_id, |             stripe_onetime_charge, gp_details, specs, vm_template_id, | ||||||
|  | @ -1191,14 +1217,14 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response, | ||||||
|                       "We have just received a payment of CHF {amount:.2f}" |                       "We have just received a payment of CHF {amount:.2f}" | ||||||
|                       " from you.{recurring}\n\n" |                       " from you.{recurring}\n\n" | ||||||
|                       "Cheers,\nYour Data Center Light team".format( |                       "Cheers,\nYour Data Center Light team".format( | ||||||
|                 name=user.get('name'), |                             name=user.get('name'), | ||||||
|                 amount=gp_details['amount'], |                             amount=gp_details['amount'], | ||||||
|                 recurring=( |                             recurring=( | ||||||
|                     recurring_text |                                 recurring_text | ||||||
|                     if gp_details['recurring'] else '' |                                 if gp_details['recurring'] else '' | ||||||
|                 ) |                             ) | ||||||
|             ) |                         ) | ||||||
|             ), |                     ), | ||||||
|             'reply_to': ['info@ungleich.ch'], |             'reply_to': ['info@ungleich.ch'], | ||||||
|         } |         } | ||||||
|         send_plain_email_task.delay(email_data) |         send_plain_email_task.delay(email_data) | ||||||
|  |  | ||||||
|  | @ -741,3 +741,22 @@ class StripeTaxRate(AssignPermissionsMixin, models.Model): | ||||||
|     display_name = models.CharField(max_length=100) |     display_name = models.CharField(max_length=100) | ||||||
|     percentage = models.FloatField(default=0) |     percentage = models.FloatField(default=0) | ||||||
|     description = models.CharField(max_length=100) |     description = models.CharField(max_length=100) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class IncompleteSubscriptions(AssignPermissionsMixin, models.Model): | ||||||
|  |     created_at = models.DateTimeField(auto_now_add=True) | ||||||
|  |     completed_at = models.DateTimeField() | ||||||
|  |     subscription_id = models.CharField(max_length=100) | ||||||
|  |     subscription_status = models.CharField(max_length=30) | ||||||
|  |     name = models.CharField(max_length=50) | ||||||
|  |     email = models.EmailField() | ||||||
|  |     request = models.TextField() | ||||||
|  |     stripe_api_cus_id = models.CharField(max_length=30) | ||||||
|  |     card_details_response = models.TextField() | ||||||
|  |     stripe_subscription_obj = models.TextField() | ||||||
|  |     stripe_onetime_charge = models.TextField() | ||||||
|  |     gp_details = models.TextField() | ||||||
|  |     specs = models.TextField() | ||||||
|  |     vm_template_id = models.PositiveIntegerField(default=0) | ||||||
|  |     template = models.TextField() | ||||||
|  |     billing_address_data = models.TextField() | ||||||
|  | @ -1,7 +1,8 @@ | ||||||
| import datetime | import datetime | ||||||
| import logging | import logging | ||||||
| 
 | import json | ||||||
| import stripe | import stripe | ||||||
|  | 
 | ||||||
| # Create your views here. | # Create your views here. | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.http import HttpResponse | from django.http import HttpResponse | ||||||
|  | @ -10,7 +11,7 @@ from django.views.decorators.http import require_POST | ||||||
| 
 | 
 | ||||||
| from datacenterlight.views import do_create_vm | from datacenterlight.views import do_create_vm | ||||||
| from membership.models import StripeCustomer | from membership.models import StripeCustomer | ||||||
| from hosting.models import HostingOrder | from hosting.models import IncompleteSubscriptions | ||||||
| 
 | 
 | ||||||
| from utils.models import BillingAddress, UserBillingAddress | from utils.models import BillingAddress, UserBillingAddress | ||||||
| from utils.tasks import send_plain_email_task | from utils.tasks import send_plain_email_task | ||||||
|  | @ -115,14 +116,16 @@ def handle_webhook(request): | ||||||
|             } |             } | ||||||
|         send_plain_email_task.delay(email_data) |         send_plain_email_task.delay(email_data) | ||||||
|     elif event.type == 'invoice.paid': |     elif event.type == 'invoice.paid': | ||||||
|         #https://stripe.com/docs/billing/migration/strong-customer-authentication#scenario-1-handling-fulfillment |         #More info:  https://stripe.com/docs/billing/migration/strong-customer-authentication#scenario-1-handling-fulfillment | ||||||
|         invoice_obj = event.data.object |         invoice_obj = event.data.object | ||||||
|         logger.debug("Webhook Event: invoice.paid") |         logger.debug("Webhook Event: invoice.paid") | ||||||
|         logger.debug("invoice_obj %s " % str(invoice_obj)) |         logger.debug("invoice_obj %s " % str(invoice_obj)) | ||||||
|         logger.debug("invoice_obj.paid = %s %s" % (invoice_obj.paid, type(invoice_obj.paid))) |         logger.debug("invoice_obj.paid = %s %s" % (invoice_obj.paid, type(invoice_obj.paid))) | ||||||
|         logger.debug("invoice_obj.billing_reason = %s %s" % (invoice_obj.billing_reason, type(invoice_obj.billing_reason))) |         logger.debug("invoice_obj.billing_reason = %s %s" % (invoice_obj.billing_reason, type(invoice_obj.billing_reason))) | ||||||
|         # We should check for billing_reason == "subscription_create" but we check for "subscription_update" |         # We should check for billing_reason == "subscription_create" but we | ||||||
|         # because we are using older api. See https://stripe.com/docs/upgrades?since=2015-07-13 |         # check for "subscription_update" | ||||||
|  |         # because we are using older api. | ||||||
|  |         # See https://stripe.com/docs/upgrades?since=2015-07-13 | ||||||
| 
 | 
 | ||||||
|         # The billing_reason attribute of the invoice object now can take the |         # The billing_reason attribute of the invoice object now can take the | ||||||
|         # value of subscription_create, indicating that it is the first |         # value of subscription_create, indicating that it is the first | ||||||
|  | @ -130,32 +133,54 @@ def handle_webhook(request): | ||||||
|         # billing_reason=subscription_create is represented as |         # billing_reason=subscription_create is represented as | ||||||
|         # subscription_update. |         # subscription_update. | ||||||
| 
 | 
 | ||||||
|         if invoice_obj.paid and invoice_obj.billing_reason == "subscription_update": |         if (invoice_obj.paid and | ||||||
|  |                 invoice_obj.billing_reason == "subscription_update"): | ||||||
|             logger.debug("Start provisioning") |             logger.debug("Start provisioning") | ||||||
|             # get subscription id, order_id |  | ||||||
|             ho = None |  | ||||||
|             try: |             try: | ||||||
|                 ho = HostingOrder.objects.get(subscription_id=invoice_obj.subscription) |                 stripe_subscription_obj = stripe.Subscription.retrieve( | ||||||
|  |                     invoice_obj.subscription) | ||||||
|  |                 try: | ||||||
|  |                     incomplete_sub = IncompleteSubscriptions.objects.get( | ||||||
|  |                         subscription_id=invoice_obj.subscription) | ||||||
|  |                     logger.debug("*******") | ||||||
|  |                     logger.debug(incomplete_sub) | ||||||
|  |                     logger.debug("*******") | ||||||
|  |                     do_create_vm( | ||||||
|  |                         request=incomplete_sub.request, | ||||||
|  |                         user={'name': incomplete_sub.name, | ||||||
|  |                               'email': incomplete_sub.email}, | ||||||
|  |                         stripe_api_cus_id=incomplete_sub.stripe_api_cus_id, | ||||||
|  |                         card_details_response=json.loads( | ||||||
|  |                             incomplete_sub.card_details_response), | ||||||
|  |                         stripe_subscription_obj=json.loads( | ||||||
|  |                             stripe_subscription_obj), | ||||||
|  |                         stripe_onetime_charge=json.loads( | ||||||
|  |                             incomplete_sub.stripe_onetime_charge), | ||||||
|  |                         gp_details=json.loads(incomplete_sub.gp_details), | ||||||
|  |                         specs=json.loads(incomplete_sub.specs), | ||||||
|  |                         vm_template_id=incomplete_sub.vm_template_id, | ||||||
|  |                         template=json.loads(incomplete_sub.template) | ||||||
|  |                     ) | ||||||
|  |                 except (IncompleteSubscriptions.DoesNotExist, | ||||||
|  |                         IncompleteSubscriptions.MultipleObjectsReturned) as ex: | ||||||
|  |                     logger.error(str(ex)) | ||||||
|  |                     # TODO Inform admin | ||||||
|  |                     email_data = { | ||||||
|  |                         'subject': "IncompleteSubscriptions error", | ||||||
|  |                         'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, | ||||||
|  |                         'to': settings.DCL_ERROR_EMAILS_TO_LIST, | ||||||
|  |                         'body': "Response = %s" % str(ex), | ||||||
|  |                     } | ||||||
|  |                     send_plain_email_task.delay(email_data) | ||||||
|             except Exception as ex: |             except Exception as ex: | ||||||
|                 logger.error(str(ex)) |                 logger.error(str(ex)) | ||||||
|             if ho: |                 email_data = { | ||||||
|                 logger.debug("Create a VM for order %s" % str(ho)) |                     'subject': "invoice.paid Webhook error", | ||||||
|                 # TODO: fix the error below |                     'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, | ||||||
|                 try: |                     'to': settings.DCL_ERROR_EMAILS_TO_LIST, | ||||||
|                     user = {'name': ho.customer.user.name, |                     'body': "Response = %s" % str(ex), | ||||||
|                             'email': ho.customer.user.email} |                 } | ||||||
|                     stripe_api_cus_id = ho.customer.stripe_id |                 send_plain_email_task.delay(email_data) | ||||||
|                     stripe_subscription_obj = stripe.Subscription.retrieve(invoice_obj.subscription) |  | ||||||
|                     do_create_vm(request, user, stripe_api_cus_id, |  | ||||||
|                                  card_details_response, |  | ||||||
|                                  stripe_subscription_obj, |  | ||||||
|                                  stripe_onetime_charge, gp_details, specs, |  | ||||||
|                                  vm_template_id, |  | ||||||
|                                  template |  | ||||||
|                                  ) |  | ||||||
|                 except Exception as ex: |  | ||||||
|                     logger.error(str(ex)) |  | ||||||
| 
 |  | ||||||
|     else: |     else: | ||||||
|         logger.error("Unhandled event : " + event.type) |         logger.error("Unhandled event : " + event.type) | ||||||
|     return HttpResponse(status=200) |     return HttpResponse(status=200) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue