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,
|
||||||
|
|
|
@ -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(
|
||||||
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)
|
||||||
|
|
Loading…
Reference in a new issue