Handle IncompleteSubscriptions in webhook

This commit is contained in:
PCoder 2020-12-23 10:59:21 +05:30
parent c4c918d591
commit 17c8f9ca18
3 changed files with 130 additions and 60 deletions

View file

@ -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,

View file

@ -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()

View file

@ -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(
except Exception as ex: invoice_obj.subscription)
logger.error(str(ex))
if ho:
logger.debug("Create a VM for order %s" % str(ho))
# TODO: fix the error below
try: try:
user = {'name': ho.customer.user.name, incomplete_sub = IncompleteSubscriptions.objects.get(
'email': ho.customer.user.email} subscription_id=invoice_obj.subscription)
stripe_api_cus_id = ho.customer.stripe_id logger.debug("*******")
stripe_subscription_obj = stripe.Subscription.retrieve(invoice_obj.subscription) logger.debug(incomplete_sub)
do_create_vm(request, user, stripe_api_cus_id, logger.debug("*******")
card_details_response, do_create_vm(
stripe_subscription_obj, request=incomplete_sub.request,
stripe_onetime_charge, gp_details, specs, user={'name': incomplete_sub.name,
vm_template_id, 'email': incomplete_sub.email},
template 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))
email_data = {
'subject': "invoice.paid Webhook 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)
else: else:
logger.error("Unhandled event : " + event.type) logger.error("Unhandled event : " + event.type)
return HttpResponse(status=200) return HttpResponse(status=200)