- {% include "datacenterlight/includes/_calculator_form.html" with vm_pricing=instance.pricing %}
+ {% include "datacenterlight/includes/_calculator_form.html" with vm_pricing=instance.pricing vm_base_price=vm_base_price %}
\ No newline at end of file
diff --git a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html
index f64a9500..2c2b51dd 100644
--- a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html
+++ b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html
@@ -9,6 +9,7 @@
window.ssdUnitPrice = {{vm_pricing.ssd_unit_price|default:0}};
window.hddUnitPrice = {{vm_pricing.hdd_unit_price|default:0}};
window.discountAmount = {{vm_pricing.discount_amount|default:0}};
+ window.vmBasePrice = {{vm_base_price|default:0}};
window.minRam = {{min_ram}};
window.minRamErr = '{% blocktrans with min_ram=min_ram %}Please enter a value in range {{min_ram}} - 200.{% endblocktrans %}';
diff --git a/hosting/static/hosting/js/initial.js b/hosting/static/hosting/js/initial.js
index 6b6d744d..36cf6d07 100644
--- a/hosting/static/hosting/js/initial.js
+++ b/hosting/static/hosting/js/initial.js
@@ -266,8 +266,8 @@ $( document ).ready(function() {
}
var total = (cardPricing['cpu'].value * window.coresUnitPrice) +
(cardPricing['ram'].value * window.ramUnitPrice) +
- (cardPricing['storage'].value * window.ssdUnitPrice) -
- window.discountAmount;
+ (cardPricing['storage'].value * window.ssdUnitPrice) +
+ window.vmBasePrice - window.discountAmount;
total = parseFloat(total.toFixed(2));
$("#total").text(total);
}
From c315030b06fde0d10e237d45f3f62fe7d785085c Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 3 Feb 2020 12:29:14 +0530
Subject: [PATCH 003/217] Add vm base price to missing places
---
utils/hosting_utils.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py
index 2c325364..2ad3d335 100644
--- a/utils/hosting_utils.py
+++ b/utils/hosting_utils.py
@@ -81,7 +81,8 @@ def get_vm_price(cpu, memory, disk_size, hdd_size=0, pricing_name='default'):
price = ((decimal.Decimal(cpu) * pricing.cores_unit_price) +
(decimal.Decimal(memory) * pricing.ram_unit_price) +
(decimal.Decimal(disk_size) * pricing.ssd_unit_price) +
- (decimal.Decimal(hdd_size) * pricing.hdd_unit_price))
+ (decimal.Decimal(hdd_size) * pricing.hdd_unit_price) +
+ decimal.Decimal(settings.VM_BASE_PRICE))
cents = decimal.Decimal('.01')
price = price.quantize(cents, decimal.ROUND_HALF_UP)
return round(float(price), 2)
@@ -104,7 +105,8 @@ def get_vm_price_for_given_vat(cpu, memory, ssd_size, hdd_size=0,
(decimal.Decimal(cpu) * pricing.cores_unit_price) +
(decimal.Decimal(memory) * pricing.ram_unit_price) +
(decimal.Decimal(ssd_size) * pricing.ssd_unit_price) +
- (decimal.Decimal(hdd_size) * pricing.hdd_unit_price)
+ (decimal.Decimal(hdd_size) * pricing.hdd_unit_price) +
+ decimal.Decimal(settings.VM_BASE_PRICE)
)
discount_name = pricing.discount_name
From 3ca1a4521738708312910d3c7aadaf6add89c1fc Mon Sep 17 00:00:00 2001
From: PCoder
Date: Tue, 4 Feb 2020 08:57:54 +0530
Subject: [PATCH 004/217] Add stripe_coupon_id to VMPricing
---
.../0031_vmpricing_stripe_coupon_id.py | 20 +++++++++++++++++++
datacenterlight/models.py | 1 +
2 files changed, 21 insertions(+)
create mode 100644 datacenterlight/migrations/0031_vmpricing_stripe_coupon_id.py
diff --git a/datacenterlight/migrations/0031_vmpricing_stripe_coupon_id.py b/datacenterlight/migrations/0031_vmpricing_stripe_coupon_id.py
new file mode 100644
index 00000000..d2e45871
--- /dev/null
+++ b/datacenterlight/migrations/0031_vmpricing_stripe_coupon_id.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2020-02-04 03:16
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('datacenterlight', '0030_dclnavbarpluginmodel_show_non_transparent_navbar_always'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='vmpricing',
+ name='stripe_coupon_id',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ ]
diff --git a/datacenterlight/models.py b/datacenterlight/models.py
index 6410254b..64d785a2 100644
--- a/datacenterlight/models.py
+++ b/datacenterlight/models.py
@@ -54,6 +54,7 @@ class VMPricing(models.Model):
discount_amount = models.DecimalField(
max_digits=6, decimal_places=2, default=0
)
+ stripe_coupon_id = models.CharField(max_length=255, null=True, blank=True)
def __str__(self):
display_str = self.name + ' => ' + ' - '.join([
From e322e58246dc9889e9146e51958b4c7cc46b2231 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Tue, 4 Feb 2020 09:06:10 +0530
Subject: [PATCH 005/217] Use appropriate stripe_coupon_id
---
datacenterlight/views.py | 10 +++++-----
hosting/views.py | 6 +++++-
utils/hosting_utils.py | 6 ++++--
3 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 185b3e29..3a720cdc 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -933,11 +933,11 @@ class OrderConfirmationView(DetailView, FormView):
subscription_result = stripe_utils.subscribe_customer_to_plan(
stripe_api_cus_id,
[{"plan": stripe_plan.get('response_object').stripe_plan_id}],
- coupon='ipv6-discount-8chf' if (
- 'name' in discount and
- discount['name'] is not None and
- 'ipv6' in discount['name'].lower()
- ) else "",
+ coupon=(discount['stripe_coupon_id']
+ if 'name' in discount and
+ 'ipv6' in discount['name'].lower() and
+ discount['stripe_coupon_id']
+ else ""),
tax_rates=[stripe_tax_rate.tax_rate_id] if stripe_tax_rate else [],
)
stripe_subscription_obj = subscription_result.get('response_object')
diff --git a/hosting/views.py b/hosting/views.py
index 729d115b..17af51fd 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -1185,7 +1185,11 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView):
subscription_result = stripe_utils.subscribe_customer_to_plan(
stripe_api_cus_id,
[{"plan": stripe_plan.get('response_object').stripe_plan_id}],
- coupon='ipv6-discount-8chf' if 'name' in discount and 'ipv6' in discount['name'].lower() else "",
+ coupon=(discount['stripe_coupon_id']
+ if 'name' in discount and
+ 'ipv6' in discount['name'].lower() and
+ discount['stripe_coupon_id']
+ else ""),
tax_rates=[stripe_tax_rate.tax_rate_id] if stripe_tax_rate else [],
)
stripe_subscription_obj = subscription_result.get('response_object')
diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py
index 2ad3d335..aebbf717 100644
--- a/utils/hosting_utils.py
+++ b/utils/hosting_utils.py
@@ -122,7 +122,8 @@ def get_vm_price_for_given_vat(cpu, memory, ssd_size, hdd_size=0,
discount = {
'name': discount_name,
'amount': discount_amount,
- 'amount_with_vat': round(float(discount_amount_with_vat), 2)
+ 'amount_with_vat': round(float(discount_amount_with_vat), 2),
+ 'stripe_coupon_id': pricing.stripe_coupon_id
}
return (round(float(price), 2), round(float(vat), 2),
round(float(vat_percent), 2), discount)
@@ -173,7 +174,8 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0,
vat = vat.quantize(cents, decimal.ROUND_HALF_UP)
discount = {
'name': pricing.discount_name,
- 'amount': round(float(pricing.discount_amount), 2)
+ 'amount': round(float(pricing.discount_amount), 2),
+ 'stripe_coupon_id': pricing.stripe_coupon_id
}
return (round(float(price), 2), round(float(vat), 2),
round(float(vat_percent), 2), discount)
From dd2eae68e6a1b989599beea3906d8dc489fdb1a6 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Tue, 4 Feb 2020 11:20:17 +0530
Subject: [PATCH 006/217] Use math round to round amount to 2 decimal places
---
datacenterlight/views.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 3a720cdc..abd17964 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -678,9 +678,9 @@ class OrderConfirmationView(DetailView, FormView):
vm_specs["vat_percent"] = vat_percent
vm_specs["vat_validation_status"] = request.session["vat_validation_status"] if "vat_validation_status" in request.session else ""
vm_specs["vat_country"] = user_vat_country
- vm_specs["price_with_vat"] = round_up(price * (1 + vm_specs["vat_percent"] * 0.01), 2)
- vm_specs["price_after_discount"] = round_up(price - discount['amount'], 2)
- vm_specs["price_after_discount_with_vat"] = round_up((price - discount['amount']) * (1 + vm_specs["vat_percent"] * 0.01), 2)
+ vm_specs["price_with_vat"] = round(price * (1 + vm_specs["vat_percent"] * 0.01), 2)
+ vm_specs["price_after_discount"] = round(price - discount['amount'], 2)
+ vm_specs["price_after_discount_with_vat"] = round((price - discount['amount']) * (1 + vm_specs["vat_percent"] * 0.01), 2)
discount["amount_with_vat"] = round(vm_specs["price_with_vat"] - vm_specs["price_after_discount_with_vat"], 2)
vm_specs["total_price"] = vm_specs["price_after_discount_with_vat"]
vm_specs["discount"] = discount
From f45f8dd51f1349243178a21e65688f1241eda305 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Tue, 4 Feb 2020 11:27:59 +0530
Subject: [PATCH 007/217] Remove unused method round_up
---
utils/hosting_utils.py | 5 -----
1 file changed, 5 deletions(-)
diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py
index aebbf717..b9e2eb8a 100644
--- a/utils/hosting_utils.py
+++ b/utils/hosting_utils.py
@@ -222,11 +222,6 @@ def get_ip_addresses(vm_id):
return "--"
-def round_up(n, decimals=0):
- multiplier = 10 ** decimals
- return math.ceil(n * multiplier) / multiplier
-
-
class HostingUtils:
@staticmethod
def clear_items_from_list(from_list, items_list):
From c05813804489cdb5bc73c96aa9564495990d4f2d Mon Sep 17 00:00:00 2001
From: PCoder
Date: Tue, 4 Feb 2020 17:07:14 +0530
Subject: [PATCH 008/217] Update Changelog for 2.10.2
---
Changelog | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/Changelog b/Changelog
index e636bc66..df611d74 100644
--- a/Changelog
+++ b/Changelog
@@ -1,4 +1,11 @@
-2.10.1: 2020-02-02:
+2.10.2: 2020-02-04
+ * Introduce base price for VMs and let admins add stripe_coupon_id (MR!730)
+ Notes for deployment:
+ 1. Add env variable `VM_BASE_PRICE`
+ 2. Migrate datacenterlight app. This introduces the stripe_coupon_code field in the VMPricing.
+ 3. Create a coupon in stripe with the desired value and note down the stripe's coupon id
+ 4. Update the discount amount and set the corresponding coupon id in the admin
+2.10.1: 2020-02-02
* Changes the pricing structure of generic products into the pre vat and with vat (like that for VM)
* Shows product name (if exists) in the invoices list if it belongs to a generic product
* Small bugfixes (right alignment of price in the invoice list, show prices with 2 decimal places etc)
From ea79fafb08a07d05dc90a9aae2f7f98184b6bd9f Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 6 Mar 2020 12:17:23 +0530
Subject: [PATCH 009/217] Handle unicodes in username + limit username length
---
dynamicweb/settings/base.py | 1 +
membership/models.py | 34 ++++++++++++++++++++++++++++++----
2 files changed, 31 insertions(+), 4 deletions(-)
diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py
index c959c237..9d5cf2bb 100644
--- a/dynamicweb/settings/base.py
+++ b/dynamicweb/settings/base.py
@@ -761,6 +761,7 @@ OTP_VERIFY_ENDPOINT = env('OTP_VERIFY_ENDPOINT')
FIRST_VM_ID_AFTER_EU_VAT = int_env('FIRST_VM_ID_AFTER_EU_VAT')
PRE_EU_VAT_RATE = float(env('PRE_EU_VAT_RATE'))
+MAX_USERNAME_LENGTH = int_env('MAX_USERNAME_LENGTH', 18)
if DEBUG:
from .local import * # flake8: noqa
diff --git a/membership/models.py b/membership/models.py
index 703b4800..ab098726 100644
--- a/membership/models.py
+++ b/membership/models.py
@@ -77,28 +77,54 @@ def get_first_and_last_name(full_name):
return first_name, last_name
+def limit_username_length(username):
+ """
+ Limit the length of username before the addition of random numbers to
+ 18 characters
+
+ :param username: the username to limit
+ :return:
+ """
+ if len(username) > settings.MAX_USERNAME_LENGTH:
+ username = username[(len(username) - settings.MAX_USERNAME_LENGTH):]
+ return username.strip()
+
+
def assign_username(user):
if not user.username:
ldap_manager = LdapManager()
# Try to come up with a username
first_name, last_name = get_first_and_last_name(user.name)
- user.username = unicodedata.normalize('NFKD', first_name + last_name)
+ user.username = unicodedata.normalize('NFKD', first_name + last_name).encode('ascii', 'ignore')
user.username = "".join([char for char in user.username if char.isalnum()]).lower()
+ if user.username.strip() == "":
+ try:
+ # the inferred username from name is empty, hence attempt
+ # inferring a username from email
+ logger.debug("Inferred username from name is empty. So, "
+ "inferring from email now.")
+ user.username = user.email[0:user.email.index("@")]
+ except Exception as ex:
+ logger.debug("Exception %s" % str(ex))
+ user.username = get_random_string(
+ allowed_chars='abcdefghijklmnopqrstuvwxyz'
+ )
exist = True
+ user_username = limit_username_length(user.username)
while exist:
# Check if it exists
- exist, entries = ldap_manager.check_user_exists(user.username)
+ exist, entries = ldap_manager.check_user_exists(user_username)
if exist:
# If username exists in ldap, come up with a new user name and check it again
- user.username = user.username + str(random.randint(0, 2 ** 10))
+ user.username = user_username + str(random.randint(0, 2 ** 10))
else:
# If username does not exists in ldap, try to save it in database
try:
user.save()
except IntegrityError:
# If username exists in database then come up with a new username
- user.username = user.username + str(random.randint(0, 2 ** 10))
+ user.username = user_username + str(random.randint(0, 2 ** 10))
exist = True
From a995f418b29b0fda388c09dee50e3a7a5f6b2b73 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 6 Mar 2020 12:25:42 +0530
Subject: [PATCH 010/217] Remove any non alphanum character present in email
for username
---
membership/models.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/membership/models.py b/membership/models.py
index ab098726..9a9dfd89 100644
--- a/membership/models.py
+++ b/membership/models.py
@@ -105,6 +105,9 @@ def assign_username(user):
logger.debug("Inferred username from name is empty. So, "
"inferring from email now.")
user.username = user.email[0:user.email.index("@")]
+ user.username = "".join(
+ [char for char in user.username if char.isalnum()]
+ ).lower()
except Exception as ex:
logger.debug("Exception %s" % str(ex))
user.username = get_random_string(
From 4890c4956f97bec49239d821133bcb99eb21295a Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 6 Mar 2020 15:29:42 +0530
Subject: [PATCH 011/217] Increase MAX_USERNAME_LENGTH to 64 chars
---
dynamicweb/settings/base.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py
index 9d5cf2bb..735cd913 100644
--- a/dynamicweb/settings/base.py
+++ b/dynamicweb/settings/base.py
@@ -761,7 +761,7 @@ OTP_VERIFY_ENDPOINT = env('OTP_VERIFY_ENDPOINT')
FIRST_VM_ID_AFTER_EU_VAT = int_env('FIRST_VM_ID_AFTER_EU_VAT')
PRE_EU_VAT_RATE = float(env('PRE_EU_VAT_RATE'))
-MAX_USERNAME_LENGTH = int_env('MAX_USERNAME_LENGTH', 18)
+MAX_USERNAME_LENGTH = int_env('MAX_USERNAME_LENGTH', 64)
if DEBUG:
from .local import * # flake8: noqa
From af70824d6a14a0a945519e7c01fdebbbeab785f7 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 6 Mar 2020 15:30:07 +0530
Subject: [PATCH 012/217] Increase username charfield to 120 chars
---
membership/models.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/membership/models.py b/membership/models.py
index 9a9dfd89..e5454839 100644
--- a/membership/models.py
+++ b/membership/models.py
@@ -145,7 +145,7 @@ class CustomUser(AbstractBaseUser, PermissionsMixin):
site = models.ForeignKey(Site, default=1)
name = models.CharField(max_length=50, validators=[validate_name])
email = models.EmailField(unique=True)
- username = models.CharField(max_length=60, unique=True, null=True)
+ username = models.CharField(max_length=120, unique=True, null=True)
validated = models.IntegerField(choices=VALIDATED_CHOICES, default=0)
in_ldap = models.BooleanField(default=False)
# By default, we initialize the validation_slug with appropriate value
From 2f16f2440ea2695c45e497f92fd4f20f5cc7e1ae Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 6 Mar 2020 15:47:25 +0530
Subject: [PATCH 013/217] Return default value when return value is None
---
dynamicweb/settings/base.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py
index 735cd913..ca8996d1 100644
--- a/dynamicweb/settings/base.py
+++ b/dynamicweb/settings/base.py
@@ -42,7 +42,7 @@ def int_env(val, default_value=0):
"details: {}").format(
val, str(e)))
- return return_value
+ return return_value if return_value is not None else default_value
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
From 4441ba37ca400201b03533b7a203d2bb669dc9d5 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 6 Mar 2020 15:48:02 +0530
Subject: [PATCH 014/217] Add migration:
membership/migrations/0012_auto_20200306_1016.py
---
.../migrations/0012_auto_20200306_1016.py | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 membership/migrations/0012_auto_20200306_1016.py
diff --git a/membership/migrations/0012_auto_20200306_1016.py b/membership/migrations/0012_auto_20200306_1016.py
new file mode 100644
index 00000000..f38f0780
--- /dev/null
+++ b/membership/migrations/0012_auto_20200306_1016.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2020-03-06 10:16
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('membership', '0011_auto_20191218_1050'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='customuser',
+ name='username',
+ field=models.CharField(max_length=120, null=True, unique=True),
+ ),
+ ]
From e18188603a0b28932134c9b7244eb68790ca4e1c Mon Sep 17 00:00:00 2001
From: PCoder
Date: Sat, 7 Mar 2020 11:13:57 +0530
Subject: [PATCH 015/217] Decode username back to string from bytes after
encode
---
membership/models.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/membership/models.py b/membership/models.py
index e5454839..b572833d 100644
--- a/membership/models.py
+++ b/membership/models.py
@@ -96,7 +96,9 @@ def assign_username(user):
# Try to come up with a username
first_name, last_name = get_first_and_last_name(user.name)
- user.username = unicodedata.normalize('NFKD', first_name + last_name).encode('ascii', 'ignore')
+ user.username = unicodedata.normalize(
+ 'NFKD', first_name + last_name
+ ).encode('ascii', 'ignore').decode('ascii', 'ignore')
user.username = "".join([char for char in user.username if char.isalnum()]).lower()
if user.username.strip() == "":
try:
From 9d96ecefeadbfc6728383a08f50cbae30e07f0b3 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 18 Mar 2020 12:44:18 +0530
Subject: [PATCH 016/217] Remove unwanted import
---
datacenterlight/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 2a55b96f..233edb20 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -29,7 +29,7 @@ from utils.forms import (
)
from utils.hosting_utils import (
get_vm_price_with_vat, get_all_public_keys, get_vat_rate_for_country,
- get_vm_price_for_given_vat, round_up
+ get_vm_price_for_given_vat
)
from utils.stripe_utils import StripeUtils
from utils.tasks import send_plain_email_task
From 46c33cf10723164245a32d582192f5d34b21766c Mon Sep 17 00:00:00 2001
From: PCoder
Date: Tue, 24 Mar 2020 12:34:11 +0530
Subject: [PATCH 017/217] Update price to 11.5 CHF per month in intro emails
---
.../locale/de/LC_MESSAGES/django.po | 36 ++++++++++---------
.../datacenterlight/emails/welcome_user.html | 2 +-
.../datacenterlight/emails/welcome_user.txt | 2 +-
3 files changed, 21 insertions(+), 19 deletions(-)
diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po
index 1a1a2a26..2dce7582 100644
--- a/datacenterlight/locale/de/LC_MESSAGES/django.po
+++ b/datacenterlight/locale/de/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-02-01 09:42+0000\n"
+"POT-Creation-Date: 2020-03-24 07:02+0000\n"
"PO-Revision-Date: 2018-03-30 23:22+0000\n"
"Last-Translator: b'Anonymous User '\n"
"Language-Team: LANGUAGE \n"
@@ -144,8 +144,8 @@ msgid ""
"the heart of Switzerland."
msgstr "Bei uns findest Du die günstiges VMs aus der Schweiz."
-msgid "Try now, order a VM. VM price starts from only 10.5 CHF per month."
-msgstr "Unser Angebot beginnt bei 10.5 CHF pro Monat. Probier's jetzt aus!"
+msgid "Try now, order a VM. VM price starts from only 11.5 CHF per month."
+msgstr "Unser Angebot beginnt bei 11.5 CHF pro Monat. Probier's jetzt aus!"
msgid "ORDER VM"
msgstr "VM BESTELLEN"
@@ -415,8 +415,9 @@ msgstr "Deine MwSt-Nummer wurde überprüft"
msgid ""
"Your VAT number is under validation. VAT will be adjusted, once the "
"validation is complete."
-msgstr "Deine MwSt-Nummer wird derzeit validiert. Die MwSt. wird angepasst, "
-"sobald die Validierung abgeschlossen ist."
+msgstr ""
+"Deine MwSt-Nummer wird derzeit validiert. Die MwSt. wird angepasst, sobald "
+"die Validierung abgeschlossen ist."
msgid "Payment method"
msgstr "Bezahlmethode"
@@ -430,18 +431,6 @@ msgstr "Bestellungsübersicht"
msgid "Product"
msgstr "Produkt"
-msgid "Price"
-msgstr "Preise"
-
-msgid "VAT for"
-msgstr "MwSt für"
-
-msgid "Total Amount"
-msgstr "Gesamtsumme"
-
-msgid "Amount"
-msgstr "Betrag"
-
msgid "Description"
msgstr "Beschreibung"
@@ -454,9 +443,13 @@ msgstr "Preis ohne MwSt."
msgid "Pre VAT"
msgstr "Exkl. MwSt."
+msgid "VAT for"
+msgstr "MwSt für"
+
msgid "Your Price in Total"
msgstr "Dein Gesamtpreis"
+#, python-format
msgid ""
"By clicking \"Place order\" this plan will charge your credit card account "
"with %(total_price)s CHF/year"
@@ -681,6 +674,15 @@ msgstr ""
"Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du "
"auf sie zugreifen kannst."
+#~ msgid "Price"
+#~ msgstr "Preise"
+
+#~ msgid "Total Amount"
+#~ msgstr "Gesamtsumme"
+
+#~ msgid "Amount"
+#~ msgstr "Betrag"
+
#~ msgid "Subtotal"
#~ msgstr "Zwischensumme"
diff --git a/datacenterlight/templates/datacenterlight/emails/welcome_user.html b/datacenterlight/templates/datacenterlight/emails/welcome_user.html
index 25185618..2044b2ee 100644
--- a/datacenterlight/templates/datacenterlight/emails/welcome_user.html
+++ b/datacenterlight/templates/datacenterlight/emails/welcome_user.html
@@ -28,7 +28,7 @@
{% blocktrans %}Thanks for joining us! We provide the most affordable virtual machines from the heart of Switzerland.{% endblocktrans %}
- {% blocktrans %}Try now, order a VM. VM price starts from only 10.5 CHF per month.{% endblocktrans %}
+ {% blocktrans %}Try now, order a VM. VM price starts from only 11.5 CHF per month.{% endblocktrans %}
diff --git a/datacenterlight/templates/datacenterlight/emails/welcome_user.txt b/datacenterlight/templates/datacenterlight/emails/welcome_user.txt
index 772e51a5..06e8aa33 100644
--- a/datacenterlight/templates/datacenterlight/emails/welcome_user.txt
+++ b/datacenterlight/templates/datacenterlight/emails/welcome_user.txt
@@ -3,7 +3,7 @@
{% trans "Welcome to Data Center Light!" %}
{% blocktrans %}Thanks for joining us! We provide the most affordable virtual machines from the heart of Switzerland.{% endblocktrans %}
-{% blocktrans %}Try now, order a VM. VM price starts from only 10.5 CHF per month.{% endblocktrans %}
+{% blocktrans %}Try now, order a VM. VM price starts from only 11.5 CHF per month.{% endblocktrans %}
{{ base_url }}{% url 'hosting:create_virtual_machine' %}
From cb3ff7310099723b5a9303df1e09494484c8e116 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 25 Mar 2020 18:47:00 +0530
Subject: [PATCH 018/217] Filter out None case for discount's name
---
datacenterlight/views.py | 1 +
hosting/views.py | 1 +
2 files changed, 2 insertions(+)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 233edb20..17a0479f 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -935,6 +935,7 @@ class OrderConfirmationView(DetailView, FormView):
[{"plan": stripe_plan.get('response_object').stripe_plan_id}],
coupon=(discount['stripe_coupon_id']
if 'name' in discount and
+ discount['name'] is not None and
'ipv6' in discount['name'].lower() and
discount['stripe_coupon_id']
else ""),
diff --git a/hosting/views.py b/hosting/views.py
index 969523b3..1d16a750 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -1187,6 +1187,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView):
[{"plan": stripe_plan.get('response_object').stripe_plan_id}],
coupon=(discount['stripe_coupon_id']
if 'name' in discount and
+ discount['name'] is not None and
'ipv6' in discount['name'].lower() and
discount['stripe_coupon_id']
else ""),
From 7072420ea5c32137a99c813b3ef88e8a0e1ccbc5 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 25 Mar 2020 18:55:49 +0530
Subject: [PATCH 019/217] Update Changelog for 2.10.6
---
Changelog | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Changelog b/Changelog
index a4840758..00d84edf 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,5 @@
+2.10.6: 2020-03-25
+ * Bugfix: Handle Nonetype for discount's name (MR!735)
2.10.5: 2020-03-17
* Introduce base price for VMs and let admins add stripe_coupon_id (MR!730)
Notes for deployment:
From cec7938c9c545f685f0940698dc502280161e33a Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 1 Apr 2020 11:30:27 +0530
Subject: [PATCH 020/217] Add tabs for one time payments
---
hosting/templates/hosting/invoices.html | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/hosting/templates/hosting/invoices.html b/hosting/templates/hosting/invoices.html
index 1a97fd1f..1c7e91b9 100644
--- a/hosting/templates/hosting/invoices.html
+++ b/hosting/templates/hosting/invoices.html
@@ -15,6 +15,11 @@
+
+ """.format(
+ product_name=hosting_order.generic_product.product_name.capitalize(),
+ created_at=hosting_order.created_at.strftime('%Y-%m-%d'),
+ total='%.2f' % (hosting_order.price),
+ receipt_url=reverse('hosting:orders',
+ kwargs={'pk': hosting_order.id}),
- see_invoice_text=_("See Invoice")
- ))
- else:
+ see_invoice_text=_("See Invoice")
+ ))
+ else:
+ return ""
+ except Exception as ex:
+ logger.error("Error %s" % str(ex))
return ""
From e8b79d6951fd2af3b42842fa8cb7be3c4d427b60 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Tue, 1 Dec 2020 17:12:55 +0530
Subject: [PATCH 076/217] Return emtpty string when plan is not set
---
datacenterlight/templatetags/custom_tags.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/datacenterlight/templatetags/custom_tags.py b/datacenterlight/templatetags/custom_tags.py
index 0015ac58..120cabbf 100644
--- a/datacenterlight/templatetags/custom_tags.py
+++ b/datacenterlight/templatetags/custom_tags.py
@@ -114,7 +114,7 @@ def get_line_item_from_stripe_invoice(invoice):
plan_name = ""
for line_data in invoice["lines"]["data"]:
if is_first:
- plan_name = line_data.plan.name
+ plan_name = line_data.plan.name if line_data.plan is not None else ""
start_date = line_data.period.start
end_date = line_data.period.end
is_first = False
From d980fb00003c15008a95bb3e9176e1f481926195 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Tue, 1 Dec 2020 17:13:09 +0530
Subject: [PATCH 077/217] Quote email in links
---
hosting/views.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/hosting/views.py b/hosting/views.py
index 438a0d55..d03661fd 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -1,6 +1,7 @@
import logging
import uuid
from datetime import datetime
+from urllib.parse import quote
from time import sleep
import stripe
@@ -1292,7 +1293,7 @@ class InvoiceListView(LoginRequiredMixin, TemplateView):
if ('user_email' in self.request.GET
and self.request.user.email == settings.ADMIN_EMAIL):
user_email = self.request.GET['user_email']
- context['user_email'] = user_email
+ context['user_email'] = '%s' % quote(user_email)
logger.debug(
"user_email = {}".format(user_email)
)
From bf1aad82b8e6a3c60d5218007e894b758df4061c Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 2 Dec 2020 18:38:37 +0530
Subject: [PATCH 078/217] Update Changelog for 2.13
---
Changelog | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Changelog b/Changelog
index 43d3495f..fdadadf1 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,6 @@
+2.13: 2020-12-02
+ * 8654: Fix 500 error on invoices list for the user contact+devuanhosting.com@virus.media (MR!742)
+ * 8593: Escape user's ssh key in xml-rpc call to create VM (MR!741)
2.12.1: 2020-07-21
* 8307: Introduce "Exclude vat calculations" for Generic Products (MR!740)
* Change DE VAT rate to 16% from 19% (MR!739)
From e9801eb9c47a584ed4ea2ce318f636c62f70013c Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 3 Dec 2020 09:48:08 +0530
Subject: [PATCH 079/217] Fix wrong logging
---
datacenterlight/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 973ce738..1e530ce0 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -716,7 +716,7 @@ class OrderConfirmationView(DetailView, FormView):
request.session.get('token')
)
logger.debug(
- "card_details=%s stripe_api_cus_id=%s" % (card_details))
+ "card_details=%s" % (card_details))
if not card_details.get('response_object'):
msg = card_details.get('error')
messages.add_message(self.request, messages.ERROR, msg,
From 352c780287e8c45230764e4fb8720c8a89e0f400 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 7 Dec 2020 07:53:20 +0530
Subject: [PATCH 080/217] Work in progress
---
datacenterlight/views.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 1e530ce0..32e62f3c 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -956,9 +956,9 @@ class OrderConfirmationView(DetailView, FormView):
logger.debug(stripe_subscription_obj)
latest_invoice = stripe.Invoice.retrieve(
stripe_subscription_obj.latest_invoice)
- ret = stripe.PaymentIntent.confirm(
- latest_invoice.payment_intent
- )
+ # ret = stripe.PaymentIntent.confirm(
+ # latest_invoice.payment_intent
+ # )
if ret.status == 'requires_source_action' or ret.status == 'requires_action':
pi = stripe.PaymentIntent.retrieve(
latest_invoice.payment_intent
From 17557fd4c98d36ed626914695dc0e17702769333 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 7 Dec 2020 09:52:53 +0530
Subject: [PATCH 081/217] Create refactored method handle_metadata_and_emails
---
.../commands/fix_vm_after_celery_error.py | 0
datacenterlight/tasks.py | 232 ++++++++++--------
2 files changed, 126 insertions(+), 106 deletions(-)
create mode 100644 datacenterlight/management/commands/fix_vm_after_celery_error.py
diff --git a/datacenterlight/management/commands/fix_vm_after_celery_error.py b/datacenterlight/management/commands/fix_vm_after_celery_error.py
new file mode 100644
index 00000000..e69de29b
diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py
index f080da90..9c98b729 100644
--- a/datacenterlight/tasks.py
+++ b/datacenterlight/tasks.py
@@ -56,11 +56,6 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id):
"Running create_vm_task on {}".format(current_task.request.hostname))
vm_id = None
try:
- final_price = (
- specs.get('total_price') if 'total_price' in specs
- else specs.get('price')
- )
-
if 'pass' in user:
on_user = user.get('username')
on_pass = user.get('pass')
@@ -92,107 +87,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id):
if vm_id is None:
raise Exception("Could not create VM")
- # Update HostingOrder with the created vm_id
- hosting_order = HostingOrder.objects.filter(id=order_id).first()
- error_msg = None
-
- try:
- hosting_order.vm_id = vm_id
- hosting_order.save()
- logger.debug(
- "Updated hosting_order {} with vm_id={}".format(
- hosting_order.id, vm_id
- )
- )
- except Exception as ex:
- error_msg = (
- "HostingOrder with id {order_id} not found. This means that "
- "the hosting order was not created and/or it is/was not "
- "associated with VM with id {vm_id}. Details {details}".format(
- order_id=order_id, vm_id=vm_id, details=str(ex)
- )
- )
- logger.error(error_msg)
-
- stripe_utils = StripeUtils()
- result = stripe_utils.set_subscription_metadata(
- subscription_id=hosting_order.subscription_id,
- metadata={"VM_ID": str(vm_id)}
- )
-
- if result.get('error') is not None:
- emsg = "Could not update subscription metadata for {sub}".format(
- sub=hosting_order.subscription_id
- )
- logger.error(emsg)
- if error_msg:
- error_msg += ". " + emsg
- else:
- error_msg = emsg
-
- vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data
-
- context = {
- 'name': user.get('name'),
- 'email': user.get('email'),
- 'cores': specs.get('cpu'),
- 'memory': specs.get('memory'),
- 'storage': specs.get('disk_size'),
- 'price': final_price,
- 'template': template.get('name'),
- 'vm_name': vm.get('name'),
- 'vm_id': vm['vm_id'],
- 'order_id': order_id
- }
-
- if error_msg:
- context['errors'] = error_msg
- if 'pricing_name' in specs:
- context['pricing'] = str(VMPricing.get_vm_pricing_by_name(
- name=specs['pricing_name']
- ))
- email_data = {
- 'subject': settings.DCL_TEXT + " Order from %s" % context['email'],
- 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
- 'to': ['info@ungleich.ch'],
- 'body': "\n".join(
- ["%s=%s" % (k, v) for (k, v) in context.items()]),
- 'reply_to': [context['email']],
- }
- email = EmailMessage(**email_data)
- email.send()
-
- if 'pass' in user:
- lang = 'en-us'
- if user.get('language') is not None:
- logger.debug(
- "Language is set to {}".format(user.get('language')))
- lang = user.get('language')
- translation.activate(lang)
- # Send notification to the user as soon as VM has been booked
- context = {
- 'base_url': "{0}://{1}".format(user.get('request_scheme'),
- user.get('request_host')),
- 'order_url': reverse('hosting:invoices'),
- 'page_header': _(
- 'Your New VM %(vm_name)s at Data Center Light') % {
- 'vm_name': vm.get('name')},
- 'vm_name': vm.get('name')
- }
- email_data = {
- 'subject': context.get('page_header'),
- 'to': user.get('email'),
- 'context': context,
- 'template_name': 'new_booked_vm',
- 'template_path': 'hosting/emails/',
- 'from_address': settings.DCL_SUPPORT_FROM_ADDRESS,
- }
- email = BaseEmail(**email_data)
- email.send()
-
- logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id))
- if vm_id > 0:
- get_or_create_vm_detail(custom_user, manager, vm_id)
+ handle_metadata_and_emails(order_id, vm_id, manager, user, specs,
+ template)
except Exception as e:
logger.error(str(e))
try:
@@ -214,3 +110,127 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id):
return
return vm_id
+
+
+def handle_metadata_and_emails(order_id, vm_id, manager, user, specs,
+ template):
+ """
+ Handle's setting up of the metadata in Stripe and database and sending of
+ emails to the user after VM creation
+
+ :param order_id: the hosting order id
+ :param vm_id: the id of the vm created
+ :param manager: the OpenNebula Manager instance
+ :param user: the user's dict passed to the celery task
+ :param specs: the specification's dict passed to the celery task
+ :param template: the template dict passed to the celery task
+
+ :return:
+ """
+
+ custom_user = CustomUser.objects.get(email=user.get('email'))
+ final_price = (
+ specs.get('total_price') if 'total_price' in specs
+ else specs.get('price')
+ )
+ # Update HostingOrder with the created vm_id
+ hosting_order = HostingOrder.objects.filter(id=order_id).first()
+ error_msg = None
+
+ try:
+ hosting_order.vm_id = vm_id
+ hosting_order.save()
+ logger.debug(
+ "Updated hosting_order {} with vm_id={}".format(
+ hosting_order.id, vm_id
+ )
+ )
+ except Exception as ex:
+ error_msg = (
+ "HostingOrder with id {order_id} not found. This means that "
+ "the hosting order was not created and/or it is/was not "
+ "associated with VM with id {vm_id}. Details {details}".format(
+ order_id=order_id, vm_id=vm_id, details=str(ex)
+ )
+ )
+ logger.error(error_msg)
+
+ stripe_utils = StripeUtils()
+ result = stripe_utils.set_subscription_metadata(
+ subscription_id=hosting_order.subscription_id,
+ metadata={"VM_ID": str(vm_id)}
+ )
+
+ if result.get('error') is not None:
+ emsg = "Could not update subscription metadata for {sub}".format(
+ sub=hosting_order.subscription_id
+ )
+ logger.error(emsg)
+ if error_msg:
+ error_msg += ". " + emsg
+ else:
+ error_msg = emsg
+
+ vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data
+
+ context = {
+ 'name': user.get('name'),
+ 'email': user.get('email'),
+ 'cores': specs.get('cpu'),
+ 'memory': specs.get('memory'),
+ 'storage': specs.get('disk_size'),
+ 'price': final_price,
+ 'template': template.get('name'),
+ 'vm_name': vm.get('name'),
+ 'vm_id': vm['vm_id'],
+ 'order_id': order_id
+ }
+
+ if error_msg:
+ context['errors'] = error_msg
+ if 'pricing_name' in specs:
+ context['pricing'] = str(VMPricing.get_vm_pricing_by_name(
+ name=specs['pricing_name']
+ ))
+ email_data = {
+ 'subject': settings.DCL_TEXT + " Order from %s" % context['email'],
+ 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
+ 'to': ['info@ungleich.ch'],
+ 'body': "\n".join(
+ ["%s=%s" % (k, v) for (k, v) in context.items()]),
+ 'reply_to': [context['email']],
+ }
+ email = EmailMessage(**email_data)
+ email.send()
+
+ if 'pass' in user:
+ lang = 'en-us'
+ if user.get('language') is not None:
+ logger.debug(
+ "Language is set to {}".format(user.get('language')))
+ lang = user.get('language')
+ translation.activate(lang)
+ # Send notification to the user as soon as VM has been booked
+ context = {
+ 'base_url': "{0}://{1}".format(user.get('request_scheme'),
+ user.get('request_host')),
+ 'order_url': reverse('hosting:invoices'),
+ 'page_header': _(
+ 'Your New VM %(vm_name)s at Data Center Light') % {
+ 'vm_name': vm.get('name')},
+ 'vm_name': vm.get('name')
+ }
+ email_data = {
+ 'subject': context.get('page_header'),
+ 'to': user.get('email'),
+ 'context': context,
+ 'template_name': 'new_booked_vm',
+ 'template_path': 'hosting/emails/',
+ 'from_address': settings.DCL_SUPPORT_FROM_ADDRESS,
+ }
+ email = BaseEmail(**email_data)
+ email.send()
+
+ logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id))
+ if vm_id > 0:
+ get_or_create_vm_detail(custom_user, manager, vm_id)
\ No newline at end of file
From 082c0b00affbc3c7c49ae13727d702931a425fcf Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 7 Dec 2020 09:53:14 +0530
Subject: [PATCH 082/217] Implement fix_vm_after_celery_error
---
.../commands/fix_vm_after_celery_error.py | 71 +++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/datacenterlight/management/commands/fix_vm_after_celery_error.py b/datacenterlight/management/commands/fix_vm_after_celery_error.py
index e69de29b..97df553e 100644
--- a/datacenterlight/management/commands/fix_vm_after_celery_error.py
+++ b/datacenterlight/management/commands/fix_vm_after_celery_error.py
@@ -0,0 +1,71 @@
+from django.core.management.base import BaseCommand
+from datacenterlight.tasks import handle_metadata_and_emails
+from opennebula_api.models import OpenNebulaManager
+import logging
+import json
+
+logger = logging.getLogger(__name__)
+
+
+class Command(BaseCommand):
+ help = '''Updates the DB after manual creation of VM'''
+
+ def add_arguments(self, parser):
+ parser.add_argument('vm_id', type=int)
+ parser.add_argument('order_id', type=int)
+ parser.add_argument('specs', type=str)
+
+ def handle(self, *args, **options):
+ vm_id = options['vm_id']
+ order_id = options['order_id']
+ user_str = options['user']
+ specs_str = options['specs']
+ template_str = options['template']
+
+ json_acceptable_string = user_str.replace("'", "\"")
+ user = json.loads(json_acceptable_string)
+
+ json_acceptable_string = specs_str.replace("'", "\"")
+ specs = json.loads(json_acceptable_string)
+
+ json_acceptable_string = template_str.replace("'", "\"")
+ template = json.loads(json_acceptable_string)
+ if vm_id <= 0:
+ self.stdout.write(self.style.ERROR(
+ 'vm_id can\'t be less than or 0. Given: %s' % vm_id))
+ return
+ if vm_id <= 0:
+ self.stdout.write(self.style.ERROR(
+ 'order_id can\'t be less than or 0. Given: %s' % vm_id))
+ return
+ if specs_str is None or specs_str == "":
+ self.stdout.write(
+ self.style.ERROR('specs can\'t be empty or None'))
+ return
+
+ user = {
+ 'name': user['name'],
+ 'email': user['email'],
+ 'username': user['username'],
+ 'pass': user['password'],
+ 'request_scheme': user['request_scheme'],
+ 'request_host': user['request_host'],
+ 'language': user['language'],
+ }
+
+ on_user = user.get('username')
+ on_pass = user.get('pass')
+
+ # Create OpenNebulaManager
+ manager = OpenNebulaManager(email=on_user, password=on_pass)
+ handle_metadata_and_emails(order_id, vm_id, manager, user, specs,
+ template)
+ self.stdout.write(
+ self.style.SUCCESS(
+ 'Done handling metadata and emails for %s %s %s' % (
+ order_id,
+ vm_id,
+ str(user)
+ )
+ )
+ )
From 81ba834b0178c2b75707487ab14ee65d9c8b3c61 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 7 Dec 2020 10:31:45 +0530
Subject: [PATCH 083/217] Add missing arguments
---
.../management/commands/fix_vm_after_celery_error.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/datacenterlight/management/commands/fix_vm_after_celery_error.py b/datacenterlight/management/commands/fix_vm_after_celery_error.py
index 97df553e..2f7325f4 100644
--- a/datacenterlight/management/commands/fix_vm_after_celery_error.py
+++ b/datacenterlight/management/commands/fix_vm_after_celery_error.py
@@ -13,7 +13,9 @@ class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument('vm_id', type=int)
parser.add_argument('order_id', type=int)
+ parser.add_argument('user', type=str)
parser.add_argument('specs', type=str)
+ parser.add_argument('template', type=str)
def handle(self, *args, **options):
vm_id = options['vm_id']
From 591f5ff37b5308f21ce7001df0fc9adf712c32d2 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 7 Dec 2020 10:37:00 +0530
Subject: [PATCH 084/217] Fix key
---
.../management/commands/fix_vm_after_celery_error.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/datacenterlight/management/commands/fix_vm_after_celery_error.py b/datacenterlight/management/commands/fix_vm_after_celery_error.py
index 2f7325f4..1ba37152 100644
--- a/datacenterlight/management/commands/fix_vm_after_celery_error.py
+++ b/datacenterlight/management/commands/fix_vm_after_celery_error.py
@@ -49,7 +49,7 @@ class Command(BaseCommand):
'name': user['name'],
'email': user['email'],
'username': user['username'],
- 'pass': user['password'],
+ 'pass': user['pass'],
'request_scheme': user['request_scheme'],
'request_host': user['request_host'],
'language': user['language'],
From bbb51b71a6f200913428e09af2a59f707135bb48 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 7 Dec 2020 10:40:55 +0530
Subject: [PATCH 085/217] Fix variable name
---
.../commands/fix_vm_after_celery_error.py | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/datacenterlight/management/commands/fix_vm_after_celery_error.py b/datacenterlight/management/commands/fix_vm_after_celery_error.py
index 1ba37152..f64963f6 100644
--- a/datacenterlight/management/commands/fix_vm_after_celery_error.py
+++ b/datacenterlight/management/commands/fix_vm_after_celery_error.py
@@ -25,7 +25,7 @@ class Command(BaseCommand):
template_str = options['template']
json_acceptable_string = user_str.replace("'", "\"")
- user = json.loads(json_acceptable_string)
+ user_dict = json.loads(json_acceptable_string)
json_acceptable_string = specs_str.replace("'", "\"")
specs = json.loads(json_acceptable_string)
@@ -46,13 +46,13 @@ class Command(BaseCommand):
return
user = {
- 'name': user['name'],
- 'email': user['email'],
- 'username': user['username'],
- 'pass': user['pass'],
- 'request_scheme': user['request_scheme'],
- 'request_host': user['request_host'],
- 'language': user['language'],
+ 'name': user_dict['name'],
+ 'email': user_dict['email'],
+ 'username': user_dict['username'],
+ 'pass': user_dict['pass'],
+ 'request_scheme': user_dict['request_scheme'],
+ 'request_host': user_dict['request_host'],
+ 'language': user_dict['language'],
}
on_user = user.get('username')
From ff28c6e8e8c6ded30c6792207475b1112a3e5a41 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 7 Dec 2020 10:44:20 +0530
Subject: [PATCH 086/217] Add log message
---
.../management/commands/fix_vm_after_celery_error.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/datacenterlight/management/commands/fix_vm_after_celery_error.py b/datacenterlight/management/commands/fix_vm_after_celery_error.py
index f64963f6..94c59868 100644
--- a/datacenterlight/management/commands/fix_vm_after_celery_error.py
+++ b/datacenterlight/management/commands/fix_vm_after_celery_error.py
@@ -59,6 +59,9 @@ class Command(BaseCommand):
on_pass = user.get('pass')
# Create OpenNebulaManager
+ self.style.SUCCESS(
+ 'Connecting using %s %s' % (on_user, on_pass)
+ )
manager = OpenNebulaManager(email=on_user, password=on_pass)
handle_metadata_and_emails(order_id, vm_id, manager, user, specs,
template)
From 8c374af4ffa96f880e2ccac6f5418849d3fe738d Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 7 Dec 2020 10:45:51 +0530
Subject: [PATCH 087/217] More logging
---
datacenterlight/management/commands/fix_vm_after_celery_error.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/datacenterlight/management/commands/fix_vm_after_celery_error.py b/datacenterlight/management/commands/fix_vm_after_celery_error.py
index 94c59868..b8c71f8d 100644
--- a/datacenterlight/management/commands/fix_vm_after_celery_error.py
+++ b/datacenterlight/management/commands/fix_vm_after_celery_error.py
@@ -62,6 +62,7 @@ class Command(BaseCommand):
self.style.SUCCESS(
'Connecting using %s %s' % (on_user, on_pass)
)
+ print('Connecting using %s %s' % (on_user, on_pass))
manager = OpenNebulaManager(email=on_user, password=on_pass)
handle_metadata_and_emails(order_id, vm_id, manager, user, specs,
template)
From 9d85c058da82aad3a2026e670b9ddcfb620ad3a5 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 7 Dec 2020 10:48:48 +0530
Subject: [PATCH 088/217] Fetch correct cred
---
.../management/commands/fix_vm_after_celery_error.py | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/datacenterlight/management/commands/fix_vm_after_celery_error.py b/datacenterlight/management/commands/fix_vm_after_celery_error.py
index b8c71f8d..3286db3a 100644
--- a/datacenterlight/management/commands/fix_vm_after_celery_error.py
+++ b/datacenterlight/management/commands/fix_vm_after_celery_error.py
@@ -1,6 +1,7 @@
from django.core.management.base import BaseCommand
from datacenterlight.tasks import handle_metadata_and_emails
from opennebula_api.models import OpenNebulaManager
+from membership.models import CustomUser
import logging
import json
@@ -54,16 +55,13 @@ class Command(BaseCommand):
'request_host': user_dict['request_host'],
'language': user_dict['language'],
}
-
- on_user = user.get('username')
- on_pass = user.get('pass')
-
+ cu = CustomUser.objects.get(username=user.get('username'))
# Create OpenNebulaManager
self.style.SUCCESS(
- 'Connecting using %s %s' % (on_user, on_pass)
+ 'Connecting using %s %s' % (cu.username, cu.password)
)
- print('Connecting using %s %s' % (on_user, on_pass))
- manager = OpenNebulaManager(email=on_user, password=on_pass)
+ print('Connecting using %s %s' % (cu.username, cu.password))
+ manager = OpenNebulaManager(email=cu.username, password=cu.password)
handle_metadata_and_emails(order_id, vm_id, manager, user, specs,
template)
self.stdout.write(
From 7bcca15f0b9fc4b6755774b9e78b67198e291ee7 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 7 Dec 2020 10:52:06 +0530
Subject: [PATCH 089/217] Cleanup logging
---
.../management/commands/fix_vm_after_celery_error.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/datacenterlight/management/commands/fix_vm_after_celery_error.py b/datacenterlight/management/commands/fix_vm_after_celery_error.py
index 3286db3a..0cfdb423 100644
--- a/datacenterlight/management/commands/fix_vm_after_celery_error.py
+++ b/datacenterlight/management/commands/fix_vm_after_celery_error.py
@@ -57,10 +57,11 @@ class Command(BaseCommand):
}
cu = CustomUser.objects.get(username=user.get('username'))
# Create OpenNebulaManager
- self.style.SUCCESS(
- 'Connecting using %s %s' % (cu.username, cu.password)
+ self.stdout.write(
+ self.style.SUCCESS(
+ 'Connecting using %s' % (cu.username)
+ )
)
- print('Connecting using %s %s' % (cu.username, cu.password))
manager = OpenNebulaManager(email=cu.username, password=cu.password)
handle_metadata_and_emails(order_id, vm_id, manager, user, specs,
template)
From a0ab436d9abeba91d0bf3f21babdb368454b48c9 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 7 Dec 2020 11:36:34 +0530
Subject: [PATCH 090/217] Update Changelog for 2.14
---
Changelog | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Changelog b/Changelog
index fdadadf1..b54713fe 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,5 @@
+2.14: 2020-12-07
+ * 8692: Create a script that fixes django db for the order after celery error (MR!743)
2.13: 2020-12-02
* 8654: Fix 500 error on invoices list for the user contact+devuanhosting.com@virus.media (MR!742)
* 8593: Escape user's ssh key in xml-rpc call to create VM (MR!741)
From 890a83cfa69335564964c421473cb2f5d23d72fb Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 10 Dec 2020 08:34:17 +0530
Subject: [PATCH 091/217] Add check_vm_templates management command
---
.../management/commands/check_vm_templates.py | 62 +++++++++++++++++++
1 file changed, 62 insertions(+)
create mode 100644 datacenterlight/management/commands/check_vm_templates.py
diff --git a/datacenterlight/management/commands/check_vm_templates.py b/datacenterlight/management/commands/check_vm_templates.py
new file mode 100644
index 00000000..ee86f15d
--- /dev/null
+++ b/datacenterlight/management/commands/check_vm_templates.py
@@ -0,0 +1,62 @@
+from django.core.management.base import BaseCommand
+from opennebula_api.models import OpenNebulaManager
+from datacenterlight.models import VMTemplate
+from membership.models import CustomUser
+
+from django.conf import settings
+from time import sleep
+import datetime
+import json
+import logging
+import os
+
+logger = logging.getLogger(__name__)
+
+
+class Command(BaseCommand):
+ help = '''Checks all VM templates to find if they can be instantiated'''
+
+ def add_arguments(self, parser):
+ parser.add_argument('user_email', type=str)
+
+ def handle(self, *args, **options):
+ result_dict = {}
+ user_email = options['user_email'] if 'user_email' in options else ""
+
+ if user_email:
+ cu = CustomUser.objects.get(email=user_email)
+ specs = {'cpu': 1, 'memory': 1, 'disk_size': 10}
+ manager = OpenNebulaManager(email=user_email, password=cu.password)
+ pub_keys = [settings.TEST_MANAGE_SSH_KEY_PUBKEY]
+ if not os.path.exists("outputs"):
+ os.mkdir("outputs")
+ for vm_template in VMTemplate.objects.all():
+ vm_name = 'test-%s' % vm_template.name
+ vm_id = manager.create_vm(
+ template_id=vm_template.opennebula_vm_template_id,
+ specs=specs,
+ ssh_key='\n'.join(pub_keys),
+ vm_name=vm_name
+ )
+ if vm_id > 0:
+ result_dict[vm_name] = "%s OK, created VM %s" % (
+ '%s %s' % (vm_template.opennebula_vm_template_id,
+ vm_template.name),
+ vm_id
+ )
+ self.stdout.write(self.style.SUCCESS(result_dict[vm_name]))
+ manager.delete_vm(vm_id)
+ else:
+ result_dict[vm_name] = '''Error creating VM %s, template_id
+ %s ''' % (vm_name, vm_template.opennebula_vm_template_id)
+ self.stdout.write(self.style.ERROR(result_dict[vm_name]))
+ sleep(1)
+ date_str = datetime.datetime.strftime(
+ datetime.datetime.now(), '%Y%m%d%H%M%S'
+ )
+ with open("output/check_vm_templates_%s.txt" % date_str, 'w',
+ encoding='utf-8') as f:
+ f.write(json.dumps(result_dict))
+ else:
+ self.stdout.write(self.style.ERROR("user_email not supplied"))
+ self.stdout.write(self.style.SUCCESS("Done"))
From 785091e4ff80ab17e439165600ad0fa75ceac395 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 10 Dec 2020 08:40:40 +0530
Subject: [PATCH 092/217] Handle vm_id is None case
---
datacenterlight/management/commands/check_vm_templates.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/datacenterlight/management/commands/check_vm_templates.py b/datacenterlight/management/commands/check_vm_templates.py
index ee86f15d..b75e9751 100644
--- a/datacenterlight/management/commands/check_vm_templates.py
+++ b/datacenterlight/management/commands/check_vm_templates.py
@@ -38,7 +38,7 @@ class Command(BaseCommand):
ssh_key='\n'.join(pub_keys),
vm_name=vm_name
)
- if vm_id > 0:
+ if vm_id and vm_id > 0:
result_dict[vm_name] = "%s OK, created VM %s" % (
'%s %s' % (vm_template.opennebula_vm_template_id,
vm_template.name),
From 01d8cc1b9bfebf5b5369cf4e79933e24ac5ba0c8 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 10 Dec 2020 08:56:34 +0530
Subject: [PATCH 093/217] Use absolute paths
---
.../management/commands/check_vm_templates.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/datacenterlight/management/commands/check_vm_templates.py b/datacenterlight/management/commands/check_vm_templates.py
index b75e9751..6346be50 100644
--- a/datacenterlight/management/commands/check_vm_templates.py
+++ b/datacenterlight/management/commands/check_vm_templates.py
@@ -28,8 +28,9 @@ class Command(BaseCommand):
specs = {'cpu': 1, 'memory': 1, 'disk_size': 10}
manager = OpenNebulaManager(email=user_email, password=cu.password)
pub_keys = [settings.TEST_MANAGE_SSH_KEY_PUBKEY]
- if not os.path.exists("outputs"):
- os.mkdir("outputs")
+ PROJECT_PATH = os.path.abspath(os.path.dirname(__name__))
+ if not os.path.exists("%s/outputs" % PROJECT_PATH):
+ os.mkdir("%s/outputs" % PROJECT_PATH)
for vm_template in VMTemplate.objects.all():
vm_name = 'test-%s' % vm_template.name
vm_id = manager.create_vm(
@@ -54,7 +55,9 @@ class Command(BaseCommand):
date_str = datetime.datetime.strftime(
datetime.datetime.now(), '%Y%m%d%H%M%S'
)
- with open("output/check_vm_templates_%s.txt" % date_str, 'w',
+ with open("%s/outputs/check_vm_templates_%s.txt" %
+ (PROJECT_PATH, date_str),
+ 'w',
encoding='utf-8') as f:
f.write(json.dumps(result_dict))
else:
From 22d3b1f83c465dd1bbda416c731de1b3692dfba4 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 10 Dec 2020 09:05:04 +0530
Subject: [PATCH 094/217] Add vm_type to the log info
---
datacenterlight/management/commands/check_vm_templates.py | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/datacenterlight/management/commands/check_vm_templates.py b/datacenterlight/management/commands/check_vm_templates.py
index 6346be50..6424df80 100644
--- a/datacenterlight/management/commands/check_vm_templates.py
+++ b/datacenterlight/management/commands/check_vm_templates.py
@@ -41,15 +41,17 @@ class Command(BaseCommand):
)
if vm_id and vm_id > 0:
result_dict[vm_name] = "%s OK, created VM %s" % (
- '%s %s' % (vm_template.opennebula_vm_template_id,
- vm_template.name),
+ '%s %s %s' % (vm_template.opennebula_vm_template_id,
+ vm_template.name, vm_template.vm_type),
vm_id
)
self.stdout.write(self.style.SUCCESS(result_dict[vm_name]))
manager.delete_vm(vm_id)
else:
result_dict[vm_name] = '''Error creating VM %s, template_id
- %s ''' % (vm_name, vm_template.opennebula_vm_template_id)
+ %s %s''' % (vm_name,
+ vm_template.opennebula_vm_template_id,
+ vm_template.vm_type)
self.stdout.write(self.style.ERROR(result_dict[vm_name]))
sleep(1)
date_str = datetime.datetime.strftime(
From 57b6b18243306b534db1306054996c6fe1d93cc2 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 10 Dec 2020 10:04:04 +0530
Subject: [PATCH 095/217] Fix PEP warning
---
datacenterlight/management/commands/check_vm_templates.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/datacenterlight/management/commands/check_vm_templates.py b/datacenterlight/management/commands/check_vm_templates.py
index 6424df80..be0a1957 100644
--- a/datacenterlight/management/commands/check_vm_templates.py
+++ b/datacenterlight/management/commands/check_vm_templates.py
@@ -42,7 +42,7 @@ class Command(BaseCommand):
if vm_id and vm_id > 0:
result_dict[vm_name] = "%s OK, created VM %s" % (
'%s %s %s' % (vm_template.opennebula_vm_template_id,
- vm_template.name, vm_template.vm_type),
+ vm_template.name, vm_template.vm_type),
vm_id
)
self.stdout.write(self.style.SUCCESS(result_dict[vm_name]))
From 504681107af92d60d566a33eda7635aa49e7b496 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 10 Dec 2020 10:04:16 +0530
Subject: [PATCH 096/217] Remove unreachable code
---
datacenterlight/management/commands/check_vm_templates.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/datacenterlight/management/commands/check_vm_templates.py b/datacenterlight/management/commands/check_vm_templates.py
index be0a1957..db36fde8 100644
--- a/datacenterlight/management/commands/check_vm_templates.py
+++ b/datacenterlight/management/commands/check_vm_templates.py
@@ -62,6 +62,4 @@ class Command(BaseCommand):
'w',
encoding='utf-8') as f:
f.write(json.dumps(result_dict))
- else:
- self.stdout.write(self.style.ERROR("user_email not supplied"))
self.stdout.write(self.style.SUCCESS("Done"))
From 0b0c932e5a115d145980e58f1888af14e7dac98e Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 18 Dec 2020 16:45:16 +0530
Subject: [PATCH 097/217] Refactor code: show_error
---
datacenterlight/views.py | 95 ++++++++++++----------------------------
1 file changed, 29 insertions(+), 66 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 32e62f3c..a3a027cc 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -719,28 +719,7 @@ class OrderConfirmationView(DetailView, FormView):
"card_details=%s" % (card_details))
if not card_details.get('response_object'):
msg = card_details.get('error')
- messages.add_message(self.request, messages.ERROR, msg,
- extra_tags='failed_payment')
- response = {
- 'status': False,
- 'redirect': "{url}#{section}".format(
- url=(reverse(
- 'show_product',
- kwargs={'product_slug':
- request.session['generic_payment_details']
- ['product_slug']}
- ) if 'generic_payment_details' in request.session else
- reverse('datacenterlight:payment')
- ),
- section='payment_error'),
- 'msg_title': str(_('Error.')),
- 'msg_body': str(
- _('There was a payment related error.'
- ' On close of this popup, you will be'
- ' redirected back to the payment page.')
- )
- }
- return JsonResponse(response)
+ return show_error(msg, self.request)
card_details_response = card_details['response_object']
card_details_dict = {
'last4': card_details_response['last4'],
@@ -765,30 +744,7 @@ class OrderConfirmationView(DetailView, FormView):
details=acc_result['error']
)
)
- messages.add_message(self.request, messages.ERROR, msg,
- extra_tags='failed_payment')
- response = {
- 'status': False,
- 'redirect': "{url}#{section}".format(
- url=(reverse(
- 'show_product',
- kwargs={'product_slug':
- request.session
- ['generic_payment_details']
- ['product_slug']}
- ) if 'generic_payment_details' in
- request.session else
- reverse('datacenterlight:payment')
- ),
- section='payment_error'),
- 'msg_title': str(_('Error.')),
- 'msg_body': str(
- _('There was a payment related error.'
- ' On close of this popup, you will be redirected'
- ' back to the payment page.')
- )
- }
- return JsonResponse(response)
+ return show_error(msg, self.request)
elif 'card_id' in request.session:
card_id = request.session.get('card_id')
user_card_detail = UserCardDetail.objects.get(id=card_id)
@@ -831,26 +787,7 @@ class OrderConfirmationView(DetailView, FormView):
# Check if the payment was approved
if not stripe_onetime_charge:
msg = charge_response.get('error')
- messages.add_message(self.request, messages.ERROR, msg,
- extra_tags='failed_payment')
- response = {
- 'status': False,
- 'redirect': "{url}#{section}".format(
- url=(reverse('show_product', kwargs={
- 'product_slug': gp_details['product_slug']}
- ) if 'generic_payment_details' in
- request.session else
- reverse('datacenterlight:payment')
- ),
- section='payment_error'),
- 'msg_title': str(_('Error.')),
- 'msg_body': str(
- _('There was a payment related error.'
- ' On close of this popup, you will be redirected'
- ' back to the payment page.'))
- }
- return JsonResponse(response)
-
+ return show_error(msg, self.request)
if ('generic_payment_type' not in request.session or
(request.session['generic_payment_details']['recurring'])):
recurring_interval = 'month'
@@ -1222,3 +1159,29 @@ class OrderConfirmationView(DetailView, FormView):
}
return JsonResponse(response)
+
+
+def show_error(msg, request):
+ messages.add_message(request, messages.ERROR, msg,
+ extra_tags='failed_payment')
+ response = {
+ 'status': False,
+ 'redirect': "{url}#{section}".format(
+ url=(reverse(
+ 'show_product',
+ kwargs={'product_slug':
+ request.session['generic_payment_details']
+ ['product_slug']}
+ ) if 'generic_payment_details' in request.session else
+ reverse('datacenterlight:payment')
+ ),
+ section='payment_error'
+ ),
+ 'msg_title': str(_('Error.')),
+ 'msg_body': str(
+ _('There was a payment related error.'
+ ' On close of this popup, you will be redirected back to'
+ ' the payment page.'))
+ }
+ return JsonResponse(response)
+
From d0d5fb01967afc2ed7aa69ec9a280c72d8945653 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 18 Dec 2020 16:47:45 +0530
Subject: [PATCH 098/217] Handle payment_intent requires SCA case
---
datacenterlight/views.py | 63 +++++++++++++++-------------------------
1 file changed, 23 insertions(+), 40 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index a3a027cc..abf0814a 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -783,7 +783,6 @@ class OrderConfirmationView(DetailView, FormView):
customer=stripe_api_cus_id
)
stripe_onetime_charge = charge_response.get('response_object')
-
# Check if the payment was approved
if not stripe_onetime_charge:
msg = charge_response.get('error')
@@ -893,52 +892,37 @@ class OrderConfirmationView(DetailView, FormView):
logger.debug(stripe_subscription_obj)
latest_invoice = stripe.Invoice.retrieve(
stripe_subscription_obj.latest_invoice)
- # ret = stripe.PaymentIntent.confirm(
- # latest_invoice.payment_intent
- # )
- if ret.status == 'requires_source_action' or ret.status == 'requires_action':
- pi = stripe.PaymentIntent.retrieve(
- latest_invoice.payment_intent
- )
- context = {
- 'sid': stripe_subscription_obj.id,
- 'payment_intent_secret': pi.client_secret,
- 'STRIPE_PUBLISHABLE_KEY': settings.STRIPE_API_PUBLIC_KEY,
- 'showSCA': True
- }
- return JsonResponse(context)
+
# Check if the subscription was approved and is active
if (stripe_subscription_obj is None
- or stripe_subscription_obj.status != 'active'):
+ or (stripe_subscription_obj.status != 'active'
+ and stripe_subscription_obj.status != 'incomplete')):
# At this point, we have created a Stripe API card and
# associated it with the customer; but the transaction failed
# due to some reason. So, we would want to dissociate this card
# here.
# ...
-
msg = subscription_result.get('error')
- messages.add_message(self.request, messages.ERROR, msg,
- extra_tags='failed_payment')
- response = {
- 'status': False,
- 'redirect': "{url}#{section}".format(
- url=(reverse(
- 'show_product',
- kwargs={'product_slug':
- request.session['generic_payment_details']
- ['product_slug']}
- ) if 'generic_payment_details' in request.session else
- reverse('datacenterlight:payment')
- ),
- section='payment_error'
- ),
- 'msg_title': str(_('Error.')),
- 'msg_body': str(
- _('There was a payment related error.'
- ' On close of this popup, you will be redirected back to'
- ' the payment page.'))
- }
- return JsonResponse(response)
+ return show_error(msg, self.request)
+ elif stripe_subscription_obj.status == 'incomplete':
+ pi = stripe.PaymentIntent.retrieve(
+ latest_invoice.payment_intent
+ )
+ # TODO: requires_attention is probably wrong value to compare
+ if (pi.status == 'requires_attention' or
+ pi.status == 'requires_source_action'):
+ logger.debug("Display SCA authentication")
+ context = {
+ 'sid': stripe_subscription_obj.id,
+ 'payment_intent_secret': pi.client_secret,
+ 'STRIPE_PUBLISHABLE_KEY': settings.STRIPE_API_PUBLIC_KEY,
+ 'showSCA': True
+ }
+ return JsonResponse(context)
+ else:
+ logger.debug("Handle this case")
+ msg = subscription_result.get('error')
+ return show_error(msg, self.request)
# Create user if the user is not logged in and if he is not already
# registered
@@ -1184,4 +1168,3 @@ def show_error(msg, request):
' the payment page.'))
}
return JsonResponse(response)
-
From a63fac1a2081378ebed59b4a4d4232d081bf223f Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 18 Dec 2020 17:16:40 +0530
Subject: [PATCH 099/217] Set data at the client side according to success or
error
---
datacenterlight/views.py | 36 ++++++++++++++++++-
.../hosting/js/virtual_machine_detail.js | 14 +++++---
2 files changed, 44 insertions(+), 6 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index abf0814a..77c0444f 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -916,7 +916,41 @@ class OrderConfirmationView(DetailView, FormView):
'sid': stripe_subscription_obj.id,
'payment_intent_secret': pi.client_secret,
'STRIPE_PUBLISHABLE_KEY': settings.STRIPE_API_PUBLIC_KEY,
- 'showSCA': True
+ 'showSCA': True,
+ 'success': {
+ 'status': True,
+ 'redirect': (
+ reverse('hosting:virtual_machines')
+ if request.user.is_authenticated()
+ else reverse('datacenterlight:index')
+ ),
+ 'msg_title': str(_('Thank you for the order.')),
+ 'msg_body': str(
+ _('Your VM will be up and running in a few moments.'
+ ' We will send you a confirmation email as soon as'
+ ' it is ready.'))
+ },
+ 'error': {
+ 'status': False,
+ 'redirect': "{url}#{section}".format(
+ url=(reverse(
+ 'show_product',
+ kwargs={'product_slug':
+ request.session[
+ 'generic_payment_details']
+ ['product_slug']}
+ ) if 'generic_payment_details' in request.session else
+ reverse('datacenterlight:payment')
+ ),
+ section='payment_error'
+ ),
+ 'msg_title': str(_('Error.')),
+ 'msg_body': str(
+ _('There was a payment related error.'
+ ' On close of this popup, you will be redirected back to'
+ ' the payment page.')
+ )
+ }
}
return JsonResponse(context)
else:
diff --git a/hosting/static/hosting/js/virtual_machine_detail.js b/hosting/static/hosting/js/virtual_machine_detail.js
index db1faaf8..dec8e680 100644
--- a/hosting/static/hosting/js/virtual_machine_detail.js
+++ b/hosting/static/hosting/js/virtual_machine_detail.js
@@ -110,16 +110,20 @@ $(document).ready(function() {
if (data.showSCA){
console.log("Show SCA");
var stripe = Stripe(data.STRIPE_PUBLISHABLE_KEY);
-
stripe.confirmCardPayment(data.payment_intent_secret).then(function(result) {
if (result.error) {
// Display error.message in your UI.
- $("#3ds_result").text("Error!");
- $("#3ds_result").addClass("text-danger");
+ modal_btn.attr('href', data.error.redirect).removeClass('hide');
+ fa_icon.attr('class', 'fa fa-close');
+ modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide');
+ $('#createvm-modal-title').text(data.error.msg_title);
+ $('#createvm-modal-body').html(data.error.msg_body);
} else {
// The payment has succeeded. Display a success message.
- $("#3ds_result").text("Thank you for payment");
- $("#3ds_result").addClass("text-success");
+ modal_btn.attr('href', data.success.redirect).removeClass('hide');
+ fa_icon.attr('class', 'checkmark');
+ $('#createvm-modal-title').text(data.success.msg_title);
+ $('#createvm-modal-body').html(data.success.msg_body);
}
});
$('#3Dsecure-modal').show();
From 3389e69af101e8f3e0c1144092443e14f064e653 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 18 Dec 2020 17:34:40 +0530
Subject: [PATCH 100/217] WIP: Begin handling of invoice.paid webhook
---
webhook/views.py | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/webhook/views.py b/webhook/views.py
index 516d1afc..ad598805 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -111,8 +111,15 @@ def handle_webhook(request):
'to': settings.DCL_ERROR_EMAILS_TO_LIST,
'body': "Response = %s" % str(tax_id_obj),
}
-
send_plain_email_task.delay(email_data)
+ elif event.type == 'invoice.paid':
+ #https://stripe.com/docs/billing/migration/strong-customer-authentication#scenario-1-handling-fulfillment
+ invoice_obj = event.data.object
+ logger.debug("Webhook Event: invoice.paid")
+ logger.debug("invoice_obj %s " % str(invoice_obj))
+ if invoice_obj.paid and invoice_obj.billing_reason == "subscription_create":
+ logger.debug("Start provisioning")
+
else:
logger.error("Unhandled event : " + event.type)
return HttpResponse(status=200)
From cb7a1ed4f4fff84c11cf94a8b0b6e060be9de402 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Sun, 20 Dec 2020 02:41:30 +0530
Subject: [PATCH 101/217] Implement provisioning of VM on invoice.paid webhook
---
datacenterlight/views.py | 420 ++++++++++++++++++++-------------------
webhook/views.py | 31 ++-
2 files changed, 247 insertions(+), 204 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 77c0444f..e04f5234 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -710,6 +710,7 @@ class OrderConfirmationView(DetailView, FormView):
stripe_api_cus_id = request.session.get('customer')
stripe_utils = StripeUtils()
logger.debug("user=%s stripe_api_cus_id=%s" % (user, stripe_api_cus_id))
+ card_details_response = None
if 'token' in request.session:
card_details = stripe_utils.get_cards_details_from_token(
@@ -954,212 +955,18 @@ class OrderConfirmationView(DetailView, FormView):
}
return JsonResponse(context)
else:
- logger.debug("Handle this case")
+ logger.debug(
+ "Handle this case when "
+ "stripe.subscription_status is incomplete but "
+ "pi.status is neither requires_attention nor "
+ "requires_source_action")
msg = subscription_result.get('error')
return show_error(msg, self.request)
- # Create user if the user is not logged in and if he is not already
- # registered
- if not request.user.is_authenticated():
- try:
- custom_user = CustomUser.objects.get(
- email=user.get('email'))
- stripe_customer = StripeCustomer.objects.filter(
- user_id=custom_user.id).first()
- if stripe_customer is None:
- stripe_customer = StripeCustomer.objects.create(
- user=custom_user, stripe_id=stripe_api_cus_id
- )
- stripe_customer_id = stripe_customer.id
- except CustomUser.DoesNotExist:
- logger.debug(
- "Customer {} does not exist.".format(user.get('email')))
- password = CustomUser.get_random_password()
- base_url = "{0}://{1}".format(self.request.scheme,
- self.request.get_host())
- custom_user = CustomUser.register(
- user.get('name'), password,
- user.get('email'),
- app='dcl', base_url=base_url, send_email=True,
- account_details=password
- )
- logger.debug("Created user {}.".format(user.get('email')))
- stripe_customer = StripeCustomer.objects. \
- create(user=custom_user, stripe_id=stripe_api_cus_id)
- stripe_customer_id = stripe_customer.id
- new_user = authenticate(username=custom_user.email,
- password=password)
- login(request, new_user)
- if 'new_user_hosting_key_id' in self.request.session:
- user_hosting_key = UserHostingKey.objects.get(id=self.request.session['new_user_hosting_key_id'])
- user_hosting_key.user = new_user
- user_hosting_key.save()
- else:
- # We assume that if the user is here, his/her StripeCustomer
- # object already exists
- stripe_customer_id = request.user.stripecustomer.id
- custom_user = request.user
-
- if 'token' in request.session:
- ucd = UserCardDetail.get_or_create_user_card_detail(
- stripe_customer=self.request.user.stripecustomer,
- card_details=card_details_response
- )
- UserCardDetail.save_default_card_local(
- self.request.user.stripecustomer.stripe_id,
- ucd.card_id
- )
- else:
- card_id = request.session.get('card_id')
- user_card_detail = UserCardDetail.objects.get(id=card_id)
- card_details_dict = {
- 'last4': user_card_detail.last4,
- 'brand': user_card_detail.brand,
- 'card_id': user_card_detail.card_id
- }
- if not user_card_detail.preferred:
- UserCardDetail.set_default_card(
- stripe_api_cus_id=stripe_api_cus_id,
- stripe_source_id=user_card_detail.card_id
- )
-
- # Save billing address
- billing_address_data = request.session.get('billing_address_data')
- logger.debug('billing_address_data is {}'.format(billing_address_data))
- billing_address_data.update({
- 'user': custom_user.id
- })
-
- if 'generic_payment_type' in request.session:
- stripe_cus = StripeCustomer.objects.filter(
- stripe_id=stripe_api_cus_id
- ).first()
- billing_address = BillingAddress(
- cardholder_name=billing_address_data['cardholder_name'],
- street_address=billing_address_data['street_address'],
- city=billing_address_data['city'],
- postal_code=billing_address_data['postal_code'],
- country=billing_address_data['country'],
- vat_number=billing_address_data['vat_number']
- )
- billing_address.save()
-
- order = HostingOrder.create(
- price=self.request
- .session['generic_payment_details']['amount'],
- customer=stripe_cus,
- billing_address=billing_address,
- vm_pricing=VMPricing.get_default_pricing()
- )
-
- # Create a Hosting Bill
- HostingBill.create(customer=stripe_cus,
- billing_address=billing_address)
-
- # Create Billing Address for User if he does not have one
- if not stripe_cus.user.billing_addresses.count():
- billing_address_data.update({
- 'user': stripe_cus.user.id
- })
- billing_address_user_form = UserBillingAddressForm(
- billing_address_data
- )
- billing_address_user_form.is_valid()
- billing_address_user_form.save()
-
- if self.request.session['generic_payment_details']['recurring']:
- # Associate the given stripe subscription with the order
- order.set_subscription_id(
- stripe_subscription_obj.id, card_details_dict
- )
- else:
- # Associate the given stripe charge id with the order
- order.set_stripe_charge(stripe_onetime_charge)
-
- # Set order status approved
- order.set_approved()
- order.generic_payment_description = gp_details["description"]
- order.generic_product_id = gp_details["product_id"]
- order.save()
- # send emails
- context = {
- 'name': user.get('name'),
- 'email': user.get('email'),
- 'amount': gp_details['amount'],
- 'description': gp_details['description'],
- 'recurring': gp_details['recurring'],
- 'product_name': gp_details['product_name'],
- 'product_id': gp_details['product_id'],
- 'order_id': order.id
- }
-
- email_data = {
- 'subject': (settings.DCL_TEXT +
- " Payment received from %s" % context['email']),
- 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
- 'to': ['info@ungleich.ch'],
- 'body': "\n".join(
- ["%s=%s" % (k, v) for (k, v) in context.items()]),
- 'reply_to': [context['email']],
- }
- send_plain_email_task.delay(email_data)
- recurring_text = _(" This is a monthly recurring plan.")
- if gp_details['recurring_interval'] == "year":
- recurring_text = _(" This is an yearly recurring plan.")
-
- email_data = {
- 'subject': _("Confirmation of your payment"),
- 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
- 'to': [user.get('email')],
- 'body': _("Hi {name},\n\n"
- "thank you for your order!\n"
- "We have just received a payment of CHF {amount:.2f}"
- " from you.{recurring}\n\n"
- "Cheers,\nYour Data Center Light team".format(
- name=user.get('name'),
- amount=gp_details['amount'],
- recurring=(
- recurring_text
- if gp_details['recurring'] else ''
- )
- )
- ),
- 'reply_to': ['info@ungleich.ch'],
- }
- send_plain_email_task.delay(email_data)
-
- response = {
- 'status': True,
- 'redirect': (
- reverse('hosting:invoices')
- if request.user.is_authenticated()
- else reverse('datacenterlight:index')
- ),
- 'msg_title': str(_('Thank you for the payment.')),
- 'msg_body': str(
- _('You will soon receive a confirmation email of the '
- 'payment. You can always contact us at '
- 'info@ungleich.ch for any question that you may have.')
- )
- }
- clear_all_session_vars(request)
-
- return JsonResponse(response)
-
- user = {
- 'name': custom_user.name,
- 'email': custom_user.email,
- 'username': custom_user.username,
- 'pass': custom_user.password,
- 'request_scheme': request.scheme,
- 'request_host': request.get_host(),
- 'language': get_language(),
- }
-
- create_vm(
- billing_address_data, stripe_customer_id, specs,
- stripe_subscription_obj, card_details_dict, request,
- vm_template_id, template, user
+ do_create_vm(self.request, user, stripe_api_cus_id,
+ card_details_response, stripe_subscription_obj,
+ stripe_onetime_charge, gp_details, specs, vm_template_id,
+ template
)
response = {
@@ -1179,6 +986,213 @@ class OrderConfirmationView(DetailView, FormView):
return JsonResponse(response)
+def 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):
+ # Create user if the user is not logged in and if he is not already
+ # registered
+ if not request.user.is_authenticated():
+ try:
+ custom_user = CustomUser.objects.get(
+ email=user.get('email'))
+ stripe_customer = StripeCustomer.objects.filter(
+ user_id=custom_user.id).first()
+ if stripe_customer is None:
+ stripe_customer = StripeCustomer.objects.create(
+ user=custom_user, stripe_id=stripe_api_cus_id
+ )
+ stripe_customer_id = stripe_customer.id
+ except CustomUser.DoesNotExist:
+ logger.debug(
+ "Customer {} does not exist.".format(user.get('email')))
+ password = CustomUser.get_random_password()
+ base_url = "{0}://{1}".format(request.scheme,
+ request.get_host())
+ custom_user = CustomUser.register(
+ user.get('name'), password,
+ user.get('email'),
+ app='dcl', base_url=base_url, send_email=True,
+ account_details=password
+ )
+ logger.debug("Created user {}.".format(user.get('email')))
+ stripe_customer = StripeCustomer.objects. \
+ create(user=custom_user, stripe_id=stripe_api_cus_id)
+ stripe_customer_id = stripe_customer.id
+ new_user = authenticate(username=custom_user.email,
+ password=password)
+ login(request, new_user)
+ if 'new_user_hosting_key_id' in request.session:
+ user_hosting_key = UserHostingKey.objects.get(
+ id=request.session['new_user_hosting_key_id'])
+ user_hosting_key.user = new_user
+ user_hosting_key.save()
+ else:
+ # We assume that if the user is here, his/her StripeCustomer
+ # object already exists
+ stripe_customer_id = request.user.stripecustomer.id
+ custom_user = request.user
+
+ if 'token' in request.session:
+ ucd = UserCardDetail.get_or_create_user_card_detail(
+ stripe_customer=request.user.stripecustomer,
+ card_details=card_details_response
+ )
+ UserCardDetail.save_default_card_local(
+ request.user.stripecustomer.stripe_id,
+ ucd.card_id
+ )
+ else:
+ card_id = request.session.get('card_id')
+ user_card_detail = UserCardDetail.objects.get(id=card_id)
+ card_details_dict = {
+ 'last4': user_card_detail.last4,
+ 'brand': user_card_detail.brand,
+ 'card_id': user_card_detail.card_id
+ }
+ if not user_card_detail.preferred:
+ UserCardDetail.set_default_card(
+ stripe_api_cus_id=stripe_api_cus_id,
+ stripe_source_id=user_card_detail.card_id
+ )
+
+ # Save billing address
+ billing_address_data = request.session.get('billing_address_data')
+ logger.debug('billing_address_data is {}'.format(billing_address_data))
+ billing_address_data.update({
+ 'user': custom_user.id
+ })
+
+ if 'generic_payment_type' in request.session:
+ stripe_cus = StripeCustomer.objects.filter(
+ stripe_id=stripe_api_cus_id
+ ).first()
+ billing_address = BillingAddress(
+ cardholder_name=billing_address_data['cardholder_name'],
+ street_address=billing_address_data['street_address'],
+ city=billing_address_data['city'],
+ postal_code=billing_address_data['postal_code'],
+ country=billing_address_data['country'],
+ vat_number=billing_address_data['vat_number']
+ )
+ billing_address.save()
+
+ order = HostingOrder.create(
+ price=request.session['generic_payment_details']['amount'],
+ customer=stripe_cus,
+ billing_address=billing_address,
+ vm_pricing=VMPricing.get_default_pricing()
+ )
+
+ # Create a Hosting Bill
+ HostingBill.create(customer=stripe_cus,
+ billing_address=billing_address)
+
+ # Create Billing Address for User if he does not have one
+ if not stripe_cus.user.billing_addresses.count():
+ billing_address_data.update({
+ 'user': stripe_cus.user.id
+ })
+ billing_address_user_form = UserBillingAddressForm(
+ billing_address_data
+ )
+ billing_address_user_form.is_valid()
+ billing_address_user_form.save()
+
+ if request.session['generic_payment_details']['recurring']:
+ # Associate the given stripe subscription with the order
+ order.set_subscription_id(
+ stripe_subscription_obj.id, card_details_dict
+ )
+ else:
+ # Associate the given stripe charge id with the order
+ order.set_stripe_charge(stripe_onetime_charge)
+
+ # Set order status approved
+ order.set_approved()
+ order.generic_payment_description = gp_details["description"]
+ order.generic_product_id = gp_details["product_id"]
+ order.save()
+ # send emails
+ context = {
+ 'name': user.get('name'),
+ 'email': user.get('email'),
+ 'amount': gp_details['amount'],
+ 'description': gp_details['description'],
+ 'recurring': gp_details['recurring'],
+ 'product_name': gp_details['product_name'],
+ 'product_id': gp_details['product_id'],
+ 'order_id': order.id
+ }
+
+ email_data = {
+ 'subject': (settings.DCL_TEXT +
+ " Payment received from %s" % context['email']),
+ 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
+ 'to': ['info@ungleich.ch'],
+ 'body': "\n".join(
+ ["%s=%s" % (k, v) for (k, v) in context.items()]),
+ 'reply_to': [context['email']],
+ }
+ send_plain_email_task.delay(email_data)
+ recurring_text = _(" This is a monthly recurring plan.")
+ if gp_details['recurring_interval'] == "year":
+ recurring_text = _(" This is an yearly recurring plan.")
+
+ email_data = {
+ 'subject': _("Confirmation of your payment"),
+ 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
+ 'to': [user.get('email')],
+ 'body': _("Hi {name},\n\n"
+ "thank you for your order!\n"
+ "We have just received a payment of CHF {amount:.2f}"
+ " from you.{recurring}\n\n"
+ "Cheers,\nYour Data Center Light team".format(
+ name=user.get('name'),
+ amount=gp_details['amount'],
+ recurring=(
+ recurring_text
+ if gp_details['recurring'] else ''
+ )
+ )
+ ),
+ 'reply_to': ['info@ungleich.ch'],
+ }
+ send_plain_email_task.delay(email_data)
+
+ response = {
+ 'status': True,
+ 'redirect': (
+ reverse('hosting:invoices')
+ if request.user.is_authenticated()
+ else reverse('datacenterlight:index')
+ ),
+ 'msg_title': str(_('Thank you for the payment.')),
+ 'msg_body': str(
+ _('You will soon receive a confirmation email of the '
+ 'payment. You can always contact us at '
+ 'info@ungleich.ch for any question that you may have.')
+ )
+ }
+ clear_all_session_vars(request)
+
+ return JsonResponse(response)
+
+ user = {
+ 'name': custom_user.name,
+ 'email': custom_user.email,
+ 'username': custom_user.username,
+ 'pass': custom_user.password,
+ 'request_scheme': request.scheme,
+ 'request_host': request.get_host(),
+ 'language': get_language(),
+ }
+
+ create_vm(
+ billing_address_data, stripe_customer_id, specs,
+ stripe_subscription_obj, card_details_dict, request,
+ vm_template_id, template, user
+ )
+
def show_error(msg, request):
messages.add_message(request, messages.ERROR, msg,
extra_tags='failed_payment')
diff --git a/webhook/views.py b/webhook/views.py
index ad598805..0b05b6ac 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -8,7 +8,9 @@ from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
+from datacenterlight.views import do_create_vm
from membership.models import StripeCustomer
+from hosting.models import HostingOrder
from utils.models import BillingAddress, UserBillingAddress
from utils.tasks import send_plain_email_task
@@ -117,8 +119,35 @@ def handle_webhook(request):
invoice_obj = event.data.object
logger.debug("Webhook Event: invoice.paid")
logger.debug("invoice_obj %s " % str(invoice_obj))
- if invoice_obj.paid and invoice_obj.billing_reason == "subscription_create":
+ 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)))
+ # We should check for billing_reason == "subscription_create" but we 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
+ # value of subscription_create, indicating that it is the first
+ # invoice of a subscription. For older API versions,
+ # billing_reason=subscription_create is represented as
+ # subscription_update.
+
+ if invoice_obj.paid and invoice_obj.billing_reason == "subscription_update":
logger.debug("Start provisioning")
+ # get subscription id, order_id
+ ho = None
+ try:
+ ho = HostingOrder.objects.get(subscription_id=invoice_obj.subscription)
+ except Exception as ex:
+ logger.error(str(ex))
+ if ho:
+ logger.debug("Create a VM for order %s" % str(ho))
+
+ # TODO: fix the error below
+ 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
+ )
else:
logger.error("Unhandled event : " + event.type)
From cda241893b865b28b4ebd3450db3b874392ae0ef Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 07:57:54 +0530
Subject: [PATCH 102/217] Fix PEP warning
---
datacenterlight/views.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index e04f5234..7ce6a90e 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1193,6 +1193,7 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
vm_template_id, template, user
)
+
def show_error(msg, request):
messages.add_message(request, messages.ERROR, msg,
extra_tags='failed_payment')
From 3e95a389bb6bf9d843cd80c5d8b2dbb9e0e24860 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 07:58:20 +0530
Subject: [PATCH 103/217] Preparing a fix for TODO (wip)
---
webhook/views.py | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/webhook/views.py b/webhook/views.py
index 0b05b6ac..04d19b63 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -140,14 +140,21 @@ def handle_webhook(request):
logger.error(str(ex))
if ho:
logger.debug("Create a VM for order %s" % str(ho))
-
- # TODO: fix the error below
- 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
- )
+ # TODO: fix the error below
+ try:
+ user = {'name': ho.customer.user.name,
+ 'email': ho.customer.user.email}
+ stripe_api_cus_id = ho.customer.stripe_id
+ 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:
logger.error("Unhandled event : " + event.type)
From ca7481cce06fa47e820a482d6b671b4ecebe063c Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 08:00:08 +0530
Subject: [PATCH 104/217] Avoid request.user.is_authenticated()
---
datacenterlight/views.py | 72 ++++++++++++++++++----------------------
1 file changed, 33 insertions(+), 39 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 7ce6a90e..c57890ad 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -991,46 +991,40 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
specs, vm_template_id, template):
# Create user if the user is not logged in and if he is not already
# registered
- if not request.user.is_authenticated():
- try:
- custom_user = CustomUser.objects.get(
- email=user.get('email'))
- stripe_customer = StripeCustomer.objects.filter(
- user_id=custom_user.id).first()
- if stripe_customer is None:
- stripe_customer = StripeCustomer.objects.create(
- user=custom_user, stripe_id=stripe_api_cus_id
- )
- stripe_customer_id = stripe_customer.id
- except CustomUser.DoesNotExist:
- logger.debug(
- "Customer {} does not exist.".format(user.get('email')))
- password = CustomUser.get_random_password()
- base_url = "{0}://{1}".format(request.scheme,
- request.get_host())
- custom_user = CustomUser.register(
- user.get('name'), password,
- user.get('email'),
- app='dcl', base_url=base_url, send_email=True,
- account_details=password
+ try:
+ custom_user = CustomUser.objects.get(
+ email=user.get('email'))
+ stripe_customer = StripeCustomer.objects.filter(
+ user_id=custom_user.id).first()
+ if stripe_customer is None:
+ stripe_customer = StripeCustomer.objects.create(
+ user=custom_user, stripe_id=stripe_api_cus_id
)
- logger.debug("Created user {}.".format(user.get('email')))
- stripe_customer = StripeCustomer.objects. \
- create(user=custom_user, stripe_id=stripe_api_cus_id)
- stripe_customer_id = stripe_customer.id
- new_user = authenticate(username=custom_user.email,
- password=password)
- login(request, new_user)
- if 'new_user_hosting_key_id' in request.session:
- user_hosting_key = UserHostingKey.objects.get(
- id=request.session['new_user_hosting_key_id'])
- user_hosting_key.user = new_user
- user_hosting_key.save()
- else:
- # We assume that if the user is here, his/her StripeCustomer
- # object already exists
- stripe_customer_id = request.user.stripecustomer.id
- custom_user = request.user
+ stripe_customer_id = stripe_customer.id
+ except CustomUser.DoesNotExist:
+ logger.debug(
+ "Customer {} does not exist.".format(user.get('email')))
+ password = CustomUser.get_random_password()
+ base_url = "{0}://{1}".format(request.scheme,
+ request.get_host())
+ custom_user = CustomUser.register(
+ user.get('name'), password,
+ user.get('email'),
+ app='dcl', base_url=base_url, send_email=True,
+ account_details=password
+ )
+ logger.debug("Created user {}.".format(user.get('email')))
+ stripe_customer = StripeCustomer.objects. \
+ create(user=custom_user, stripe_id=stripe_api_cus_id)
+ stripe_customer_id = stripe_customer.id
+ new_user = authenticate(username=custom_user.email,
+ password=password)
+ login(request, new_user)
+ if 'new_user_hosting_key_id' in request.session:
+ user_hosting_key = UserHostingKey.objects.get(
+ id=request.session['new_user_hosting_key_id'])
+ user_hosting_key.user = new_user
+ user_hosting_key.save()
if 'token' in request.session:
ucd = UserCardDetail.get_or_create_user_card_detail(
From 9e247cc556ec7ab6caf960dca3ab15fa5cac2040 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 09:08:08 +0530
Subject: [PATCH 105/217] Fix PEP warning
---
datacenterlight/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index c57890ad..3cdc49c3 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -710,7 +710,7 @@ class OrderConfirmationView(DetailView, FormView):
stripe_api_cus_id = request.session.get('customer')
stripe_utils = StripeUtils()
logger.debug("user=%s stripe_api_cus_id=%s" % (user, stripe_api_cus_id))
- card_details_response = None
+ card_details_response = None
if 'token' in request.session:
card_details = stripe_utils.get_cards_details_from_token(
From 9f49c664fa63504755174bcd13ae821ffd5e0d5e Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 09:09:40 +0530
Subject: [PATCH 106/217] Make do_create_vm independent of session
---
datacenterlight/views.py | 45 ++++++++++++++++++++--------------------
1 file changed, 22 insertions(+), 23 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 3cdc49c3..db192623 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -966,7 +966,7 @@ class OrderConfirmationView(DetailView, FormView):
do_create_vm(self.request, user, stripe_api_cus_id,
card_details_response, stripe_subscription_obj,
stripe_onetime_charge, gp_details, specs, vm_template_id,
- template
+ template, request.session.get('billing_address_data')
)
response = {
@@ -1005,8 +1005,8 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
logger.debug(
"Customer {} does not exist.".format(user.get('email')))
password = CustomUser.get_random_password()
- base_url = "{0}://{1}".format(request.scheme,
- request.get_host())
+ base_url = "{0}://{1}".format(request['scheme'],
+ request['host'])
custom_user = CustomUser.register(
user.get('name'), password,
user.get('email'),
@@ -1020,23 +1020,14 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
new_user = authenticate(username=custom_user.email,
password=password)
login(request, new_user)
- if 'new_user_hosting_key_id' in request.session:
+ if 'new_user_hosting_key_id' in request:
user_hosting_key = UserHostingKey.objects.get(
- id=request.session['new_user_hosting_key_id'])
+ id=request['new_user_hosting_key_id'])
user_hosting_key.user = new_user
user_hosting_key.save()
- if 'token' in request.session:
- ucd = UserCardDetail.get_or_create_user_card_detail(
- stripe_customer=request.user.stripecustomer,
- card_details=card_details_response
- )
- UserCardDetail.save_default_card_local(
- request.user.stripecustomer.stripe_id,
- ucd.card_id
- )
- else:
- card_id = request.session.get('card_id')
+ if 'card_id' in request.get('card'):
+ card_id = request.get('card')['card_id']
user_card_detail = UserCardDetail.objects.get(id=card_id)
card_details_dict = {
'last4': user_card_detail.last4,
@@ -1048,15 +1039,23 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
stripe_api_cus_id=stripe_api_cus_id,
stripe_source_id=user_card_detail.card_id
)
+ else:
+ ucd = UserCardDetail.get_or_create_user_card_detail(
+ stripe_customer=custom_user.stripecustomer,
+ card_details=card_details_response
+ )
+ UserCardDetail.save_default_card_local(
+ custom_user.stripecustomer.stripe_id,
+ ucd.card_id
+ )
# Save billing address
- billing_address_data = request.session.get('billing_address_data')
logger.debug('billing_address_data is {}'.format(billing_address_data))
billing_address_data.update({
'user': custom_user.id
})
- if 'generic_payment_type' in request.session:
+ if 'generic_payment_type' in request:
stripe_cus = StripeCustomer.objects.filter(
stripe_id=stripe_api_cus_id
).first()
@@ -1071,7 +1070,7 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
billing_address.save()
order = HostingOrder.create(
- price=request.session['generic_payment_details']['amount'],
+ price=request['generic_payment_details']['amount'],
customer=stripe_cus,
billing_address=billing_address,
vm_pricing=VMPricing.get_default_pricing()
@@ -1092,7 +1091,7 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
billing_address_user_form.is_valid()
billing_address_user_form.save()
- if request.session['generic_payment_details']['recurring']:
+ if request['generic_payment_details']['recurring']:
# Associate the given stripe subscription with the order
order.set_subscription_id(
stripe_subscription_obj.id, card_details_dict
@@ -1176,9 +1175,9 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
'email': custom_user.email,
'username': custom_user.username,
'pass': custom_user.password,
- 'request_scheme': request.scheme,
- 'request_host': request.get_host(),
- 'language': get_language(),
+ 'request_scheme': request['scheme'],
+ 'request_host': request['host'],
+ 'language': request['language'],
}
create_vm(
From 2a84d20f35b14d74769f308e83965027b8310dd7 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 09:09:53 +0530
Subject: [PATCH 107/217] Add docstring
---
datacenterlight/views.py | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index db192623..f26f1788 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -988,7 +988,38 @@ class OrderConfirmationView(DetailView, FormView):
def 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):
+ specs, vm_template_id, template, billing_address_data):
+ """
+ :param request: a dict
+ {
+ 'scheme': 'https',
+ 'host': 'domain',
+ 'language': 'en-us',
+ 'new_user_hosting_key_id': 1,
+ 'card': {
+ 'card_id': 1, # if usercarddetail exists already, else
+ },
+ 'generic_payment_type': 'generic' # represents a generic payment
+ 'generic_payment_details': {
+ 'amount': 100,
+ 'recurring':
+ }
+ }
+ :param user: a dict
+ {
+ 'name': 'John Doe',
+ 'email': 'john@doe.com'
+ }
+ :param stripe_api_cus_id: 'cus_xxxxxxx' the actual stripe customer id str
+ :param card_details_response:
+ :param stripe_subscription_obj: The actual Stripe's Subscription Object
+ :param stripe_onetime_charge: Stripe's Charge object
+ :param gp_details:
+ :param specs:
+ :param vm_template_id:
+ :param template:
+ :return:
+ """
# Create user if the user is not logged in and if he is not already
# registered
try:
From 20c6703236ad06bc6d469b5c6dc63c18c51d1866 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 09:10:03 +0530
Subject: [PATCH 108/217] Add a todo
---
datacenterlight/views.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index f26f1788..35f4c856 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1050,6 +1050,7 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
stripe_customer_id = stripe_customer.id
new_user = authenticate(username=custom_user.email,
password=password)
+ # TODO do we need login here ?
login(request, new_user)
if 'new_user_hosting_key_id' in request:
user_hosting_key = UserHostingKey.objects.get(
From c4c918d591eac20493889009a06042c8eb43933a Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 09:26:01 +0530
Subject: [PATCH 109/217] Prepare params from session to pass to do_create_vm
---
datacenterlight/views.py | 31 +++++++++++++++++++++++++------
1 file changed, 25 insertions(+), 6 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 35f4c856..00f8a555 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -962,8 +962,29 @@ class OrderConfirmationView(DetailView, FormView):
"requires_source_action")
msg = subscription_result.get('error')
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(self.request, user, stripe_api_cus_id,
+ do_create_vm(req, user, stripe_api_cus_id,
card_details_response, stripe_subscription_obj,
stripe_onetime_charge, gp_details, specs, vm_template_id,
template, request.session.get('billing_address_data')
@@ -996,9 +1017,7 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
'host': 'domain',
'language': 'en-us',
'new_user_hosting_key_id': 1,
- 'card': {
- 'card_id': 1, # if usercarddetail exists already, else
- },
+ 'card_id': 1, # if usercarddetail exists already,
'generic_payment_type': 'generic' # represents a generic payment
'generic_payment_details': {
'amount': 100,
@@ -1058,8 +1077,8 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
user_hosting_key.user = new_user
user_hosting_key.save()
- if 'card_id' in request.get('card'):
- card_id = request.get('card')['card_id']
+ if 'card_id' in request:
+ card_id = request.get('card_id')
user_card_detail = UserCardDetail.objects.get(id=card_id)
card_details_dict = {
'last4': user_card_detail.last4,
From 17c8f9ca18f813652433cf4eaab9f354da4118f0 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 10:59:21 +0530
Subject: [PATCH 110/217] Handle IncompleteSubscriptions in webhook
---
datacenterlight/views.py | 92 ++++++++++++++++++++++++++--------------
hosting/models.py | 19 +++++++++
webhook/views.py | 79 ++++++++++++++++++++++------------
3 files changed, 130 insertions(+), 60 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 00f8a555..3eb77412 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1,6 +1,7 @@
import logging
-
+import json
import stripe
+
from django import forms
from django.conf import settings
from django.contrib import messages
@@ -19,9 +20,8 @@ from hosting.forms import (
)
from hosting.models import (
HostingBill, HostingOrder, UserCardDetail, GenericProduct, UserHostingKey,
- StripeTaxRate)
+ StripeTaxRate, IncompleteSubscriptions)
from membership.models import CustomUser, StripeCustomer
-from opennebula_api.models import OpenNebulaManager
from opennebula_api.serializers import VMTemplateSerializer
from utils.forms import (
BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm,
@@ -894,6 +894,53 @@ class OrderConfirmationView(DetailView, FormView):
latest_invoice = stripe.Invoice.retrieve(
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
if (stripe_subscription_obj is None
or (stripe_subscription_obj.status != 'active'
@@ -953,6 +1000,7 @@ class OrderConfirmationView(DetailView, FormView):
)
}
}
+ clear_all_session_vars(request)
return JsonResponse(context)
else:
logger.debug(
@@ -962,28 +1010,6 @@ class OrderConfirmationView(DetailView, FormView):
"requires_source_action")
msg = subscription_result.get('error')
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,
card_details_response, stripe_subscription_obj,
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}"
" from you.{recurring}\n\n"
"Cheers,\nYour Data Center Light team".format(
- name=user.get('name'),
- amount=gp_details['amount'],
- recurring=(
- recurring_text
- if gp_details['recurring'] else ''
- )
- )
- ),
+ name=user.get('name'),
+ amount=gp_details['amount'],
+ recurring=(
+ recurring_text
+ if gp_details['recurring'] else ''
+ )
+ )
+ ),
'reply_to': ['info@ungleich.ch'],
}
send_plain_email_task.delay(email_data)
diff --git a/hosting/models.py b/hosting/models.py
index 1061cf54..0554f2e9 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -741,3 +741,22 @@ class StripeTaxRate(AssignPermissionsMixin, models.Model):
display_name = models.CharField(max_length=100)
percentage = models.FloatField(default=0)
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()
\ No newline at end of file
diff --git a/webhook/views.py b/webhook/views.py
index 04d19b63..5f8c85b0 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -1,7 +1,8 @@
import datetime
import logging
-
+import json
import stripe
+
# Create your views here.
from django.conf import settings
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 membership.models import StripeCustomer
-from hosting.models import HostingOrder
+from hosting.models import IncompleteSubscriptions
from utils.models import BillingAddress, UserBillingAddress
from utils.tasks import send_plain_email_task
@@ -115,14 +116,16 @@ def handle_webhook(request):
}
send_plain_email_task.delay(email_data)
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
logger.debug("Webhook Event: invoice.paid")
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.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"
- # because we are using older api. See https://stripe.com/docs/upgrades?since=2015-07-13
+ # We should check for billing_reason == "subscription_create" but we
+ # 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
# 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
# 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")
- # get subscription id, order_id
- ho = None
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:
logger.error(str(ex))
- if ho:
- logger.debug("Create a VM for order %s" % str(ho))
- # TODO: fix the error below
- try:
- user = {'name': ho.customer.user.name,
- 'email': ho.customer.user.email}
- stripe_api_cus_id = ho.customer.stripe_id
- 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))
-
+ 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:
logger.error("Unhandled event : " + event.type)
return HttpResponse(status=200)
From 1ed42e608c2472316d123f741a033553159ec126 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 11:07:20 +0530
Subject: [PATCH 111/217] Add incompletesubscriptions migration file
---
.../0062_incompletesubscriptions.py | 39 +++++++++++++++++++
1 file changed, 39 insertions(+)
create mode 100644 hosting/migrations/0062_incompletesubscriptions.py
diff --git a/hosting/migrations/0062_incompletesubscriptions.py b/hosting/migrations/0062_incompletesubscriptions.py
new file mode 100644
index 00000000..0405e086
--- /dev/null
+++ b/hosting/migrations/0062_incompletesubscriptions.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2020-12-23 05:36
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import utils.mixins
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('hosting', '0061_genericproduct_exclude_vat_calculations'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='IncompleteSubscriptions',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('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(max_length=254)),
+ ('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()),
+ ],
+ bases=(utils.mixins.AssignPermissionsMixin, models.Model),
+ ),
+ ]
From 50d9eb1c50e6147e364adbe9e15b42e128f3ad0e Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 11:26:52 +0530
Subject: [PATCH 112/217] Fix UnboundLocalError: local variable
'stripe_onetime_charge' referenced before assignment
---
datacenterlight/views.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 3eb77412..a8760290 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -706,6 +706,12 @@ class OrderConfirmationView(DetailView, FormView):
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
+ stripe_onetime_charge = None
+ stripe_customer_obj = None
+ gp_details = None
+ specs = None
+ vm_template_id = 0
+ template = None
user = request.session.get('user')
stripe_api_cus_id = request.session.get('customer')
stripe_utils = StripeUtils()
From 95a1b8fa20759a32b4a08adfd40c63e71f6c1479 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 11:52:41 +0530
Subject: [PATCH 113/217] Make complete_at allow null
---
datacenterlight/views.py | 2 +-
hosting/migrations/0063_auto_20201223_0612.py | 20 +++++++++++++++++++
hosting/models.py | 2 +-
3 files changed, 22 insertions(+), 2 deletions(-)
create mode 100644 hosting/migrations/0063_auto_20201223_0612.py
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index a8760290..f8b9a097 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1006,7 +1006,7 @@ class OrderConfirmationView(DetailView, FormView):
)
}
}
- clear_all_session_vars(request)
+ #clear_all_session_vars(request)
return JsonResponse(context)
else:
logger.debug(
diff --git a/hosting/migrations/0063_auto_20201223_0612.py b/hosting/migrations/0063_auto_20201223_0612.py
new file mode 100644
index 00000000..eb4ca9d4
--- /dev/null
+++ b/hosting/migrations/0063_auto_20201223_0612.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2020-12-23 06:12
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('hosting', '0062_incompletesubscriptions'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='incompletesubscriptions',
+ name='completed_at',
+ field=models.DateTimeField(null=True),
+ ),
+ ]
diff --git a/hosting/models.py b/hosting/models.py
index 0554f2e9..ac44cc9e 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -745,7 +745,7 @@ class StripeTaxRate(AssignPermissionsMixin, models.Model):
class IncompleteSubscriptions(AssignPermissionsMixin, models.Model):
created_at = models.DateTimeField(auto_now_add=True)
- completed_at = models.DateTimeField()
+ completed_at = models.DateTimeField(null=True)
subscription_id = models.CharField(max_length=100)
subscription_status = models.CharField(max_length=30)
name = models.CharField(max_length=50)
From b1dd9988ce65d13c6b352c3bb12eb56807d2a7e5 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 12:03:17 +0530
Subject: [PATCH 114/217] Add missing subscription_id param
---
datacenterlight/views.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index f8b9a097..348ad860 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -930,6 +930,7 @@ class OrderConfirmationView(DetailView, FormView):
# Store params so that they can be retrieved later
IncompleteSubscriptions.objects.create(
+ subscription_id=stripe_subscription_obj.id,
subscription_status=subscription_status,
name=user.get('name'),
email=user.get('email'),
From 92bafed3b327cd2d18573994a7442cc28d7fbcbd Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 12:09:19 +0530
Subject: [PATCH 115/217] Fix getting stripe_subscription_obj
---
webhook/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/webhook/views.py b/webhook/views.py
index 5f8c85b0..203716e4 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -153,7 +153,7 @@ def handle_webhook(request):
card_details_response=json.loads(
incomplete_sub.card_details_response),
stripe_subscription_obj=json.loads(
- stripe_subscription_obj),
+ incomplete_sub.stripe_subscription_obj),
stripe_onetime_charge=json.loads(
incomplete_sub.stripe_onetime_charge),
gp_details=json.loads(incomplete_sub.gp_details),
From c70753767fb577ac05fc7fa9971be8ba2bcb0c61 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 12:32:42 +0530
Subject: [PATCH 116/217] Load request object correctly and pass correct
subscription object
---
webhook/views.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/webhook/views.py b/webhook/views.py
index 203716e4..9381239a 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -146,14 +146,13 @@ def handle_webhook(request):
logger.debug(incomplete_sub)
logger.debug("*******")
do_create_vm(
- request=incomplete_sub.request,
+ request=json.loads(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(
- incomplete_sub.stripe_subscription_obj),
+ stripe_subscription_obj=stripe_subscription_obj,
stripe_onetime_charge=json.loads(
incomplete_sub.stripe_onetime_charge),
gp_details=json.loads(incomplete_sub.gp_details),
From a5f49cf8bebabda04ba300ad72db17d406e4a29b Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 12:41:46 +0530
Subject: [PATCH 117/217] Add debug messages
---
webhook/views.py | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/webhook/views.py b/webhook/views.py
index 9381239a..25325984 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -143,8 +143,19 @@ def handle_webhook(request):
incomplete_sub = IncompleteSubscriptions.objects.get(
subscription_id=invoice_obj.subscription)
logger.debug("*******")
- logger.debug(incomplete_sub)
+ logger.debug(str(incomplete_sub))
logger.debug("*******")
+ logger.debug("1*******")
+ logger.debug(json.loads(incomplete_sub.request))
+ logger.debug("2*******")
+ logger.debug(json.loads(incomplete_sub.card_details_response))
+ logger.debug("3*******")
+ logger.debug(json.loads(incomplete_sub.stripe_onetime_charge))
+ logger.debug("4*******")
+ logger.debug(json.loads(incomplete_sub.gp_details))
+ logger.debug("5*******")
+ logger.debug(json.loads(incomplete_sub.template))
+ logger.debug("6*******")
do_create_vm(
request=json.loads(incomplete_sub.request),
user={'name': incomplete_sub.name,
From de6bc06eaf47b8c1dcd3354e082b66bd0e975149 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 12:50:21 +0530
Subject: [PATCH 118/217] Fix json loads for none
---
webhook/views.py | 23 ++++++++++++++++-------
1 file changed, 16 insertions(+), 7 deletions(-)
diff --git a/webhook/views.py b/webhook/views.py
index 25325984..d788c71d 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -142,17 +142,27 @@ def handle_webhook(request):
try:
incomplete_sub = IncompleteSubscriptions.objects.get(
subscription_id=invoice_obj.subscription)
+ soc = ""
+ card_details_response = ""
+ gp_details = ""
+ if incomplete_sub.stripe_onetime_charge:
+ soc = json.loads(incomplete_sub.stripe_onetime_charge)
+ if incomplete_sub.gp_details:
+ gp_details = json.loads(incomplete_sub.gp_details)
+ if incomplete_sub.card_details_response:
+ card_details_response = json.loads(
+ incomplete_sub.card_details_response)
logger.debug("*******")
logger.debug(str(incomplete_sub))
logger.debug("*******")
logger.debug("1*******")
logger.debug(json.loads(incomplete_sub.request))
logger.debug("2*******")
- logger.debug(json.loads(incomplete_sub.card_details_response))
+ logger.debug(card_details_response)
logger.debug("3*******")
- logger.debug(json.loads(incomplete_sub.stripe_onetime_charge))
+ logger.debug(json.loads(soc))
logger.debug("4*******")
- logger.debug(json.loads(incomplete_sub.gp_details))
+ logger.debug(json.loads(gp_details))
logger.debug("5*******")
logger.debug(json.loads(incomplete_sub.template))
logger.debug("6*******")
@@ -162,11 +172,10 @@ def handle_webhook(request):
'email': incomplete_sub.email},
stripe_api_cus_id=incomplete_sub.stripe_api_cus_id,
card_details_response=json.loads(
- incomplete_sub.card_details_response),
+ card_details_response),
stripe_subscription_obj=stripe_subscription_obj,
- stripe_onetime_charge=json.loads(
- incomplete_sub.stripe_onetime_charge),
- gp_details=json.loads(incomplete_sub.gp_details),
+ stripe_onetime_charge=json.loads(soc),
+ gp_details=json.loads(gp_details),
specs=json.loads(incomplete_sub.specs),
vm_template_id=incomplete_sub.vm_template_id,
template=json.loads(incomplete_sub.template)
From 2baa77a7d4323f2038835323ab8dff3bcc135f33 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 13:00:05 +0530
Subject: [PATCH 119/217] Fix json loads issue
---
webhook/views.py | 31 ++++++++++++++++++++-----------
1 file changed, 20 insertions(+), 11 deletions(-)
diff --git a/webhook/views.py b/webhook/views.py
index d788c71d..ecd44306 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -142,9 +142,16 @@ def handle_webhook(request):
try:
incomplete_sub = IncompleteSubscriptions.objects.get(
subscription_id=invoice_obj.subscription)
+ request = ""
soc = ""
card_details_response = ""
gp_details = ""
+ template = ""
+ specs = ""
+ if incomplete_sub.request:
+ request = json.loads(incomplete_sub.request)
+ if incomplete_sub.specs:
+ specs = json.loads(incomplete_sub.specs)
if incomplete_sub.stripe_onetime_charge:
soc = json.loads(incomplete_sub.stripe_onetime_charge)
if incomplete_sub.gp_details:
@@ -152,33 +159,35 @@ def handle_webhook(request):
if incomplete_sub.card_details_response:
card_details_response = json.loads(
incomplete_sub.card_details_response)
+ if incomplete_sub.template:
+ template = json.loads(
+ incomplete_sub.template)
logger.debug("*******")
logger.debug(str(incomplete_sub))
logger.debug("*******")
logger.debug("1*******")
- logger.debug(json.loads(incomplete_sub.request))
+ logger.debug(request)
logger.debug("2*******")
logger.debug(card_details_response)
logger.debug("3*******")
- logger.debug(json.loads(soc))
+ logger.debug(soc)
logger.debug("4*******")
- logger.debug(json.loads(gp_details))
+ logger.debug(gp_details)
logger.debug("5*******")
- logger.debug(json.loads(incomplete_sub.template))
+ logger.debug(template)
logger.debug("6*******")
do_create_vm(
- request=json.loads(incomplete_sub.request),
+ request=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(
- card_details_response),
+ card_details_response=card_details_response,
stripe_subscription_obj=stripe_subscription_obj,
- stripe_onetime_charge=json.loads(soc),
- gp_details=json.loads(gp_details),
- specs=json.loads(incomplete_sub.specs),
+ stripe_onetime_charge=soc,
+ gp_details=gp_details,
+ specs=specs,
vm_template_id=incomplete_sub.vm_template_id,
- template=json.loads(incomplete_sub.template)
+ template=template
)
except (IncompleteSubscriptions.DoesNotExist,
IncompleteSubscriptions.MultipleObjectsReturned) as ex:
From f6f6482ce08b6c5d67603e0ac8f558786ca70bc5 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 13:07:28 +0530
Subject: [PATCH 120/217] Add missing billing_address_data
---
webhook/views.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/webhook/views.py b/webhook/views.py
index ecd44306..0b3c4e30 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -148,6 +148,7 @@ def handle_webhook(request):
gp_details = ""
template = ""
specs = ""
+ billing_address_data = ""
if incomplete_sub.request:
request = json.loads(incomplete_sub.request)
if incomplete_sub.specs:
@@ -162,6 +163,9 @@ def handle_webhook(request):
if incomplete_sub.template:
template = json.loads(
incomplete_sub.template)
+ if incomplete_sub.billing_address:
+ billing_address_data = json.loads(
+ incomplete_sub.billing_address_data)
logger.debug("*******")
logger.debug(str(incomplete_sub))
logger.debug("*******")
@@ -187,7 +191,8 @@ def handle_webhook(request):
gp_details=gp_details,
specs=specs,
vm_template_id=incomplete_sub.vm_template_id,
- template=template
+ template=template,
+ billing_address_data=billing_address_data
)
except (IncompleteSubscriptions.DoesNotExist,
IncompleteSubscriptions.MultipleObjectsReturned) as ex:
From 9d765fcb6e85a63279a524780ce6e5171a33306e Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 13:10:29 +0530
Subject: [PATCH 121/217] Fix wrong variable name billing_address
---
webhook/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/webhook/views.py b/webhook/views.py
index 0b3c4e30..20ee6c45 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -163,7 +163,7 @@ def handle_webhook(request):
if incomplete_sub.template:
template = json.loads(
incomplete_sub.template)
- if incomplete_sub.billing_address:
+ if incomplete_sub.billing_address_data:
billing_address_data = json.loads(
incomplete_sub.billing_address_data)
logger.debug("*******")
From 812157b6c648d9be41337930e878610dc0b2965a Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 13:40:19 +0530
Subject: [PATCH 122/217] Move login code out of the refactored do_create_vm
---
datacenterlight/views.py | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 348ad860..ee44416c 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1017,11 +1017,18 @@ class OrderConfirmationView(DetailView, FormView):
"requires_source_action")
msg = subscription_result.get('error')
return show_error(msg, self.request)
- do_create_vm(req, user, stripe_api_cus_id,
+ do_create_vm(
+ req, user, stripe_api_cus_id,
card_details_response, stripe_subscription_obj,
stripe_onetime_charge, gp_details, specs, vm_template_id,
template, request.session.get('billing_address_data')
)
+ try:
+ custom_user = CustomUser.objects.get(email=user.get('email'))
+ login(self.request, custom_user)
+ except (CustomUser.DoesNotExist,
+ CustomUser.MultipleObjectsReturned) as ex:
+ logger.error(str(ex))
response = {
'status': True,
@@ -1102,16 +1109,17 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
stripe_customer_id = stripe_customer.id
new_user = authenticate(username=custom_user.email,
password=password)
- # TODO do we need login here ?
- login(request, new_user)
+ logger.debug("User %s is authenticated" % custom_user.email)
if 'new_user_hosting_key_id' in request:
user_hosting_key = UserHostingKey.objects.get(
id=request['new_user_hosting_key_id'])
user_hosting_key.user = new_user
user_hosting_key.save()
+ logger.debug("User's key is saved" % custom_user.email)
if 'card_id' in request:
card_id = request.get('card_id')
+ logger.debug("card_id %s was in request" % card_id)
user_card_detail = UserCardDetail.objects.get(id=card_id)
card_details_dict = {
'last4': user_card_detail.last4,
@@ -1124,6 +1132,8 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
stripe_source_id=user_card_detail.card_id
)
else:
+ logger.debug("card_id %s was NOT in request, using "
+ "card_details_response")
ucd = UserCardDetail.get_or_create_user_card_detail(
stripe_customer=custom_user.stripecustomer,
card_details=card_details_response
From a4a5acd0e75a9a0bf81222e8daa6ba5239c847a0 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 13:46:34 +0530
Subject: [PATCH 123/217] Fix string formatting issues
---
datacenterlight/views.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index ee44416c..613fac26 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1115,7 +1115,7 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
id=request['new_user_hosting_key_id'])
user_hosting_key.user = new_user
user_hosting_key.save()
- logger.debug("User's key is saved" % custom_user.email)
+ logger.debug("User %s key is saved" % custom_user.email)
if 'card_id' in request:
card_id = request.get('card_id')
@@ -1132,7 +1132,7 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
stripe_source_id=user_card_detail.card_id
)
else:
- logger.debug("card_id %s was NOT in request, using "
+ logger.debug("card_id was NOT in request, using "
"card_details_response")
ucd = UserCardDetail.get_or_create_user_card_detail(
stripe_customer=custom_user.stripecustomer,
From 981e68aa4f94c372f979d00d4051b75a4058bf6c Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 13:56:08 +0530
Subject: [PATCH 124/217] Fix getting card_id and compare
---
datacenterlight/views.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 613fac26..d6fb5303 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1117,8 +1117,8 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
user_hosting_key.save()
logger.debug("User %s key is saved" % custom_user.email)
- if 'card_id' in request:
- card_id = request.get('card_id')
+ card_id = request.get('card_id', None)
+ if card_id:
logger.debug("card_id %s was in request" % card_id)
user_card_detail = UserCardDetail.objects.get(id=card_id)
card_details_dict = {
From 259c5091130cb0fbfbedae050d92bcf86f2564be Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 14:02:27 +0530
Subject: [PATCH 125/217] Don't handle generic exception for the moment
---
webhook/views.py | 140 +++++++++++++++++++++++------------------------
1 file changed, 70 insertions(+), 70 deletions(-)
diff --git a/webhook/views.py b/webhook/views.py
index 20ee6c45..bd45ef16 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -136,84 +136,84 @@ def handle_webhook(request):
if (invoice_obj.paid and
invoice_obj.billing_reason == "subscription_update"):
logger.debug("Start provisioning")
+ # try:
+ stripe_subscription_obj = stripe.Subscription.retrieve(
+ invoice_obj.subscription)
try:
- stripe_subscription_obj = stripe.Subscription.retrieve(
- invoice_obj.subscription)
- try:
- incomplete_sub = IncompleteSubscriptions.objects.get(
- subscription_id=invoice_obj.subscription)
- request = ""
- soc = ""
- card_details_response = ""
- gp_details = ""
- template = ""
- specs = ""
- billing_address_data = ""
- if incomplete_sub.request:
- request = json.loads(incomplete_sub.request)
- if incomplete_sub.specs:
- specs = json.loads(incomplete_sub.specs)
- if incomplete_sub.stripe_onetime_charge:
- soc = json.loads(incomplete_sub.stripe_onetime_charge)
- if incomplete_sub.gp_details:
- gp_details = json.loads(incomplete_sub.gp_details)
- if incomplete_sub.card_details_response:
- card_details_response = json.loads(
- incomplete_sub.card_details_response)
- if incomplete_sub.template:
- template = json.loads(
- incomplete_sub.template)
- if incomplete_sub.billing_address_data:
- billing_address_data = json.loads(
- incomplete_sub.billing_address_data)
- logger.debug("*******")
- logger.debug(str(incomplete_sub))
- logger.debug("*******")
- logger.debug("1*******")
- logger.debug(request)
- logger.debug("2*******")
- logger.debug(card_details_response)
- logger.debug("3*******")
- logger.debug(soc)
- logger.debug("4*******")
- logger.debug(gp_details)
- logger.debug("5*******")
- logger.debug(template)
- logger.debug("6*******")
- do_create_vm(
- request=request,
- user={'name': incomplete_sub.name,
- 'email': incomplete_sub.email},
- stripe_api_cus_id=incomplete_sub.stripe_api_cus_id,
- card_details_response=card_details_response,
- stripe_subscription_obj=stripe_subscription_obj,
- stripe_onetime_charge=soc,
- gp_details=gp_details,
- specs=specs,
- vm_template_id=incomplete_sub.vm_template_id,
- template=template,
- billing_address_data=billing_address_data
- )
- 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:
+ incomplete_sub = IncompleteSubscriptions.objects.get(
+ subscription_id=invoice_obj.subscription)
+ request = ""
+ soc = ""
+ card_details_response = ""
+ gp_details = ""
+ template = ""
+ specs = ""
+ billing_address_data = ""
+ if incomplete_sub.request:
+ request = json.loads(incomplete_sub.request)
+ if incomplete_sub.specs:
+ specs = json.loads(incomplete_sub.specs)
+ if incomplete_sub.stripe_onetime_charge:
+ soc = json.loads(incomplete_sub.stripe_onetime_charge)
+ if incomplete_sub.gp_details:
+ gp_details = json.loads(incomplete_sub.gp_details)
+ if incomplete_sub.card_details_response:
+ card_details_response = json.loads(
+ incomplete_sub.card_details_response)
+ if incomplete_sub.template:
+ template = json.loads(
+ incomplete_sub.template)
+ if incomplete_sub.billing_address_data:
+ billing_address_data = json.loads(
+ incomplete_sub.billing_address_data)
+ logger.debug("*******")
+ logger.debug(str(incomplete_sub))
+ logger.debug("*******")
+ logger.debug("1*******")
+ logger.debug(request)
+ logger.debug("2*******")
+ logger.debug(card_details_response)
+ logger.debug("3*******")
+ logger.debug(soc)
+ logger.debug("4*******")
+ logger.debug(gp_details)
+ logger.debug("5*******")
+ logger.debug(template)
+ logger.debug("6*******")
+ do_create_vm(
+ request=request,
+ user={'name': incomplete_sub.name,
+ 'email': incomplete_sub.email},
+ stripe_api_cus_id=incomplete_sub.stripe_api_cus_id,
+ card_details_response=card_details_response,
+ stripe_subscription_obj=stripe_subscription_obj,
+ stripe_onetime_charge=soc,
+ gp_details=gp_details,
+ specs=specs,
+ vm_template_id=incomplete_sub.vm_template_id,
+ template=template,
+ billing_address_data=billing_address_data
+ )
+ except (IncompleteSubscriptions.DoesNotExist,
+ IncompleteSubscriptions.MultipleObjectsReturned) as ex:
logger.error(str(ex))
+ # TODO Inform admin
email_data = {
- 'subject': "invoice.paid Webhook error",
+ '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:
+ # 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:
logger.error("Unhandled event : " + event.type)
return HttpResponse(status=200)
From 70c8ed6825d70613b5c48e02e7de73842f64982e Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 14:12:30 +0530
Subject: [PATCH 126/217] Add debugging messages
---
datacenterlight/views.py | 11 ++-
webhook/views.py | 140 +++++++++++++++++++--------------------
2 files changed, 78 insertions(+), 73 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index d6fb5303..32650752 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1144,12 +1144,14 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
)
# Save billing address
- logger.debug('billing_address_data is {}'.format(billing_address_data))
billing_address_data.update({
'user': custom_user.id
})
+ logger.debug('billing_address_data is {}'.format(billing_address_data))
- if 'generic_payment_type' in request:
+ generic_payment_type = request.get('generic_payment_type', None)
+ if generic_payment_type:
+ logger.debug("generic_payment_type case")
stripe_cus = StripeCustomer.objects.filter(
stripe_id=stripe_api_cus_id
).first()
@@ -1185,12 +1187,15 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
billing_address_user_form.is_valid()
billing_address_user_form.save()
- if request['generic_payment_details']['recurring']:
+ recurring = request['generic_payment_details'].get('recurring')
+ if recurring:
+ logger.debug("recurring case")
# Associate the given stripe subscription with the order
order.set_subscription_id(
stripe_subscription_obj.id, card_details_dict
)
else:
+ logger.debug("one time charge case")
# Associate the given stripe charge id with the order
order.set_stripe_charge(stripe_onetime_charge)
diff --git a/webhook/views.py b/webhook/views.py
index bd45ef16..20ee6c45 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -136,84 +136,84 @@ def handle_webhook(request):
if (invoice_obj.paid and
invoice_obj.billing_reason == "subscription_update"):
logger.debug("Start provisioning")
- # try:
- stripe_subscription_obj = stripe.Subscription.retrieve(
- invoice_obj.subscription)
try:
- incomplete_sub = IncompleteSubscriptions.objects.get(
- subscription_id=invoice_obj.subscription)
- request = ""
- soc = ""
- card_details_response = ""
- gp_details = ""
- template = ""
- specs = ""
- billing_address_data = ""
- if incomplete_sub.request:
- request = json.loads(incomplete_sub.request)
- if incomplete_sub.specs:
- specs = json.loads(incomplete_sub.specs)
- if incomplete_sub.stripe_onetime_charge:
- soc = json.loads(incomplete_sub.stripe_onetime_charge)
- if incomplete_sub.gp_details:
- gp_details = json.loads(incomplete_sub.gp_details)
- if incomplete_sub.card_details_response:
- card_details_response = json.loads(
- incomplete_sub.card_details_response)
- if incomplete_sub.template:
- template = json.loads(
- incomplete_sub.template)
- if incomplete_sub.billing_address_data:
- billing_address_data = json.loads(
- incomplete_sub.billing_address_data)
- logger.debug("*******")
- logger.debug(str(incomplete_sub))
- logger.debug("*******")
- logger.debug("1*******")
- logger.debug(request)
- logger.debug("2*******")
- logger.debug(card_details_response)
- logger.debug("3*******")
- logger.debug(soc)
- logger.debug("4*******")
- logger.debug(gp_details)
- logger.debug("5*******")
- logger.debug(template)
- logger.debug("6*******")
- do_create_vm(
- request=request,
- user={'name': incomplete_sub.name,
- 'email': incomplete_sub.email},
- stripe_api_cus_id=incomplete_sub.stripe_api_cus_id,
- card_details_response=card_details_response,
- stripe_subscription_obj=stripe_subscription_obj,
- stripe_onetime_charge=soc,
- gp_details=gp_details,
- specs=specs,
- vm_template_id=incomplete_sub.vm_template_id,
- template=template,
- billing_address_data=billing_address_data
- )
- except (IncompleteSubscriptions.DoesNotExist,
- IncompleteSubscriptions.MultipleObjectsReturned) as ex:
+ stripe_subscription_obj = stripe.Subscription.retrieve(
+ invoice_obj.subscription)
+ try:
+ incomplete_sub = IncompleteSubscriptions.objects.get(
+ subscription_id=invoice_obj.subscription)
+ request = ""
+ soc = ""
+ card_details_response = ""
+ gp_details = ""
+ template = ""
+ specs = ""
+ billing_address_data = ""
+ if incomplete_sub.request:
+ request = json.loads(incomplete_sub.request)
+ if incomplete_sub.specs:
+ specs = json.loads(incomplete_sub.specs)
+ if incomplete_sub.stripe_onetime_charge:
+ soc = json.loads(incomplete_sub.stripe_onetime_charge)
+ if incomplete_sub.gp_details:
+ gp_details = json.loads(incomplete_sub.gp_details)
+ if incomplete_sub.card_details_response:
+ card_details_response = json.loads(
+ incomplete_sub.card_details_response)
+ if incomplete_sub.template:
+ template = json.loads(
+ incomplete_sub.template)
+ if incomplete_sub.billing_address_data:
+ billing_address_data = json.loads(
+ incomplete_sub.billing_address_data)
+ logger.debug("*******")
+ logger.debug(str(incomplete_sub))
+ logger.debug("*******")
+ logger.debug("1*******")
+ logger.debug(request)
+ logger.debug("2*******")
+ logger.debug(card_details_response)
+ logger.debug("3*******")
+ logger.debug(soc)
+ logger.debug("4*******")
+ logger.debug(gp_details)
+ logger.debug("5*******")
+ logger.debug(template)
+ logger.debug("6*******")
+ do_create_vm(
+ request=request,
+ user={'name': incomplete_sub.name,
+ 'email': incomplete_sub.email},
+ stripe_api_cus_id=incomplete_sub.stripe_api_cus_id,
+ card_details_response=card_details_response,
+ stripe_subscription_obj=stripe_subscription_obj,
+ stripe_onetime_charge=soc,
+ gp_details=gp_details,
+ specs=specs,
+ vm_template_id=incomplete_sub.vm_template_id,
+ template=template,
+ billing_address_data=billing_address_data
+ )
+ 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:
logger.error(str(ex))
- # TODO Inform admin
email_data = {
- 'subject': "IncompleteSubscriptions error",
+ '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)
- # except Exception as 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:
logger.error("Unhandled event : " + event.type)
return HttpResponse(status=200)
From 4962b72d1a2c8a5fd498650fd064d4247238651f Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 14:19:22 +0530
Subject: [PATCH 127/217] Add logging message
---
datacenterlight/utils.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py
index 97bfef4c..d0c4b0f3 100644
--- a/datacenterlight/utils.py
+++ b/datacenterlight/utils.py
@@ -38,6 +38,7 @@ def get_cms_integration(name):
def create_vm(billing_address_data, stripe_customer_id, specs,
stripe_subscription_obj, card_details_dict, request,
vm_template_id, template, user):
+ logger.debug("In create_vm")
billing_address = BillingAddress(
cardholder_name=billing_address_data['cardholder_name'],
street_address=billing_address_data['street_address'],
From 480e38fbc9b252856347302d91627d0bbea503e7 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 14:19:52 +0530
Subject: [PATCH 128/217] Attempt fix for local variable 'card_details_dict'
referenced before assignment
---
datacenterlight/views.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 32650752..f373d9b0 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1142,6 +1142,11 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
custom_user.stripecustomer.stripe_id,
ucd.card_id
)
+ card_details_dict = {
+ 'last4': ucd.last4,
+ 'brand': ucd.brand,
+ 'card_id': ucd.card_id
+ }
# Save billing address
billing_address_data.update({
From 0c1b7b1885caa8e47555f0e131b1aa156157e37f Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 14:33:32 +0530
Subject: [PATCH 129/217] Do clear session vars at the end
---
datacenterlight/utils.py | 2 --
datacenterlight/views.py | 50 ++++++++++++++++++++--------------------
2 files changed, 25 insertions(+), 27 deletions(-)
diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py
index d0c4b0f3..8a087e3e 100644
--- a/datacenterlight/utils.py
+++ b/datacenterlight/utils.py
@@ -103,8 +103,6 @@ def create_vm(billing_address_data, stripe_customer_id, specs,
create_vm_task.delay(vm_template_id, user, specs, template, order.id)
- clear_all_session_vars(request)
-
def clear_all_session_vars(request):
if request.session is not None:
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index f373d9b0..6ce1562b 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -717,6 +717,30 @@ class OrderConfirmationView(DetailView, FormView):
stripe_utils = StripeUtils()
logger.debug("user=%s stripe_api_cus_id=%s" % (user, stripe_api_cus_id))
card_details_response = None
+ new_user_hosting_key_id = None
+ card_id = None
+ generic_payment_type = None
+ generic_payment_details = None
+ stripe_subscription_obj = 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
+ }
if 'token' in request.session:
card_details = stripe_utils.get_cards_details_from_token(
@@ -899,31 +923,6 @@ class OrderConfirmationView(DetailView, FormView):
logger.debug(stripe_subscription_obj)
latest_invoice = stripe.Invoice.retrieve(
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
@@ -1043,6 +1042,7 @@ class OrderConfirmationView(DetailView, FormView):
' We will send you a confirmation email as soon as'
' it is ready.'))
}
+ clear_all_session_vars(request)
return JsonResponse(response)
From 41e993a3d9d18f3930abeb0d1386d2bd0d7dc39d Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 16:34:26 +0530
Subject: [PATCH 130/217] Log variable value
---
datacenterlight/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 6ce1562b..fac5f6fe 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -965,7 +965,7 @@ class OrderConfirmationView(DetailView, FormView):
# TODO: requires_attention is probably wrong value to compare
if (pi.status == 'requires_attention' or
pi.status == 'requires_source_action'):
- logger.debug("Display SCA authentication")
+ logger.debug("Display SCA authentication %s " % pi.status)
context = {
'sid': stripe_subscription_obj.id,
'payment_intent_secret': pi.client_secret,
From a99924b94cd904e688b140037fce58d36b4e86ee Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 17:09:27 +0530
Subject: [PATCH 131/217] Rename do_create_vm to do_provisioning; and pass
real_request
---
datacenterlight/views.py | 28 +++++++++++++++++-----------
webhook/views.py | 7 ++++---
2 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index fac5f6fe..9e4e7ed7 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1016,11 +1016,12 @@ class OrderConfirmationView(DetailView, FormView):
"requires_source_action")
msg = subscription_result.get('error')
return show_error(msg, self.request)
- do_create_vm(
+ do_provisioning(
req, user, stripe_api_cus_id,
card_details_response, stripe_subscription_obj,
stripe_onetime_charge, gp_details, specs, vm_template_id,
- template, request.session.get('billing_address_data')
+ template, request.session.get('billing_address_data'),
+ self.request
)
try:
custom_user = CustomUser.objects.get(email=user.get('email'))
@@ -1047,9 +1048,10 @@ class OrderConfirmationView(DetailView, FormView):
return JsonResponse(response)
-def 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, billing_address_data):
+def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
+ stripe_subscription_obj, stripe_onetime_charge, gp_details,
+ specs, vm_template_id, template, billing_address_data,
+ real_request):
"""
:param request: a dict
{
@@ -1077,6 +1079,7 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
:param specs:
:param vm_template_id:
:param template:
+ :param real_request:
:return:
"""
# Create user if the user is not logged in and if he is not already
@@ -1255,14 +1258,14 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
'reply_to': ['info@ungleich.ch'],
}
send_plain_email_task.delay(email_data)
-
+ redirect_url = reverse('datacenterlight:index')
+ if real_request:
+ clear_all_session_vars(real_request)
+ if real_request.user.is_authenticated():
+ redirect_url = reverse('hosting:invoices')
response = {
'status': True,
- 'redirect': (
- reverse('hosting:invoices')
- if request.user.is_authenticated()
- else reverse('datacenterlight:index')
- ),
+ 'redirect': redirect_url,
'msg_title': str(_('Thank you for the payment.')),
'msg_body': str(
_('You will soon receive a confirmation email of the '
@@ -1290,6 +1293,9 @@ def do_create_vm(request, user, stripe_api_cus_id, card_details_response,
vm_template_id, template, user
)
+ if real_request:
+ clear_all_session_vars(real_request)
+
def show_error(msg, request):
messages.add_message(request, messages.ERROR, msg,
diff --git a/webhook/views.py b/webhook/views.py
index 20ee6c45..c61bf3f0 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -9,7 +9,7 @@ from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
-from datacenterlight.views import do_create_vm
+from datacenterlight.views import do_provisioning
from membership.models import StripeCustomer
from hosting.models import IncompleteSubscriptions
@@ -180,7 +180,7 @@ def handle_webhook(request):
logger.debug("5*******")
logger.debug(template)
logger.debug("6*******")
- do_create_vm(
+ do_provisioning(
request=request,
user={'name': incomplete_sub.name,
'email': incomplete_sub.email},
@@ -192,7 +192,8 @@ def handle_webhook(request):
specs=specs,
vm_template_id=incomplete_sub.vm_template_id,
template=template,
- billing_address_data=billing_address_data
+ billing_address_data=billing_address_data,
+ real_request=None
)
except (IncompleteSubscriptions.DoesNotExist,
IncompleteSubscriptions.MultipleObjectsReturned) as ex:
From a9778076d6efee7a221dbf4ee7194048781db770 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 17:10:06 +0530
Subject: [PATCH 132/217] Clean session variables using real_request
---
datacenterlight/views.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 9e4e7ed7..0c366f9b 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1043,7 +1043,6 @@ class OrderConfirmationView(DetailView, FormView):
' We will send you a confirmation email as soon as'
' it is ready.'))
}
- clear_all_session_vars(request)
return JsonResponse(response)
@@ -1064,7 +1063,7 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
'generic_payment_details': {
'amount': 100,
'recurring':
- }
+ },
}
:param user: a dict
{
@@ -1273,7 +1272,6 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
'info@ungleich.ch for any question that you may have.')
)
}
- clear_all_session_vars(request)
return JsonResponse(response)
From 78b819116524fa0a717dec8e2db45f18bf1bdd6e Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 17:10:23 +0530
Subject: [PATCH 133/217] Implement invoice.payment_failed case
---
webhook/views.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/webhook/views.py b/webhook/views.py
index c61bf3f0..5627c648 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -215,6 +215,14 @@ def handle_webhook(request):
'body': "Response = %s" % str(ex),
}
send_plain_email_task.delay(email_data)
+ elif event.type == 'invoice.payment_failed':
+ invoice_obj = event.data.object
+ logger.debug("Webhook Event: invoice.payment_failed")
+ logger.debug("invoice_obj %s " % str(invoice_obj))
+ if (invoice_obj.payment_failed and
+ invoice_obj.billing_reason == "subscription_update"):
+ logger.debug("Payment failed, inform the users")
+
else:
logger.error("Unhandled event : " + event.type)
return HttpResponse(status=200)
From 799194152ee6ab29f2e7177ad5514dfd2e3858cb Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 17:16:19 +0530
Subject: [PATCH 134/217] Remove todo
---
webhook/views.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/webhook/views.py b/webhook/views.py
index 5627c648..b0eac5df 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -198,7 +198,6 @@ def handle_webhook(request):
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,
From d447f8d9e69943bf2147bcbd85f7aaa1a71e3152 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 17:26:40 +0530
Subject: [PATCH 135/217] Add logging message
---
datacenterlight/views.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 0c366f9b..d6ac7b5f 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -309,6 +309,7 @@ class PaymentOrderView(FormView):
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
def get(self, request, *args, **kwargs):
+ logger.debug("Session: %s" % str(request.session))
request.session.pop('vat_validation_status')
if (('type' in request.GET and request.GET['type'] == 'generic')
or 'product_slug' in kwargs):
@@ -461,6 +462,7 @@ class PaymentOrderView(FormView):
token = address_form.cleaned_data.get('token')
if token is '':
card_id = address_form.cleaned_data.get('card')
+ logger.debug("token is empty and card_id is %s" % card_id)
try:
user_card_detail = UserCardDetail.objects.get(id=card_id)
if not request.user.has_perm(
@@ -487,6 +489,7 @@ class PaymentOrderView(FormView):
request.session['card_id'] = user_card_detail.id
else:
request.session['token'] = token
+ logger.debug("token is %s" % token)
if request.user.is_authenticated():
this_user = {
'email': request.user.email,
From 377d12b5a57bf81e7a3b665294a120607a16def5 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 17:47:37 +0530
Subject: [PATCH 136/217] Create IncompleteSubscriptions only for SCA case
---
datacenterlight/views.py | 41 ++++++++++++++++++++--------------------
1 file changed, 21 insertions(+), 20 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index d6ac7b5f..ee5dfd00 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -930,26 +930,6 @@ class OrderConfirmationView(DetailView, FormView):
if stripe_subscription_obj:
subscription_status = stripe_subscription_obj.status
- # Store params so that they can be retrieved later
- IncompleteSubscriptions.objects.create(
- subscription_id=stripe_subscription_obj.id,
- 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
if (stripe_subscription_obj is None
or (stripe_subscription_obj.status != 'active'
@@ -962,6 +942,27 @@ class OrderConfirmationView(DetailView, FormView):
msg = subscription_result.get('error')
return show_error(msg, self.request)
elif stripe_subscription_obj.status == 'incomplete':
+ # Store params so that they can be retrieved later
+ IncompleteSubscriptions.objects.create(
+ subscription_id=stripe_subscription_obj.id,
+ 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')
+ )
+ )
pi = stripe.PaymentIntent.retrieve(
latest_invoice.payment_intent
)
From c0aeac4dc7eac59c653670d6b4bf8206c71a99ef Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 17:47:50 +0530
Subject: [PATCH 137/217] Add some logger messages
---
datacenterlight/views.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index ee5dfd00..be387986 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1205,6 +1205,7 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
order.set_subscription_id(
stripe_subscription_obj.id, card_details_dict
)
+ logger.debug("recurring case, set order subscription id done")
else:
logger.debug("one time charge case")
# Associate the given stripe charge id with the order
@@ -1215,6 +1216,7 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
order.generic_payment_description = gp_details["description"]
order.generic_product_id = gp_details["product_id"]
order.save()
+ logger.debug("Order saved")
# send emails
context = {
'name': user.get('name'),
@@ -1262,10 +1264,12 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
}
send_plain_email_task.delay(email_data)
redirect_url = reverse('datacenterlight:index')
+ logger.debug("Sent user/admin emails")
if real_request:
clear_all_session_vars(real_request)
if real_request.user.is_authenticated():
redirect_url = reverse('hosting:invoices')
+ logger.debug("redirect_url = %s " % redirect_url)
response = {
'status': True,
'redirect': redirect_url,
From c28bd9091abde5dca94b976f7b63cfb72a117626 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 17:53:19 +0530
Subject: [PATCH 138/217] Add more logger
---
datacenterlight/views.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index be387986..aea7dcbd 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1280,7 +1280,8 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
'info@ungleich.ch for any question that you may have.')
)
}
-
+ logger.debug("after response")
+ logger.debug(str(response))
return JsonResponse(response)
user = {
From a03e2dc00625dc80775d04b10d76b40daa10b413 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 18:10:17 +0530
Subject: [PATCH 139/217] Return provisioning if set in do_provisioning
---
datacenterlight/views.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index aea7dcbd..17690c78 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1020,7 +1020,7 @@ class OrderConfirmationView(DetailView, FormView):
"requires_source_action")
msg = subscription_result.get('error')
return show_error(msg, self.request)
- do_provisioning(
+ provisioning_response = do_provisioning(
req, user, stripe_api_cus_id,
card_details_response, stripe_subscription_obj,
stripe_onetime_charge, gp_details, specs, vm_template_id,
@@ -1034,6 +1034,10 @@ class OrderConfirmationView(DetailView, FormView):
CustomUser.MultipleObjectsReturned) as ex:
logger.error(str(ex))
+ if (provisioning_response and
+ type(provisioning_response) == JsonResponse):
+ return provisioning_response
+
response = {
'status': True,
'redirect': (
From c8519058c49a20924f74f3ed50e45018db3c78cb Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 18:31:45 +0530
Subject: [PATCH 140/217] Fix new_user_hosting_key_id
---
datacenterlight/views.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 17690c78..d0a9419c 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1120,9 +1120,10 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
new_user = authenticate(username=custom_user.email,
password=password)
logger.debug("User %s is authenticated" % custom_user.email)
- if 'new_user_hosting_key_id' in request:
+ new_user_hosting_key_id = request.get('new_user_hosting_key_id', None)
+ if new_user_hosting_key_id:
user_hosting_key = UserHostingKey.objects.get(
- id=request['new_user_hosting_key_id'])
+ id=new_user_hosting_key_id)
user_hosting_key.user = new_user
user_hosting_key.save()
logger.debug("User %s key is saved" % custom_user.email)
From 080a45f39c4a3812ebd61cf38a9a78dceda94778 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 20:38:42 +0530
Subject: [PATCH 141/217] Pop up stale card_id/token or billing_address_data in
the paymentorder page
---
datacenterlight/views.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index d0a9419c..388d78b2 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -309,8 +309,13 @@ class PaymentOrderView(FormView):
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
def get(self, request, *args, **kwargs):
- logger.debug("Session: %s" % str(request.session))
request.session.pop('vat_validation_status')
+ request.session.pop('card_id')
+ request.session.pop('token')
+ request.session.pop('billing_address_data')
+ logger.debug("Session: %s" % str(request.session))
+ for key, value in request.session.items():
+ logger.debug("Session: %s %s" % (key, value))
if (('type' in request.GET and request.GET['type'] == 'generic')
or 'product_slug' in kwargs):
request.session['generic_payment_type'] = 'generic'
From 6a7373523e2f0d71e11916b81081cb972e0aa6cd Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 20:39:41 +0530
Subject: [PATCH 142/217] Set default card before making payments
---
datacenterlight/views.py | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 388d78b2..a59cb325 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -792,6 +792,10 @@ class OrderConfirmationView(DetailView, FormView):
'brand': user_card_detail.brand,
'card_id': user_card_detail.card_id
}
+ UserCardDetail.set_default_card(
+ stripe_api_cus_id=stripe_api_cus_id,
+ stripe_source_id=user_card_detail.card_id
+ )
logger.debug("card_details_dict=%s" % card_details_dict)
else:
response = {
@@ -1142,11 +1146,11 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
'brand': user_card_detail.brand,
'card_id': user_card_detail.card_id
}
- if not user_card_detail.preferred:
- UserCardDetail.set_default_card(
- stripe_api_cus_id=stripe_api_cus_id,
- stripe_source_id=user_card_detail.card_id
- )
+ #if not user_card_detail.preferred:
+ UserCardDetail.set_default_card(
+ stripe_api_cus_id=stripe_api_cus_id,
+ stripe_source_id=user_card_detail.card_id
+ )
else:
logger.debug("card_id was NOT in request, using "
"card_details_response")
From 968eaaf6a40c273d1c7c93128a1e5b53602dd067 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 20:40:20 +0530
Subject: [PATCH 143/217] For generic payments, take users to invoice page
after purchase
---
datacenterlight/views.py | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index a59cb325..3f0d87d6 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -987,15 +987,18 @@ class OrderConfirmationView(DetailView, FormView):
'success': {
'status': True,
'redirect': (
- reverse('hosting:virtual_machines')
+ reverse('hosting:invoices')
if request.user.is_authenticated()
else reverse('datacenterlight:index')
),
'msg_title': str(_('Thank you for the order.')),
'msg_body': str(
- _('Your VM will be up and running in a few moments.'
- ' We will send you a confirmation email as soon as'
- ' it is ready.'))
+ _('Your product will be provisioned as soon as'
+ ' we receive a payment confirmation from '
+ 'Stripe. We will send you a confirmation '
+ 'email. You can always contact us at '
+ 'support@datacenterlight.ch')
+ )
},
'error': {
'status': False,
From eefabe45b62a9cb0e496e0d7c2e3c62c748be21f Mon Sep 17 00:00:00 2001
From: PCoder
Date: Wed, 23 Dec 2020 20:41:31 +0530
Subject: [PATCH 144/217] Login new users only for non-SCA generic subscription
payments only
---
datacenterlight/views.py | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 3f0d87d6..988be8d0 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1039,16 +1039,13 @@ class OrderConfirmationView(DetailView, FormView):
template, request.session.get('billing_address_data'),
self.request
)
- try:
- custom_user = CustomUser.objects.get(email=user.get('email'))
- login(self.request, custom_user)
- except (CustomUser.DoesNotExist,
- CustomUser.MultipleObjectsReturned) as ex:
- logger.error(str(ex))
if (provisioning_response and
- type(provisioning_response) == JsonResponse):
- return provisioning_response
+ type(provisioning_response['response']) == JsonResponse):
+ new_user = provisioning_response.get('user', None)
+ if new_user:
+ login(self.request, new_user)
+ return provisioning_response['response']
response = {
'status': True,
@@ -1103,6 +1100,7 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
"""
# Create user if the user is not logged in and if he is not already
# registered
+ new_user = None
try:
custom_user = CustomUser.objects.get(
email=user.get('email'))
@@ -1299,7 +1297,7 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
}
logger.debug("after response")
logger.debug(str(response))
- return JsonResponse(response)
+ return {'response': JsonResponse(response), 'user': new_user}
user = {
'name': custom_user.name,
From 39c8e35eca86ec4db33aed04af9ec1b093255151 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 24 Dec 2020 06:25:22 +0530
Subject: [PATCH 145/217] Add logger messages
---
utils/stripe_utils.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index b8667a40..e25e736f 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -34,6 +34,7 @@ def handleStripeError(f):
logger.error(str(e))
return response
except stripe.error.RateLimitError as e:
+ logger.error(str(e))
response.update(
{'error': "Too many requests made to the API too quickly"})
return response
@@ -317,13 +318,16 @@ class StripeUtils(object):
]
:return: The subscription StripeObject
"""
-
+ logger.debug("Subscribing %s to plan %s : coupon = %s" % (
+ customer, str(plans), str(coupon)
+ ))
subscription_result = self.stripe.Subscription.create(
customer=customer, items=plans, trial_end=trial_end,
coupon=coupon,
default_tax_rates=tax_rates,
payment_behavior='allow_incomplete'
)
+ logger.debug("Done subscribing")
return subscription_result
@handleStripeError
From 98628596f02509497c6c2742ea2a8662c58fb120 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 24 Dec 2020 06:25:37 +0530
Subject: [PATCH 146/217] Add params to docstring
---
utils/stripe_utils.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index e25e736f..8455cbb5 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -305,6 +305,8 @@ class StripeUtils(object):
"""
Subscribes the given customer to the list of given plans
+ :param tax_rates:
+ :param coupon:
:param customer: The stripe customer identifier
:param plans: A list of stripe plans.
:param trial_end: An integer representing when the Stripe subscription
From 82359064cd1f8b96055d2c5e3dde0f455e7dea4c Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 24 Dec 2020 06:47:33 +0530
Subject: [PATCH 147/217] Handle creation of correctly
---
utils/stripe_utils.py | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index 8455cbb5..65b47228 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -70,7 +70,7 @@ class StripeUtils(object):
CURRENCY = 'chf'
INTERVAL = 'month'
SUCCEEDED_STATUS = 'succeeded'
- STRIPE_PLAN_ALREADY_EXISTS = 'Plan already exists'
+ RESOURCE_ALREADY_EXISTS_ERROR_CODE = 'resource_already_exists'
STRIPE_NO_SUCH_PLAN = 'No such plan'
PLAN_EXISTS_ERROR_MSG = 'Plan {} exists already.\nCreating a local StripePlan now.'
PLAN_DOES_NOT_EXIST_ERROR_MSG = 'Plan {} does not exist.'
@@ -268,11 +268,17 @@ class StripeUtils(object):
stripe_plan_db_obj = StripePlan.objects.create(
stripe_plan_id=stripe_plan_id)
except stripe.error.InvalidRequestError as e:
- if self.STRIPE_PLAN_ALREADY_EXISTS in str(e):
+ logger.error(str(e))
+ logger.error("error_code = " % e.error.code)
+ if self.RESOURCE_ALREADY_EXISTS_ERROR_CODE in e.error.code:
logger.debug(
self.PLAN_EXISTS_ERROR_MSG.format(stripe_plan_id))
- stripe_plan_db_obj = StripePlan.objects.create(
+ stripe_plan_db_obj, c = StripePlan.objects.get_or_create(
stripe_plan_id=stripe_plan_id)
+ if c:
+ logger.debug("Created stripe plan %s" % stripe_plan_id)
+ else:
+ logger.debug("Plan %s exists already" % stripe_plan_id)
return stripe_plan_db_obj
@handleStripeError
From 624cc45c12090aef90fa9da5f8c21f9f4cc072bc Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 24 Dec 2020 06:51:29 +0530
Subject: [PATCH 148/217] Log error dict
---
utils/stripe_utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index 65b47228..d32bd4e5 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -269,7 +269,7 @@ class StripeUtils(object):
stripe_plan_id=stripe_plan_id)
except stripe.error.InvalidRequestError as e:
logger.error(str(e))
- logger.error("error_code = " % e.error.code)
+ logger.error("error_code = " % e.__dict__)
if self.RESOURCE_ALREADY_EXISTS_ERROR_CODE in e.error.code:
logger.debug(
self.PLAN_EXISTS_ERROR_MSG.format(stripe_plan_id))
From acba77976d4f4d5ec4d0673a63d0b46a711c813b Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 24 Dec 2020 06:55:19 +0530
Subject: [PATCH 149/217] Add missing formatting identifier
---
utils/stripe_utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index d32bd4e5..f5df8aa8 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -269,7 +269,7 @@ class StripeUtils(object):
stripe_plan_id=stripe_plan_id)
except stripe.error.InvalidRequestError as e:
logger.error(str(e))
- logger.error("error_code = " % e.__dict__)
+ logger.error("error_code = %s" % str(e.__dict__))
if self.RESOURCE_ALREADY_EXISTS_ERROR_CODE in e.error.code:
logger.debug(
self.PLAN_EXISTS_ERROR_MSG.format(stripe_plan_id))
From 1c4f29777555dd8ab6f32021a765e0302104cbc2 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 24 Dec 2020 19:34:06 +0530
Subject: [PATCH 150/217] Begin migrating to PaymentIntent
---
.../datacenterlight/landing_payment.html | 1 +
datacenterlight/views.py | 16 ++++++++++++++++
hosting/static/hosting/js/payment.js | 2 +-
utils/stripe_utils.py | 13 +++++++++++++
webhook/views.py | 5 ++++-
5 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/datacenterlight/templates/datacenterlight/landing_payment.html b/datacenterlight/templates/datacenterlight/landing_payment.html
index 66a0e63f..f112f0d9 100644
--- a/datacenterlight/templates/datacenterlight/landing_payment.html
+++ b/datacenterlight/templates/datacenterlight/landing_payment.html
@@ -187,6 +187,7 @@
window.enter_your_card_text = '{%trans "Enter your credit card number" %}';
(function () {
+ window.paymentIntentSecret = "{{payment_intent_secret}}";
window.stripeKey = "{{stripe_key}}";
window.current_lan = "{{LANGUAGE_CODE}}";
})();
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 988be8d0..3c92506b 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -285,12 +285,28 @@ class PaymentOrderView(FormView):
product = GenericProduct.objects.get(
id=self.request.session['product_id']
)
+ # TODO get the correct price of the product from order
+ # confirmation
+ stripe_utils = StripeUtils()
+ payment_intent_response = stripe_utils.get_payment_intent(
+ float(product.get_actual_price())
+ )
+ if not payment_intent_response.get('response_object'):
+ logger.error("Could not create payment_intent %s" %
+ str(payment_intent_response))
+ else:
+ logger.debug("*******")
+ logger.debug(
+ "payment_intent_obj = %s" %
+ str(payment_intent_response.get('response_object')))
+ logger.debug("*******")
context.update({'generic_payment_form': ProductPaymentForm(
prefix='generic_payment_form',
initial={'product_name': product.product_name,
'amount': float(product.get_actual_price()),
'recurring': product.product_is_subscription,
'description': product.product_description,
+ 'payment_intent_secret': 'secret_here'
},
product_id=product.id
), })
diff --git a/hosting/static/hosting/js/payment.js b/hosting/static/hosting/js/payment.js
index fa89f218..be94a866 100644
--- a/hosting/static/hosting/js/payment.js
+++ b/hosting/static/hosting/js/payment.js
@@ -197,7 +197,7 @@ $(document).ready(function () {
} else {
var process_text = "Processing";
if (typeof window.processing_text !== 'undefined') {
- process_text = window.processing_text
+ process_text = window.processing_text;
}
$form_new.find('[type=submit]').html(process_text + ' ');
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index f5df8aa8..bf731508 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -493,6 +493,19 @@ class StripeUtils(object):
)
return tax_id_obj
+ @handleStripeError
+ def get_payment_intent(self, amount):
+ """
+ Adds VM metadata to a subscription
+ :param amount: the amount of payment_intent
+ :return:
+ """
+ payment_intent_obj = stripe.PaymentIntent.create(
+ amount=amount,
+ currency='chf'
+ )
+ return payment_intent_obj
+
def compare_vat_numbers(self, vat1, vat2):
_vat1 = vat1.replace(" ", "").replace(".", "").replace("-","")
_vat2 = vat2.replace(" ", "").replace(".", "").replace("-","")
diff --git a/webhook/views.py b/webhook/views.py
index b0eac5df..416d07b7 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -221,7 +221,10 @@ def handle_webhook(request):
if (invoice_obj.payment_failed and
invoice_obj.billing_reason == "subscription_update"):
logger.debug("Payment failed, inform the users")
-
+ elif event.type == 'payment_intent.succeeded':
+ payment_intent_obj = event.data.object
+ logger.debug("Webhook Event: payment_intent.succeeded")
+ logger.debug("payment_intent_obj %s " % str(payment_intent_obj))
else:
logger.error("Unhandled event : " + event.type)
return HttpResponse(status=200)
From ec1da8fbdfabcf0b448b1ddca6ebb88c276417bd Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 24 Dec 2020 19:40:39 +0530
Subject: [PATCH 151/217] Use cents price for Stripe
---
datacenterlight/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 3c92506b..cd1de47d 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -289,7 +289,7 @@ class PaymentOrderView(FormView):
# confirmation
stripe_utils = StripeUtils()
payment_intent_response = stripe_utils.get_payment_intent(
- float(product.get_actual_price())
+ int(product.get_actual_price() * 100)
)
if not payment_intent_response.get('response_object'):
logger.error("Could not create payment_intent %s" %
From e9c596de66fa3720eafc25fff703f6d2b336d983 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 24 Dec 2020 20:03:46 +0530
Subject: [PATCH 152/217] Test PaymentIntent for payment of generic onetime
products
---
datacenterlight/views.py | 23 +++++++++++------------
hosting/static/hosting/js/payment.js | 23 ++++++++++++++++++++++-
2 files changed, 33 insertions(+), 13 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index cd1de47d..bc586ec2 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -291,23 +291,22 @@ class PaymentOrderView(FormView):
payment_intent_response = stripe_utils.get_payment_intent(
int(product.get_actual_price() * 100)
)
- if not payment_intent_response.get('response_object'):
+ payment_intent = payment_intent_response.get('response_object')
+ if not payment_intent:
logger.error("Could not create payment_intent %s" %
str(payment_intent_response))
else:
- logger.debug("*******")
- logger.debug(
- "payment_intent_obj = %s" %
- str(payment_intent_response.get('response_object')))
- logger.debug("*******")
+ logger.debug("payment_intent_obj = %s" %
+ str(payment_intent))
context.update({'generic_payment_form': ProductPaymentForm(
prefix='generic_payment_form',
- initial={'product_name': product.product_name,
- 'amount': float(product.get_actual_price()),
- 'recurring': product.product_is_subscription,
- 'description': product.product_description,
- 'payment_intent_secret': 'secret_here'
- },
+ initial={
+ 'product_name': product.product_name,
+ 'amount': float(product.get_actual_price()),
+ 'recurring': product.product_is_subscription,
+ 'description': product.product_description,
+ 'payment_intent_secret': payment_intent.client_secret
+ },
product_id=product.id
), })
else:
diff --git a/hosting/static/hosting/js/payment.js b/hosting/static/hosting/js/payment.js
index be94a866..933e15df 100644
--- a/hosting/static/hosting/js/payment.js
+++ b/hosting/static/hosting/js/payment.js
@@ -178,7 +178,28 @@ $(document).ready(function () {
}
var $form_new = $('#payment-form-new');
- $form_new.submit(payWithStripe_new);
+ $form_new.submit(payWithPaymentIntent);
+ function payWithPaymentIntent(e) {
+ e.preventDefault();
+
+ stripe.confirmCardPayment(
+ window.paymentIntentSecret,
+ {
+ payment_method: {card: cardNumberElement}
+ }
+ ).then(function(result) {
+ if (result.error) {
+ // Display error.message in your UI.
+ var errorElement = document.getElementById('card-errors');
+ errorElement.textContent = result.error.message;
+ } else {
+ // The payment has succeeded
+ // Display a success message
+ alert("Thanks for the order. Your product will be provisioned " +
+ "as soon as we receive the payment. Thank you.");
+ }
+ });
+ }
function payWithStripe_new(e) {
e.preventDefault();
From 35cc9d422925826007a18fa6f60085f7bf7782f2 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 24 Dec 2020 20:07:31 +0530
Subject: [PATCH 153/217] Log client secret
---
datacenterlight/views.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index bc586ec2..5d12a878 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -296,8 +296,8 @@ class PaymentOrderView(FormView):
logger.error("Could not create payment_intent %s" %
str(payment_intent_response))
else:
- logger.debug("payment_intent_obj = %s" %
- str(payment_intent))
+ logger.debug("payment_intent.client_secret = %s" %
+ str(payment_intent.client_secret))
context.update({'generic_payment_form': ProductPaymentForm(
prefix='generic_payment_form',
initial={
From c3286a68a522eb3719a04bbb85491c1620571099 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 10:04:21 +0530
Subject: [PATCH 154/217] Use payment method instead of token and PaymentIntent
all over
---
.../datacenterlight/landing_payment.html | 1 -
.../datacenterlight/order_detail.html | 13 ++
datacenterlight/views.py | 108 ++++++++-----
hosting/static/hosting/js/payment.js | 150 +++++++++++-------
.../hosting/js/virtual_machine_detail.js | 31 +++-
hosting/views.py | 15 +-
membership/models.py | 2 +-
utils/stripe_utils.py | 46 ++++--
8 files changed, 245 insertions(+), 121 deletions(-)
diff --git a/datacenterlight/templates/datacenterlight/landing_payment.html b/datacenterlight/templates/datacenterlight/landing_payment.html
index f112f0d9..66a0e63f 100644
--- a/datacenterlight/templates/datacenterlight/landing_payment.html
+++ b/datacenterlight/templates/datacenterlight/landing_payment.html
@@ -187,7 +187,6 @@
window.enter_your_card_text = '{%trans "Enter your credit card number" %}';
(function () {
- window.paymentIntentSecret = "{{payment_intent_secret}}";
window.stripeKey = "{{stripe_key}}";
window.current_lan = "{{LANGUAGE_CODE}}";
})();
diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html
index d8eb4934..1914acb8 100644
--- a/datacenterlight/templates/datacenterlight/order_detail.html
+++ b/datacenterlight/templates/datacenterlight/order_detail.html
@@ -2,6 +2,14 @@
{% load staticfiles bootstrap3 i18n custom_tags humanize %}
{% block content %}
+
{% if messages %}
@@ -321,5 +329,10 @@
{%endblock%}
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 5d12a878..f50cf422 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -285,28 +285,13 @@ class PaymentOrderView(FormView):
product = GenericProduct.objects.get(
id=self.request.session['product_id']
)
- # TODO get the correct price of the product from order
- # confirmation
- stripe_utils = StripeUtils()
- payment_intent_response = stripe_utils.get_payment_intent(
- int(product.get_actual_price() * 100)
- )
- payment_intent = payment_intent_response.get('response_object')
- if not payment_intent:
- logger.error("Could not create payment_intent %s" %
- str(payment_intent_response))
- else:
- logger.debug("payment_intent.client_secret = %s" %
- str(payment_intent.client_secret))
context.update({'generic_payment_form': ProductPaymentForm(
prefix='generic_payment_form',
- initial={
- 'product_name': product.product_name,
- 'amount': float(product.get_actual_price()),
- 'recurring': product.product_is_subscription,
- 'description': product.product_description,
- 'payment_intent_secret': payment_intent.client_secret
- },
+ initial={'product_name': product.product_name,
+ 'amount': float(product.get_actual_price()),
+ 'recurring': product.product_is_subscription,
+ 'description': product.product_description,
+ },
product_id=product.id
), })
else:
@@ -479,8 +464,9 @@ class PaymentOrderView(FormView):
context['generic_payment_form'] = generic_payment_form
context['billing_address_form'] = address_form
return self.render_to_response(context)
- token = address_form.cleaned_data.get('token')
- if token is '':
+ id_payment_method = self.request.POST.get('id_payment_method',
+ None)
+ if id_payment_method is None:
card_id = address_form.cleaned_data.get('card')
logger.debug("token is empty and card_id is %s" % card_id)
try:
@@ -508,15 +494,16 @@ class PaymentOrderView(FormView):
)
request.session['card_id'] = user_card_detail.id
else:
- request.session['token'] = token
- logger.debug("token is %s" % token)
+ request.session["id_payment_method"] = id_payment_method
+ logger.debug("id_payment_method is %s" % id_payment_method)
if request.user.is_authenticated():
this_user = {
'email': request.user.email,
'name': request.user.name
}
customer = StripeCustomer.get_or_create(
- email=this_user.get('email'), token=token
+ email=this_user.get('email'),
+ id_payment_method=id_payment_method
)
else:
user_email = address_form.cleaned_data.get('email')
@@ -539,7 +526,7 @@ class PaymentOrderView(FormView):
)
customer = StripeCustomer.create_stripe_api_customer(
email=user_email,
- token=token,
+ token=id_payment_method,
customer_name=user_name)
except CustomUser.DoesNotExist:
logger.debug(
@@ -550,7 +537,7 @@ class PaymentOrderView(FormView):
)
customer = StripeCustomer.create_stripe_api_customer(
email=user_email,
- token=token,
+ token=id_payment_method,
customer_name=user_name)
billing_address = address_form.save()
@@ -622,11 +609,11 @@ class OrderConfirmationView(DetailView, FormView):
if (('specs' not in request.session or 'user' not in request.session)
and 'generic_payment_type' not in request.session):
return HttpResponseRedirect(reverse('datacenterlight:index'))
- if 'token' in self.request.session:
- token = self.request.session['token']
+ if 'id_payment_method' in self.request.session:
+ payment_method = self.request.session['id_payment_method']
stripe_utils = StripeUtils()
- card_details = stripe_utils.get_cards_details_from_token(
- token
+ card_details = stripe_utils.get_cards_details_from_payment_method(
+ payment_method
)
if not card_details.get('response_object'):
return HttpResponseRedirect(reverse('hosting:payment'))
@@ -635,6 +622,7 @@ class OrderConfirmationView(DetailView, FormView):
context['cc_brand'] = card_details_response['brand']
context['cc_exp_year'] = card_details_response['exp_year']
context['cc_exp_month'] = '{:02d}'.format(card_details_response['exp_month'])
+ context['id_payment_method'] = payment_method
else:
card_id = self.request.session.get('card_id')
card_detail = UserCardDetail.objects.get(id=card_id)
@@ -718,6 +706,27 @@ class OrderConfirmationView(DetailView, FormView):
'form': UserHostingKeyForm(request=self.request),
'keys': get_all_public_keys(self.request.user)
})
+
+ # Obtain PaymentIntent so that we can initiate and charge/subscribe
+ # the customer
+ stripe_utils = StripeUtils()
+ payment_intent_response = stripe_utils.get_payment_intent(
+ int(request.session['generic_payment_details']['amount'] *
+ 100),
+ customer=request.session['customer']
+ )
+ payment_intent = payment_intent_response.get(
+ 'response_object')
+ if not payment_intent:
+ logger.error("Could not create payment_intent %s" %
+ str(payment_intent_response))
+ else:
+ logger.debug("payment_intent.client_secret = %s" %
+ str(payment_intent.client_secret))
+ context.update({
+ 'payment_intent_secret': payment_intent.client_secret
+ })
+
context.update({
'site_url': reverse('datacenterlight:index'),
'page_header_text': _('Confirm Order'),
@@ -725,6 +734,8 @@ class OrderConfirmationView(DetailView, FormView):
request.session.get('billing_address_data')
),
'cms_integration': get_cms_integration('default'),
+ 'error_msg': get_error_response_dict("Error", request),
+ 'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
})
return render(request, self.template_name, context)
@@ -765,9 +776,9 @@ class OrderConfirmationView(DetailView, FormView):
'generic_payment_details': generic_payment_details
}
- if 'token' in request.session:
- card_details = stripe_utils.get_cards_details_from_token(
- request.session.get('token')
+ if 'id_payment_method' in request.session:
+ card_details = stripe_utils.get_cards_details_from_payment_method(
+ request.session.get('id_payment_method')
)
logger.debug(
"card_details=%s" % (card_details))
@@ -788,7 +799,7 @@ class OrderConfirmationView(DetailView, FormView):
)
if not ucd:
acc_result = stripe_utils.associate_customer_card(
- stripe_api_cus_id, request.session['token'],
+ stripe_api_cus_id, request.session['id_payment_method'],
set_as_default=True
)
if acc_result['response_object'] is None:
@@ -799,6 +810,21 @@ class OrderConfirmationView(DetailView, FormView):
)
)
return show_error(msg, self.request)
+ else:
+ # Associate PaymentMethod with the stripe customer
+ # and set it as the default source
+ acc_result = stripe_utils.associate_customer_card(
+ stripe_api_cus_id, request.session['id_payment_method'],
+ set_as_default=True
+ )
+ if acc_result['response_object'] is None:
+ msg = _(
+ 'An error occurred while associating the card.'
+ ' Details: {details}'.format(
+ details=acc_result['error']
+ )
+ )
+ return show_error(msg, self.request)
elif 'card_id' in request.session:
card_id = request.session.get('card_id')
user_card_detail = UserCardDetail.objects.get(id=card_id)
@@ -1334,9 +1360,7 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
clear_all_session_vars(real_request)
-def show_error(msg, request):
- messages.add_message(request, messages.ERROR, msg,
- extra_tags='failed_payment')
+def get_error_response_dict(msg, request):
response = {
'status': False,
'redirect': "{url}#{section}".format(
@@ -1356,4 +1380,10 @@ def show_error(msg, request):
' On close of this popup, you will be redirected back to'
' the payment page.'))
}
- return JsonResponse(response)
+ return response
+
+
+def show_error(msg, request):
+ messages.add_message(request, messages.ERROR, msg,
+ extra_tags='failed_payment')
+ return JsonResponse(get_error_response_dict(msg,request))
diff --git a/hosting/static/hosting/js/payment.js b/hosting/static/hosting/js/payment.js
index 933e15df..a2e2717a 100644
--- a/hosting/static/hosting/js/payment.js
+++ b/hosting/static/hosting/js/payment.js
@@ -84,68 +84,72 @@ $(document).ready(function () {
var hasCreditcard = window.hasCreditcard || false;
if (!hasCreditcard && window.stripeKey) {
var stripe = Stripe(window.stripeKey);
- var element_style = {
- fonts: [{
- family: 'lato-light',
- src: 'url(https://cdn.jsdelivr.net/font-lato/2.0/Lato/Lato-Light.woff) format("woff2")'
- }, {
- family: 'lato-regular',
- src: 'url(https://cdn.jsdelivr.net/font-lato/2.0/Lato/Lato-Regular.woff) format("woff2")'
- }
- ],
- locale: window.current_lan
- };
- var elements = stripe.elements(element_style);
- var credit_card_text_style = {
- base: {
- iconColor: '#666EE8',
- color: '#31325F',
- lineHeight: '25px',
- fontWeight: 300,
- fontFamily: "'lato-light', sans-serif",
- fontSize: '14px',
- '::placeholder': {
- color: '#777'
+ if (window.pm_id) {
+
+ } else {
+ var element_style = {
+ fonts: [{
+ family: 'lato-light',
+ src: 'url(https://cdn.jsdelivr.net/font-lato/2.0/Lato/Lato-Light.woff) format("woff2")'
+ }, {
+ family: 'lato-regular',
+ src: 'url(https://cdn.jsdelivr.net/font-lato/2.0/Lato/Lato-Regular.woff) format("woff2")'
}
- },
- invalid: {
- iconColor: '#eb4d5c',
- color: '#eb4d5c',
- lineHeight: '25px',
- fontWeight: 300,
- fontFamily: "'lato-regular', sans-serif",
- fontSize: '14px',
- '::placeholder': {
+ ],
+ locale: window.current_lan
+ };
+ var elements = stripe.elements(element_style);
+ var credit_card_text_style = {
+ base: {
+ iconColor: '#666EE8',
+ color: '#31325F',
+ lineHeight: '25px',
+ fontWeight: 300,
+ fontFamily: "'lato-light', sans-serif",
+ fontSize: '14px',
+ '::placeholder': {
+ color: '#777'
+ }
+ },
+ invalid: {
+ iconColor: '#eb4d5c',
color: '#eb4d5c',
- fontWeight: 400
+ lineHeight: '25px',
+ fontWeight: 300,
+ fontFamily: "'lato-regular', sans-serif",
+ fontSize: '14px',
+ '::placeholder': {
+ color: '#eb4d5c',
+ fontWeight: 400
+ }
}
- }
- };
+ };
- var enter_ccard_text = "Enter your credit card number";
- if (typeof window.enter_your_card_text !== 'undefined') {
- enter_ccard_text = window.enter_your_card_text;
+ var enter_ccard_text = "Enter your credit card number";
+ if (typeof window.enter_your_card_text !== 'undefined') {
+ enter_ccard_text = window.enter_your_card_text;
+ }
+ var cardNumberElement = elements.create('cardNumber', {
+ style: credit_card_text_style,
+ placeholder: enter_ccard_text
+ });
+ cardNumberElement.mount('#card-number-element');
+
+ var cardExpiryElement = elements.create('cardExpiry', {
+ style: credit_card_text_style
+ });
+ cardExpiryElement.mount('#card-expiry-element');
+
+ var cardCvcElement = elements.create('cardCvc', {
+ style: credit_card_text_style
+ });
+ cardCvcElement.mount('#card-cvc-element');
+ cardNumberElement.on('change', function (event) {
+ if (event.brand) {
+ setBrandIcon(event.brand);
+ }
+ });
}
- var cardNumberElement = elements.create('cardNumber', {
- style: credit_card_text_style,
- placeholder: enter_ccard_text
- });
- cardNumberElement.mount('#card-number-element');
-
- var cardExpiryElement = elements.create('cardExpiry', {
- style: credit_card_text_style
- });
- cardExpiryElement.mount('#card-expiry-element');
-
- var cardCvcElement = elements.create('cardCvc', {
- style: credit_card_text_style
- });
- cardCvcElement.mount('#card-cvc-element');
- cardNumberElement.on('change', function (event) {
- if (event.brand) {
- setBrandIcon(event.brand);
- }
- });
}
var submit_form_btn = $('#payment_button_with_creditcard');
@@ -163,7 +167,7 @@ $(document).ready(function () {
if (parts.length === 2) return parts.pop().split(";").shift();
}
- function submitBillingForm() {
+ function submitBillingForm(pmId) {
var billing_form = $('#billing-form');
var recurring_input = $('#id_generic_payment_form-recurring');
billing_form.append('');
@@ -174,20 +178,46 @@ $(document).ready(function () {
billing_form.append('');
}
billing_form.append('');
+ billing_form.append('');
billing_form.submit();
}
var $form_new = $('#payment-form-new');
$form_new.submit(payWithPaymentIntent);
+ window.result = "";
+ window.card = "";
function payWithPaymentIntent(e) {
e.preventDefault();
- stripe.confirmCardPayment(
+ function stripePMHandler(paymentMethod) {
+ // Insert the token ID into the form so it gets submitted to the server
+ console.log(paymentMethod);
+ $('#id_payment_method').val(paymentMethod.id);
+ submitBillingForm(paymentMethod.id);
+ }
+ stripe.createPaymentMethod({
+ type: 'card',
+ card: cardNumberElement,
+ })
+ .then(function(result) {
+ // Handle result.error or result.paymentMethod
+ window.result = result;
+ if(result.error) {
+ var errorElement = document.getElementById('card-errors');
+ errorElement.textContent = result.error.message;
+ } else {
+ console.log("created paymentMethod " + result.paymentMethod.id);
+ stripePMHandler(result.paymentMethod);
+ }
+ });
+ window.card = cardNumberElement;
+ /* stripe.confirmCardPayment(
window.paymentIntentSecret,
{
payment_method: {card: cardNumberElement}
}
).then(function(result) {
+ window.result = result;
if (result.error) {
// Display error.message in your UI.
var errorElement = document.getElementById('card-errors');
@@ -198,7 +228,7 @@ $(document).ready(function () {
alert("Thanks for the order. Your product will be provisioned " +
"as soon as we receive the payment. Thank you.");
}
- });
+ }); */
}
function payWithStripe_new(e) {
e.preventDefault();
diff --git a/hosting/static/hosting/js/virtual_machine_detail.js b/hosting/static/hosting/js/virtual_machine_detail.js
index dec8e680..e1bfd3a8 100644
--- a/hosting/static/hosting/js/virtual_machine_detail.js
+++ b/hosting/static/hosting/js/virtual_machine_detail.js
@@ -92,6 +92,35 @@ $(document).ready(function() {
});
var create_vm_form = $('#virtual_machine_create_form');
+ create_vm_form.submit(placeOrderPaymentIntent);
+
+ function placeOrderPaymentIntent(e) {
+ e.preventDefault();
+ var stripe = Stripe(window.stripeKey);
+ stripe.confirmCardPayment(
+ window.paymentIntentSecret,
+ {
+ payment_method: window.pm_id
+ }
+ ).then(function(result) {
+ window.result = result;
+ if (result.error) {
+ // Display error.message in your UI.
+ var errorElement = document.getElementById('card-errors');
+ errorElement.textContent = result.error.message;
+ } else {
+ // The payment has succeeded
+ // Display a success message
+ alert("Thanks for the order. Your product will be provisioned " +
+ "as soon as we receive the payment. Thank you.");
+ modal_btn.attr('href', err).removeClass('hide');
+ fa_icon.attr('class', 'checkmark');
+ $('#createvm-modal-title').text(data.success.msg_title);
+ $('#createvm-modal-body').html(data.success.msg_body);
+ }
+ });
+ }
+ /*
create_vm_form.submit(function () {
$('#btn-create-vm').prop('disabled', true);
$.ajax({
@@ -154,7 +183,7 @@ $(document).ready(function() {
}
});
return false;
- });
+ });*/
$('#createvm-modal').on('hidden.bs.modal', function () {
$(this).find('.modal-footer .btn').addClass('hide');
});
diff --git a/hosting/views.py b/hosting/views.py
index cc038d12..f18a22b7 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -694,16 +694,17 @@ class SettingsView(LoginRequiredMixin, FormView):
msg = _("Billing address updated successfully")
messages.add_message(request, messages.SUCCESS, msg)
else:
- token = form.cleaned_data.get('token')
+ # TODO : Test this flow
+ id_payment_method = form.cleaned_data.get('id_payment_method')
stripe_utils = StripeUtils()
- card_details = stripe_utils.get_cards_details_from_token(
- token
+ card_details = stripe_utils.get_cards_details_from_payment_method(
+ id_payment_method
)
if not card_details.get('response_object'):
form.add_error("__all__", card_details.get('error'))
return self.render_to_response(self.get_context_data())
stripe_customer = StripeCustomer.get_or_create(
- email=request.user.email, token=token
+ email=request.user.email, id_payment_method=id_payment_method
)
card = card_details['response_object']
if UserCardDetail.get_user_card_details(stripe_customer, card):
@@ -711,7 +712,7 @@ class SettingsView(LoginRequiredMixin, FormView):
messages.add_message(request, messages.ERROR, msg)
else:
acc_result = stripe_utils.associate_customer_card(
- request.user.stripecustomer.stripe_id, token
+ request.user.stripecustomer.stripe_id, id_payment_method
)
if acc_result['response_object'] is None:
msg = _(
@@ -1085,7 +1086,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView):
template, specs, stripe_customer_id, billing_address_data,
vm_template_id, stripe_api_cus_id)
)
- if 'token' in self.request.session:
+ if 'id_payment_method' in self.request.session:
card_details = stripe_utils.get_cards_details_from_token(
request.session['token']
)
@@ -1102,7 +1103,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView):
)
if not ucd:
acc_result = stripe_utils.associate_customer_card(
- stripe_api_cus_id, request.session['token'],
+ stripe_api_cus_id, request.session['id_payment_method'],
set_as_default=True
)
if acc_result['response_object'] is None:
diff --git a/membership/models.py b/membership/models.py
index 703b4800..079b60e0 100644
--- a/membership/models.py
+++ b/membership/models.py
@@ -296,7 +296,7 @@ class StripeCustomer(models.Model):
return None
@classmethod
- def get_or_create(cls, email=None, token=None):
+ def get_or_create(cls, email=None, token=None, id_payment_method=None):
"""
Check if there is a registered stripe customer with that email
or create a new one
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index bf731508..a4cc2c6a 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -83,12 +83,15 @@ class StripeUtils(object):
customer.save()
@handleStripeError
- def associate_customer_card(self, stripe_customer_id, token,
+ def associate_customer_card(self, stripe_customer_id, id_payment_method,
set_as_default=False):
customer = stripe.Customer.retrieve(stripe_customer_id)
- card = customer.sources.create(source=token)
+ stripe.PaymentMethod.attach(
+ id_payment_method,
+ customer=stripe_customer_id,
+ )
if set_as_default:
- customer.default_source = card.id
+ customer.invoice_settings.default_payment_method = id_payment_method
customer.save()
return True
@@ -100,6 +103,7 @@ class StripeUtils(object):
@handleStripeError
def update_customer_card(self, customer_id, token):
+ # TODO replace token with payment intent
customer = stripe.Customer.retrieve(customer_id)
current_card_token = customer.default_source
customer.sources.retrieve(current_card_token).delete()
@@ -188,6 +192,24 @@ class StripeUtils(object):
}
return card_details
+ @handleStripeError
+ def get_cards_details_from_payment_method(self, payment_method_id):
+ payment_method = stripe.PaymentMethod.retrieve(payment_method_id)
+ # payment_method does not always seem to have a card with id
+ # if that is the case, fallback to payment_method_id for card_id
+ card_id = payment_method_id
+ if hasattr(payment_method.card, 'id'):
+ card_id = payment_method.card.id
+ card_details = {
+ 'last4': payment_method.card.last4,
+ 'brand': payment_method.card.brand,
+ 'exp_month': payment_method.card.exp_month,
+ 'exp_year': payment_method.card.exp_year,
+ 'fingerprint': payment_method.card.fingerprint,
+ 'card_id': card_id
+ }
+ return card_details
+
def check_customer(self, stripe_cus_api_id, user, token):
try:
customer = stripe.Customer.retrieve(stripe_cus_api_id)
@@ -207,11 +229,11 @@ class StripeUtils(object):
return customer
@handleStripeError
- def create_customer(self, token, email, name=None):
+ def create_customer(self, id_payment_method, email, name=None):
if name is None or name.strip() == "":
name = email
customer = self.stripe.Customer.create(
- source=token,
+ payment_method=id_payment_method,
description=name,
email=email
)
@@ -494,19 +516,19 @@ class StripeUtils(object):
return tax_id_obj
@handleStripeError
- def get_payment_intent(self, amount):
- """
- Adds VM metadata to a subscription
- :param amount: the amount of payment_intent
- :return:
+ def get_payment_intent(self, amount, customer):
+ """ Create a stripe PaymentIntent of the given amount and return it
+ :param amount: the amount of payment_intent
+ :return:
"""
payment_intent_obj = stripe.PaymentIntent.create(
amount=amount,
- currency='chf'
+ currency='chf',
+ customer=customer
)
return payment_intent_obj
def compare_vat_numbers(self, vat1, vat2):
_vat1 = vat1.replace(" ", "").replace(".", "").replace("-","")
_vat2 = vat2.replace(" ", "").replace(".", "").replace("-","")
- return True if _vat1 == _vat2 else False
\ No newline at end of file
+ return True if _vat1 == _vat2 else False
From 42c9ec6f2839e3c1a96fc2a5a18b2b9498baa30d Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 10:32:25 +0530
Subject: [PATCH 155/217] Handle js success/error messages
---
.../datacenterlight/order_detail.html | 3 ++
datacenterlight/views.py | 8 +++++
.../hosting/js/virtual_machine_detail.js | 31 ++++++++++---------
3 files changed, 28 insertions(+), 14 deletions(-)
diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html
index 1914acb8..b96d5123 100644
--- a/datacenterlight/templates/datacenterlight/order_detail.html
+++ b/datacenterlight/templates/datacenterlight/order_detail.html
@@ -333,6 +333,9 @@
var error_url = '{{ error_msg.redirect }}';
var error_msg = '{{ error_msg.msg_body }}';
var error_title = '{{ error_msg.msg_title }}';
+ var success_msg = '{{ success_msg.msg_body }}';
+ var success_title = '{{ success_msg.msg_title }}';
+ var success_url = '{{ success_msg.redirect }}';
window.stripeKey = "{{stripe_key}}";
{%endblock%}
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index f50cf422..43cf7eaa 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -735,6 +735,14 @@ class OrderConfirmationView(DetailView, FormView):
),
'cms_integration': get_cms_integration('default'),
'error_msg': get_error_response_dict("Error", request),
+ 'success_msg': {
+ 'msg_title': _("Thank you !"),
+ 'msg_body': _("Your product will be provisioned as soon as "
+ "we receive the payment."),
+ 'redirect': reverse('hosting:invoices') if
+ request.user.is_authenticated() else
+ reverse('datacenterlight:index')
+ },
'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
})
return render(request, self.template_name, context)
diff --git a/hosting/static/hosting/js/virtual_machine_detail.js b/hosting/static/hosting/js/virtual_machine_detail.js
index e1bfd3a8..5e13519c 100644
--- a/hosting/static/hosting/js/virtual_machine_detail.js
+++ b/hosting/static/hosting/js/virtual_machine_detail.js
@@ -104,20 +104,23 @@ $(document).ready(function() {
}
).then(function(result) {
window.result = result;
- if (result.error) {
- // Display error.message in your UI.
- var errorElement = document.getElementById('card-errors');
- errorElement.textContent = result.error.message;
- } else {
- // The payment has succeeded
- // Display a success message
- alert("Thanks for the order. Your product will be provisioned " +
- "as soon as we receive the payment. Thank you.");
- modal_btn.attr('href', err).removeClass('hide');
- fa_icon.attr('class', 'checkmark');
- $('#createvm-modal-title').text(data.success.msg_title);
- $('#createvm-modal-body').html(data.success.msg_body);
- }
+ fa_icon = $('.modal-icon > .fa');
+ modal_btn = $('#createvm-modal-done-btn');
+ if (result.error) {
+ // Display error.message in your UI.
+ modal_btn.attr('href', error_url).removeClass('hide');
+ fa_icon.attr('class', 'fa fa-close');
+ modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide');
+ $('#createvm-modal-title').text(error_title);
+ $('#createvm-modal-body').html(result.error.message + " " + error_msg);
+ } else {
+ // The payment has succeeded
+ // Display a success message
+ modal_btn.attr('href', success_url).removeClass('hide');
+ fa_icon.attr('class', 'checkmark');
+ $('#createvm-modal-title').text(success_title);
+ $('#createvm-modal-body').html(success_msg);
+ }
});
}
/*
From ba92c8e416222a7fbdee0d9849dd225399861471 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 11:05:32 +0530
Subject: [PATCH 156/217] Do not pop billing address data from session in case
of a payment failure
Instead pop id_payment_method
---
datacenterlight/views.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 43cf7eaa..1e8b1472 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1,7 +1,7 @@
-import logging
import json
-import stripe
+import logging
+import stripe
from django import forms
from django.conf import settings
from django.contrib import messages
@@ -312,7 +312,7 @@ class PaymentOrderView(FormView):
request.session.pop('vat_validation_status')
request.session.pop('card_id')
request.session.pop('token')
- request.session.pop('billing_address_data')
+ request.session.pop('id_payment_method')
logger.debug("Session: %s" % str(request.session))
for key, value in request.session.items():
logger.debug("Session: %s %s" % (key, value))
From 41de724904ef7089255dbcf9690ed82339c5b9d9 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 15:24:47 +0530
Subject: [PATCH 157/217] Handle PaymentMethod type in set_default_card
---
hosting/models.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/hosting/models.py b/hosting/models.py
index ac44cc9e..fe0d824c 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -673,7 +673,11 @@ class UserCardDetail(AssignPermissionsMixin, models.Model):
stripe_utils = StripeUtils()
cus_response = stripe_utils.get_customer(stripe_api_cus_id)
cu = cus_response['response_object']
- cu.default_source = stripe_source_id
+ if stripe_source_id.startswith("pm"):
+ # card is a payment method
+ cu.invoice_settings.default_payment_method = stripe_source_id
+ else:
+ cu.default_source = stripe_source_id
cu.save()
UserCardDetail.save_default_card_local(
stripe_api_cus_id, stripe_source_id
From f628046417a02ab6214a06346e405fccb7659d9a Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 15:25:14 +0530
Subject: [PATCH 158/217] Add IncompletePaymentIntents model
---
hosting/models.py | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/hosting/models.py b/hosting/models.py
index fe0d824c..e95c5bb6 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -1,4 +1,3 @@
-import decimal
import json
import logging
import os
@@ -747,6 +746,19 @@ class StripeTaxRate(AssignPermissionsMixin, models.Model):
description = models.CharField(max_length=100)
+class IncompletePaymentIntents(AssignPermissionsMixin, models.Model):
+ completed_at = models.DateTimeField(null=True)
+ created_at = models.DateTimeField(auto_now_add=True)
+ payment_intent_id = models.CharField(max_length=100)
+ request = models.TextField()
+ stripe_api_cus_id = models.CharField(max_length=30)
+ card_details_response = models.TextField()
+ stripe_subscription_id = models.TextField()
+ stripe_charge_id = models.TextField()
+ gp_details = models.TextField()
+ billing_address_data = models.TextField()
+
+
class IncompleteSubscriptions(AssignPermissionsMixin, models.Model):
created_at = models.DateTimeField(auto_now_add=True)
completed_at = models.DateTimeField(null=True)
From 98b5d03d0b16f07d654ec81ef811b8bccaa6b10f Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 15:40:46 +0530
Subject: [PATCH 159/217] Refactor code for do_provisioning_generic
---
datacenterlight/views.py | 269 +++++++++++++++++++++++++++++++++------
1 file changed, 228 insertions(+), 41 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 1e8b1472..058fa96b 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -20,7 +20,7 @@ from hosting.forms import (
)
from hosting.models import (
HostingBill, HostingOrder, UserCardDetail, GenericProduct, UserHostingKey,
- StripeTaxRate, IncompleteSubscriptions)
+ StripeTaxRate, IncompleteSubscriptions, IncompletePaymentIntents)
from membership.models import CustomUser, StripeCustomer
from opennebula_api.serializers import VMTemplateSerializer
from utils.forms import (
@@ -745,6 +745,16 @@ class OrderConfirmationView(DetailView, FormView):
},
'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
})
+ IncompletePaymentIntents.objects.create(
+ request=create_incomplete_intent_request(self.request),
+ payment_intent_id=payment_intent.id,
+ stripe_api_cus_id=request.session['customer'],
+ card_details_response=card_details_response,
+ stripe_subscription_id=None,
+ stripe_charge_id=None,
+ gp_details=request.session["generic_payment_details"],
+ billing_address_data=request.session["billing_address_data"]
+ )
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
@@ -1113,46 +1123,36 @@ class OrderConfirmationView(DetailView, FormView):
return JsonResponse(response)
-def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
- stripe_subscription_obj, stripe_onetime_charge, gp_details,
- specs, vm_template_id, template, billing_address_data,
- real_request):
+def create_incomplete_intent_request(request):
"""
- :param request: a dict
- {
- 'scheme': 'https',
- 'host': 'domain',
- 'language': 'en-us',
- 'new_user_hosting_key_id': 1,
- 'card_id': 1, # if usercarddetail exists already,
- 'generic_payment_type': 'generic' # represents a generic payment
- 'generic_payment_details': {
- 'amount': 100,
- 'recurring':
- },
- }
- :param user: a dict
- {
- 'name': 'John Doe',
- 'email': 'john@doe.com'
- }
- :param stripe_api_cus_id: 'cus_xxxxxxx' the actual stripe customer id str
- :param card_details_response:
- :param stripe_subscription_obj: The actual Stripe's Subscription Object
- :param stripe_onetime_charge: Stripe's Charge object
- :param gp_details:
- :param specs:
- :param vm_template_id:
- :param template:
- :param real_request:
+ Persist session variables so that they could be pick up
+ in the webhook for processing.
+ :param request:
:return:
"""
- # Create user if the user is not logged in and if he is not already
- # registered
+ req = {
+ 'scheme': request.scheme,
+ 'host': request.get_host(),
+ 'language': get_language(),
+ 'new_user_hosting_key_id': request.session.get(
+ 'new_user_hosting_key_id', None),
+ 'card_id': request.session.get('card_id', None),
+ 'generic_payment_type': request.session.get(
+ 'generic_payment_type', None),
+ 'generic_payment_details': request.session.get(
+ 'generic_payment_details', None),
+ 'user': request.session.get('user', None)
+ }
+ return json.dumps(req)
+
+
+def get_or_create_custom_user(request, stripe_api_cus_id):
new_user = None
+ name = request.get('user').get('name')
+ email = request.get('user').get('email')
+
try:
- custom_user = CustomUser.objects.get(
- email=user.get('email'))
+ custom_user = CustomUser.objects.get(email=email)
stripe_customer = StripeCustomer.objects.filter(
user_id=custom_user.id).first()
if stripe_customer is None:
@@ -1162,17 +1162,17 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
stripe_customer_id = stripe_customer.id
except CustomUser.DoesNotExist:
logger.debug(
- "Customer {} does not exist.".format(user.get('email')))
+ "Customer {} does not exist.".format(email))
password = CustomUser.get_random_password()
base_url = "{0}://{1}".format(request['scheme'],
request['host'])
custom_user = CustomUser.register(
- user.get('name'), password,
- user.get('email'),
+ name, password,
+ email,
app='dcl', base_url=base_url, send_email=True,
account_details=password
)
- logger.debug("Created user {}.".format(user.get('email')))
+ logger.debug("Created user {}.".format(email))
stripe_customer = StripeCustomer.objects. \
create(user=custom_user, stripe_id=stripe_api_cus_id)
stripe_customer_id = stripe_customer.id
@@ -1186,8 +1186,11 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
user_hosting_key.user = new_user
user_hosting_key.save()
logger.debug("User %s key is saved" % custom_user.email)
+ return custom_user, new_user
- card_id = request.get('card_id', None)
+
+def set_user_card(card_id, stripe_api_cus_id, custom_user,
+ card_details_response):
if card_id:
logger.debug("card_id %s was in request" % card_id)
user_card_detail = UserCardDetail.objects.get(id=card_id)
@@ -1217,6 +1220,190 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
'brand': ucd.brand,
'card_id': ucd.card_id
}
+ return card_details_dict
+
+
+def do_provisioning_generic(
+ request, stripe_api_cus_id, card_details_response,
+ stripe_subscription_id, stripe_charge_id, gp_details,
+ billing_address_data):
+ user = request.get('user', None)
+ logger.debug("generic_payment_type case")
+ custom_user, new_user = get_or_create_custom_user(
+ request, stripe_api_cus_id)
+
+ card_id = request.get('card_id', None)
+
+ card_details_dict = set_user_card(card_id, stripe_api_cus_id, custom_user,
+ card_details_response)
+
+ # Save billing address
+ billing_address_data.update({
+ 'user': custom_user.id
+ })
+ logger.debug('billing_address_data is {}'.format(billing_address_data))
+
+ stripe_cus = StripeCustomer.objects.filter(
+ stripe_id=stripe_api_cus_id
+ ).first()
+ billing_address = BillingAddress(
+ cardholder_name=billing_address_data['cardholder_name'],
+ street_address=billing_address_data['street_address'],
+ city=billing_address_data['city'],
+ postal_code=billing_address_data['postal_code'],
+ country=billing_address_data['country'],
+ vat_number=billing_address_data['vat_number']
+ )
+ billing_address.save()
+
+ order = HostingOrder.create(
+ price=request['generic_payment_details']['amount'],
+ customer=stripe_cus,
+ billing_address=billing_address,
+ vm_pricing=VMPricing.get_default_pricing()
+ )
+
+ # Create a Hosting Bill
+ HostingBill.create(customer=stripe_cus,
+ billing_address=billing_address)
+
+ # Create Billing Address for User if he does not have one
+ if not stripe_cus.user.billing_addresses.count():
+ billing_address_data.update({
+ 'user': stripe_cus.user.id
+ })
+ billing_address_user_form = UserBillingAddressForm(
+ billing_address_data
+ )
+ billing_address_user_form.is_valid()
+ billing_address_user_form.save()
+
+ recurring = request['generic_payment_details'].get('recurring')
+ if recurring:
+ logger.debug("recurring case")
+ # Associate the given stripe subscription with the order
+ order.set_subscription_id(
+ stripe_subscription_id, card_details_dict
+ )
+ logger.debug("recurring case, set order subscription id done")
+ else:
+ logger.debug("one time charge case")
+ # Associate the given stripe charge id with the order
+ stripe_onetime_charge = stripe.Charge.retrieve(stripe_charge_id)
+ order.set_stripe_charge(stripe_onetime_charge)
+
+ # Set order status approved
+ order.set_approved()
+ order.generic_payment_description = gp_details["description"]
+ order.generic_product_id = gp_details["product_id"]
+ order.save()
+ logger.debug("Order saved")
+ # send emails
+ context = {
+ 'name': user.get('name'),
+ 'email': user.get('email'),
+ 'amount': gp_details['amount'],
+ 'description': gp_details['description'],
+ 'recurring': gp_details['recurring'],
+ 'product_name': gp_details['product_name'],
+ 'product_id': gp_details['product_id'],
+ 'order_id': order.id
+ }
+
+ email_data = {
+ 'subject': (settings.DCL_TEXT +
+ " Payment received from %s" % context['email']),
+ 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
+ 'to': ['info@ungleich.ch'],
+ 'body': "\n".join(
+ ["%s=%s" % (k, v) for (k, v) in context.items()]),
+ 'reply_to': [context['email']],
+ }
+ send_plain_email_task.delay(email_data)
+ recurring_text = _(" This is a monthly recurring plan.")
+ if gp_details['recurring_interval'] == "year":
+ recurring_text = _(" This is an yearly recurring plan.")
+
+ email_data = {
+ 'subject': _("Confirmation of your payment"),
+ 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
+ 'to': [user.get('email')],
+ 'body': _("Hi {name},\n\n"
+ "thank you for your order!\n"
+ "We have just received a payment of CHF {amount:.2f}"
+ " from you.{recurring}\n\n"
+ "Cheers,\nYour Data Center Light team".format(
+ name=user.get('name'),
+ amount=gp_details['amount'],
+ recurring=(
+ recurring_text
+ if gp_details['recurring'] else ''
+ )
+ )
+ ),
+ 'reply_to': ['info@ungleich.ch'],
+ }
+ send_plain_email_task.delay(email_data)
+ redirect_url = reverse('datacenterlight:index')
+ logger.debug("Sent user/admin emails")
+ logger.debug("redirect_url = %s " % redirect_url)
+ response = {
+ 'status': True,
+ 'redirect': redirect_url,
+ 'msg_title': str(_('Thank you for the payment.')),
+ 'msg_body': str(
+ _('You will soon receive a confirmation email of the '
+ 'payment. You can always contact us at '
+ 'info@ungleich.ch for any question that you may have.')
+ )
+ }
+ logger.debug("after response")
+ logger.debug(str(response))
+ return {'response': JsonResponse(response), 'user': new_user}
+
+
+def do_provisioning(request, stripe_api_cus_id, card_details_response,
+ stripe_subscription_obj, stripe_onetime_charge, gp_details,
+ specs, vm_template_id, template, billing_address_data,
+ real_request):
+ """
+ :param request: a dict
+ {
+ 'scheme': 'https',
+ 'host': 'domain',
+ 'language': 'en-us',
+ 'new_user_hosting_key_id': 1,
+ 'card_id': 1, # if usercarddetail exists already,
+ 'generic_payment_type': 'generic' # represents a generic payment
+ 'generic_payment_details': {
+ 'amount': 100,
+ 'recurring':
+ },
+ 'user': {
+ 'name': 'John Doe',
+ 'email': 'john@doe.com'
+ }
+ }
+ :param stripe_api_cus_id: 'cus_xxxxxxx' the actual stripe customer id str
+ :param card_details_response:
+ :param stripe_subscription_obj: The actual Stripe's Subscription Object
+ :param stripe_onetime_charge: Stripe's Charge object
+ :param gp_details:
+ :param specs:
+ :param vm_template_id:
+ :param template:
+ :param real_request:
+ :return:
+ """
+ # Create user if the user is not logged in and if he is not already
+ # registered
+ custom_user, new_user = get_or_create_custom_user(
+ request, stripe_api_cus_id)
+
+ card_id = request.get('card_id', None)
+
+ card_details_dict = set_user_card(card_id, stripe_api_cus_id, custom_user,
+ card_details_response)
# Save billing address
billing_address_data.update({
From 9077eb0cf2b11f901e1d4dc03a39feb6deee283d Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 15:41:43 +0530
Subject: [PATCH 160/217] Implement webhook
---
webhook/views.py | 61 ++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 57 insertions(+), 4 deletions(-)
diff --git a/webhook/views.py b/webhook/views.py
index 416d07b7..970f0d33 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -9,9 +9,9 @@ from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
-from datacenterlight.views import do_provisioning
+from datacenterlight.views import do_provisioning, do_provisioning_generic
from membership.models import StripeCustomer
-from hosting.models import IncompleteSubscriptions
+from hosting.models import IncompleteSubscriptions, IncompletePaymentIntents
from utils.models import BillingAddress, UserBillingAddress
from utils.tasks import send_plain_email_task
@@ -135,6 +135,8 @@ def handle_webhook(request):
if (invoice_obj.paid and
invoice_obj.billing_reason == "subscription_update"):
+ logger.debug("""invoice_obj.paid and
+ invoice_obj.billing_reason == subscription_update""")
logger.debug("Start provisioning")
try:
stripe_subscription_obj = stripe.Subscription.retrieve(
@@ -182,8 +184,6 @@ def handle_webhook(request):
logger.debug("6*******")
do_provisioning(
request=request,
- user={'name': incomplete_sub.name,
- 'email': incomplete_sub.email},
stripe_api_cus_id=incomplete_sub.stripe_api_cus_id,
card_details_response=card_details_response,
stripe_subscription_obj=stripe_subscription_obj,
@@ -225,6 +225,59 @@ def handle_webhook(request):
payment_intent_obj = event.data.object
logger.debug("Webhook Event: payment_intent.succeeded")
logger.debug("payment_intent_obj %s " % str(payment_intent_obj))
+
+ try:
+ incomplete_pm = IncompletePaymentIntents.objects.get(
+ payment_intent_obj.id)
+ request = ""
+ soc = ""
+ card_details_response = ""
+ gp_details = ""
+ template = ""
+ billing_address_data = ""
+ if incomplete_pm.request:
+ request = json.loads(incomplete_pm.request)
+ if incomplete_pm.stripe_onetime_charge:
+ soc = json.loads(incomplete_pm.stripe_onetime_charge)
+ if incomplete_pm.gp_details:
+ gp_details = json.loads(incomplete_pm.gp_details)
+ if incomplete_pm.card_details_response:
+ card_details_response = json.loads(
+ incomplete_pm.card_details_response)
+ if incomplete_pm.billing_address_data:
+ billing_address_data = json.loads(
+ incomplete_pm.billing_address_data)
+ logger.debug("1*******")
+ logger.debug(request)
+ logger.debug("2*******")
+ logger.debug(card_details_response)
+ logger.debug("3*******")
+ logger.debug(soc)
+ logger.debug("4*******")
+ logger.debug(gp_details)
+ logger.debug("5*******")
+ logger.debug(template)
+ logger.debug("6*******")
+ logger.debug(billing_address_data)
+ do_provisioning_generic(
+ request=request,
+ stripe_api_cus_id=incomplete_pm.stripe_api_cus_id,
+ card_details_response=card_details_response,
+ stripe_subscription_id=None,
+ stripe_charge_id=soc,
+ gp_details=gp_details,
+ billing_address_data=billing_address_data
+ )
+ except (IncompletePaymentIntents.DoesNotExist,
+ IncompletePaymentIntents.MultipleObjectsReturned) as ex:
+ logger.error(str(ex))
+ email_data = {
+ 'subject': "IncompletePaymentIntents 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:
logger.error("Unhandled event : " + event.type)
return HttpResponse(status=200)
From 9ae4b969688d650c0e14d63fb1f5d6cdd0719a99 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 15:43:46 +0530
Subject: [PATCH 161/217] Add
hosting/migrations/0064_incompletepaymentintents.py
---
.../0064_incompletepaymentintents.py | 33 +++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 hosting/migrations/0064_incompletepaymentintents.py
diff --git a/hosting/migrations/0064_incompletepaymentintents.py b/hosting/migrations/0064_incompletepaymentintents.py
new file mode 100644
index 00000000..868e053e
--- /dev/null
+++ b/hosting/migrations/0064_incompletepaymentintents.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2020-12-31 10:13
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import utils.mixins
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('hosting', '0063_auto_20201223_0612'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='IncompletePaymentIntents',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('completed_at', models.DateTimeField(null=True)),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('payment_intent_id', models.CharField(max_length=100)),
+ ('request', models.TextField()),
+ ('stripe_api_cus_id', models.CharField(max_length=30)),
+ ('card_details_response', models.TextField()),
+ ('stripe_subscription_id', models.TextField()),
+ ('stripe_charge_id', models.TextField()),
+ ('gp_details', models.TextField()),
+ ('billing_address_data', models.TextField()),
+ ],
+ bases=(utils.mixins.AssignPermissionsMixin, models.Model),
+ ),
+ ]
From b2f0a456790b7158ca3eed4a442225a9cc616fce Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 15:56:08 +0530
Subject: [PATCH 162/217] Add logger message
---
datacenterlight/views.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 058fa96b..afe5d0ae 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -745,6 +745,9 @@ class OrderConfirmationView(DetailView, FormView):
},
'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
})
+ logger.debug("Request %s" % create_incomplete_intent_request(
+ self.request))
+ logger.debug("%s" % str(payment_intent))
IncompletePaymentIntents.objects.create(
request=create_incomplete_intent_request(self.request),
payment_intent_id=payment_intent.id,
@@ -755,6 +758,7 @@ class OrderConfirmationView(DetailView, FormView):
gp_details=request.session["generic_payment_details"],
billing_address_data=request.session["billing_address_data"]
)
+ logger.debug("IncompletePaymentIntent done")
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
From 87b85c43b46c01685dc9dc4c9f50a5684d4b3ca0 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 15:59:09 +0530
Subject: [PATCH 163/217] More logger
---
datacenterlight/views.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index afe5d0ae..fe98645a 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -748,6 +748,10 @@ class OrderConfirmationView(DetailView, FormView):
logger.debug("Request %s" % create_incomplete_intent_request(
self.request))
logger.debug("%s" % str(payment_intent))
+ logger.debug("customer %s" % request.session['customer'])
+ logger.debug("card_details_response %s" % card_details_response)
+ logger.debug("request.session[generic_payment_details] %s" % request.session["generic_payment_details"])
+ logger.debug("request.session[billing_address_data] %s" % request.session["billing_address_data"])
IncompletePaymentIntents.objects.create(
request=create_incomplete_intent_request(self.request),
payment_intent_id=payment_intent.id,
From 7db0594778b07141db63e1f0a7f0780446c9d0cb Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 16:12:22 +0530
Subject: [PATCH 164/217] Update IncompletePaymentIntents to allow null
subscription id and charge id
---
hosting/migrations/0065_auto_20201231_1041.py | 25 +++++++++++++++++++
hosting/models.py | 4 +--
2 files changed, 27 insertions(+), 2 deletions(-)
create mode 100644 hosting/migrations/0065_auto_20201231_1041.py
diff --git a/hosting/migrations/0065_auto_20201231_1041.py b/hosting/migrations/0065_auto_20201231_1041.py
new file mode 100644
index 00000000..936ccab1
--- /dev/null
+++ b/hosting/migrations/0065_auto_20201231_1041.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2020-12-31 10:41
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('hosting', '0064_incompletepaymentintents'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='incompletepaymentintents',
+ name='stripe_charge_id',
+ field=models.CharField(max_length=100, null=True),
+ ),
+ migrations.AlterField(
+ model_name='incompletepaymentintents',
+ name='stripe_subscription_id',
+ field=models.CharField(max_length=100, null=True),
+ ),
+ ]
diff --git a/hosting/models.py b/hosting/models.py
index e95c5bb6..c14d18bf 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -753,8 +753,8 @@ class IncompletePaymentIntents(AssignPermissionsMixin, models.Model):
request = models.TextField()
stripe_api_cus_id = models.CharField(max_length=30)
card_details_response = models.TextField()
- stripe_subscription_id = models.TextField()
- stripe_charge_id = models.TextField()
+ stripe_subscription_id = models.CharField(max_length=100, null=True)
+ stripe_charge_id = models.CharField(max_length=100, null=True)
gp_details = models.TextField()
billing_address_data = models.TextField()
From 12c9140b3a4001101a8ef989980b30098b2ba3a8 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 16:23:44 +0530
Subject: [PATCH 165/217] Fix using correct payment intent id
---
webhook/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/webhook/views.py b/webhook/views.py
index 970f0d33..07bf34ad 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -228,7 +228,7 @@ def handle_webhook(request):
try:
incomplete_pm = IncompletePaymentIntents.objects.get(
- payment_intent_obj.id)
+ payment_intent_id=payment_intent_obj.id)
request = ""
soc = ""
card_details_response = ""
From 52d1fb6a0eb402a00c3536446dbf4c7f713516de Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 16:27:12 +0530
Subject: [PATCH 166/217] Add logger + return 200 on success of webhook
---
datacenterlight/views.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index fe98645a..5b67d87c 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -8,7 +8,8 @@ from django.contrib import messages
from django.contrib.auth import login, authenticate
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
-from django.http import HttpResponseRedirect, JsonResponse, Http404
+from django.http import HttpResponseRedirect, JsonResponse, Http404, \
+ HttpResponse
from django.shortcuts import render
from django.utils.translation import get_language, ugettext_lazy as _
from django.views.decorators.cache import cache_control
@@ -1239,12 +1240,15 @@ def do_provisioning_generic(
logger.debug("generic_payment_type case")
custom_user, new_user = get_or_create_custom_user(
request, stripe_api_cus_id)
+ logger.debug("%s %s" % (custom_user.email, custom_user.id))
card_id = request.get('card_id', None)
card_details_dict = set_user_card(card_id, stripe_api_cus_id, custom_user,
card_details_response)
+ logger.debug("After card details dict %s" % str(card_details_dict))
+
# Save billing address
billing_address_data.update({
'user': custom_user.id
@@ -1367,7 +1371,7 @@ def do_provisioning_generic(
}
logger.debug("after response")
logger.debug(str(response))
- return {'response': JsonResponse(response), 'user': new_user}
+ return HttpResponse(status=200)
def do_provisioning(request, stripe_api_cus_id, card_details_response,
From f48a5cfe71d40cfa9120258daaf34f3eca126856 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 16:29:18 +0530
Subject: [PATCH 167/217] Set completed_at value
---
webhook/views.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/webhook/views.py b/webhook/views.py
index 07bf34ad..27b12b05 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -268,6 +268,7 @@ def handle_webhook(request):
gp_details=gp_details,
billing_address_data=billing_address_data
)
+ incomplete_pm.completed_at = datetime.datetime.now()
except (IncompletePaymentIntents.DoesNotExist,
IncompletePaymentIntents.MultipleObjectsReturned) as ex:
logger.error(str(ex))
From 85757e01c92cd52537e32ad184c5f467aaa683ba Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 16:37:25 +0530
Subject: [PATCH 168/217] Save charge id
---
webhook/views.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/webhook/views.py b/webhook/views.py
index 27b12b05..3c5adfef 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -269,6 +269,15 @@ def handle_webhook(request):
billing_address_data=billing_address_data
)
incomplete_pm.completed_at = datetime.datetime.now()
+ charges = ""
+ if len(payment_intent_obj.charges.data) > 0:
+ for d in payment_intent_obj.charges.data:
+ if charges == "":
+ charges = "%s" % d.id
+ else:
+ charges = "%s,%s" % (charges, d.id)
+ incomplete_pm.stripe_charge_id=charges
+ incomplete_pm.save()
except (IncompletePaymentIntents.DoesNotExist,
IncompletePaymentIntents.MultipleObjectsReturned) as ex:
logger.error(str(ex))
From 8e4b3ce96b0667be8e3e83d617ddea7d044774de Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 16:39:25 +0530
Subject: [PATCH 169/217] Store charge id from payement intent result
---
webhook/views.py | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/webhook/views.py b/webhook/views.py
index 3c5adfef..bdcf1654 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -259,15 +259,6 @@ def handle_webhook(request):
logger.debug(template)
logger.debug("6*******")
logger.debug(billing_address_data)
- do_provisioning_generic(
- request=request,
- stripe_api_cus_id=incomplete_pm.stripe_api_cus_id,
- card_details_response=card_details_response,
- stripe_subscription_id=None,
- stripe_charge_id=soc,
- gp_details=gp_details,
- billing_address_data=billing_address_data
- )
incomplete_pm.completed_at = datetime.datetime.now()
charges = ""
if len(payment_intent_obj.charges.data) > 0:
@@ -276,7 +267,17 @@ def handle_webhook(request):
charges = "%s" % d.id
else:
charges = "%s,%s" % (charges, d.id)
+ logger.debug("Charge ids = %s" % charges)
incomplete_pm.stripe_charge_id=charges
+ do_provisioning_generic(
+ request=request,
+ stripe_api_cus_id=incomplete_pm.stripe_api_cus_id,
+ card_details_response=card_details_response,
+ stripe_subscription_id=None,
+ stripe_charge_id=charges,
+ gp_details=gp_details,
+ billing_address_data=billing_address_data
+ )
incomplete_pm.save()
except (IncompletePaymentIntents.DoesNotExist,
IncompletePaymentIntents.MultipleObjectsReturned) as ex:
From 8827bd15bac9b6c9d282004ea8501d3675314791 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 16:47:45 +0530
Subject: [PATCH 170/217] More loggers
---
webhook/views.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/webhook/views.py b/webhook/views.py
index bdcf1654..8dc1d909 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -225,8 +225,9 @@ def handle_webhook(request):
payment_intent_obj = event.data.object
logger.debug("Webhook Event: payment_intent.succeeded")
logger.debug("payment_intent_obj %s " % str(payment_intent_obj))
-
try:
+ logger.debug("Looking for IncompletePaymentIntents %s " %
+ payment_intent_obj.id)
incomplete_pm = IncompletePaymentIntents.objects.get(
payment_intent_id=payment_intent_obj.id)
request = ""
@@ -282,6 +283,7 @@ def handle_webhook(request):
except (IncompletePaymentIntents.DoesNotExist,
IncompletePaymentIntents.MultipleObjectsReturned) as ex:
logger.error(str(ex))
+ logger.debug(str(ex))
email_data = {
'subject': "IncompletePaymentIntents error",
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
From 37f82a48d581a9f949a51316d14b983ed24de35d Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 16:55:05 +0530
Subject: [PATCH 171/217] More loggers
---
webhook/views.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/webhook/views.py b/webhook/views.py
index 8dc1d909..420e718c 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -230,6 +230,7 @@ def handle_webhook(request):
payment_intent_obj.id)
incomplete_pm = IncompletePaymentIntents.objects.get(
payment_intent_id=payment_intent_obj.id)
+ logger.debug("incomplete_pm = %s" % str(incomplete_pm))
request = ""
soc = ""
card_details_response = ""
@@ -238,16 +239,21 @@ def handle_webhook(request):
billing_address_data = ""
if incomplete_pm.request:
request = json.loads(incomplete_pm.request)
+ logger.debug("request = %s" % str(request))
if incomplete_pm.stripe_onetime_charge:
soc = json.loads(incomplete_pm.stripe_onetime_charge)
+ logger.debug("stripe_onetime_charge = %s" % str(soc))
if incomplete_pm.gp_details:
gp_details = json.loads(incomplete_pm.gp_details)
+ logger.debug("gp_details = %s" % str(gp_details))
if incomplete_pm.card_details_response:
card_details_response = json.loads(
incomplete_pm.card_details_response)
+ logger.debug("card_details_response = %s" % str(card_details_response))
if incomplete_pm.billing_address_data:
billing_address_data = json.loads(
incomplete_pm.billing_address_data)
+ logger.debug("billing_address_data = %s" % str(billing_address_data))
logger.debug("1*******")
logger.debug(request)
logger.debug("2*******")
From ff7b20b0dc1fbb8adcbcce59c3d70d88ff2cbccc Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 16:59:22 +0530
Subject: [PATCH 172/217] Stripe id is not a dict
---
webhook/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/webhook/views.py b/webhook/views.py
index 420e718c..7e297944 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -241,7 +241,7 @@ def handle_webhook(request):
request = json.loads(incomplete_pm.request)
logger.debug("request = %s" % str(request))
if incomplete_pm.stripe_onetime_charge:
- soc = json.loads(incomplete_pm.stripe_onetime_charge)
+ soc = incomplete_pm.stripe_onetime_charge
logger.debug("stripe_onetime_charge = %s" % str(soc))
if incomplete_pm.gp_details:
gp_details = json.loads(incomplete_pm.gp_details)
From 7309b8416ce934ce7be9439f62be35cf7b6bdef9 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 17:02:03 +0530
Subject: [PATCH 173/217] Handle any exception
---
webhook/views.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/webhook/views.py b/webhook/views.py
index 7e297944..318b9b35 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -287,7 +287,8 @@ def handle_webhook(request):
)
incomplete_pm.save()
except (IncompletePaymentIntents.DoesNotExist,
- IncompletePaymentIntents.MultipleObjectsReturned) as ex:
+ IncompletePaymentIntents.MultipleObjectsReturned,
+ Exception) as ex:
logger.error(str(ex))
logger.debug(str(ex))
email_data = {
From 33f741424d6a19f51b43ff2e0a2e6bc23d6f15b0 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 17:05:01 +0530
Subject: [PATCH 174/217] See inner values of incomplete_pm
---
webhook/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/webhook/views.py b/webhook/views.py
index 318b9b35..f53e7af7 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -230,7 +230,7 @@ def handle_webhook(request):
payment_intent_obj.id)
incomplete_pm = IncompletePaymentIntents.objects.get(
payment_intent_id=payment_intent_obj.id)
- logger.debug("incomplete_pm = %s" % str(incomplete_pm))
+ logger.debug("incomplete_pm = %s" % str(incomplete_pm.__dict__))
request = ""
soc = ""
card_details_response = ""
From 213b9a068e0308f06fc6fe8b9d58ef412501ee79 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 17:07:51 +0530
Subject: [PATCH 175/217] Fix attribute name
---
webhook/views.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/webhook/views.py b/webhook/views.py
index f53e7af7..5760b0dd 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -240,8 +240,8 @@ def handle_webhook(request):
if incomplete_pm.request:
request = json.loads(incomplete_pm.request)
logger.debug("request = %s" % str(request))
- if incomplete_pm.stripe_onetime_charge:
- soc = incomplete_pm.stripe_onetime_charge
+ if incomplete_pm.stripe_charge_id:
+ soc = incomplete_pm.stripe_charge_id
logger.debug("stripe_onetime_charge = %s" % str(soc))
if incomplete_pm.gp_details:
gp_details = json.loads(incomplete_pm.gp_details)
From 2f98294eab6a1c52013f2f8cd7071581d03a4f7e Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 17:12:17 +0530
Subject: [PATCH 176/217] Store dicts as json in db
---
datacenterlight/views.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 5b67d87c..44e871a5 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -757,11 +757,11 @@ class OrderConfirmationView(DetailView, FormView):
request=create_incomplete_intent_request(self.request),
payment_intent_id=payment_intent.id,
stripe_api_cus_id=request.session['customer'],
- card_details_response=card_details_response,
+ card_details_response=json.dumps(card_details_response),
stripe_subscription_id=None,
stripe_charge_id=None,
- gp_details=request.session["generic_payment_details"],
- billing_address_data=request.session["billing_address_data"]
+ gp_details=json.dumps(request.session["generic_payment_details"]),
+ billing_address_data=json.dumps(request.session["billing_address_data"])
)
logger.debug("IncompletePaymentIntent done")
return render(request, self.template_name, context)
From 13f5f576b50776548a93533579fb6a92914e1bc5 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 17:23:43 +0530
Subject: [PATCH 177/217] Fix getting cc details from payment_methods
---
hosting/models.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/hosting/models.py b/hosting/models.py
index c14d18bf..48238afe 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -169,8 +169,12 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
def set_stripe_charge(self, stripe_charge):
self.stripe_charge_id = stripe_charge.id
- self.last4 = stripe_charge.source.last4
- self.cc_brand = stripe_charge.source.brand
+ if stripe_charge.source is None:
+ self.last4 = stripe_charge.payment_method_details.card.last4
+ self.cc_brand = stripe_charge.payment_method_details.card.brand
+ else:
+ self.last4 = stripe_charge.source.last4
+ self.cc_brand = stripe_charge.source.brand
self.save()
def set_subscription_id(self, subscription_id, cc_details):
From a823efd8e28376f9651c10746929616af46601c4 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 20:01:52 +0530
Subject: [PATCH 178/217] Add get_available_payment_methods
---
datacenterlight/views.py | 6 ++++--
utils/stripe_utils.py | 28 ++++++++++++++++++++++++++++
2 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 44e871a5..3b05bd73 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -263,9 +263,11 @@ class PaymentOrderView(FormView):
stripe_customer = user.stripecustomer
else:
stripe_customer = None
- cards_list = UserCardDetail.get_all_cards_list(
- stripe_customer=stripe_customer
+ stripe_utils = StripeUtils()
+ cards_list_request = stripe_utils.get_available_payment_methods(
+ stripe_customer
)
+ cards_list = cards_list_request.get('response_object')
context.update({'cards_list': cards_list})
else:
billing_address_form = BillingAddressFormSignup(
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index a4cc2c6a..e7e8d9af 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -528,6 +528,34 @@ class StripeUtils(object):
)
return payment_intent_obj
+ @handleStripeError
+ def get_available_payment_methods(self, customer):
+ """ Retrieves all payment methods of the given customer
+ :param customer: StripeCustomer object
+ :return: a list of available payment methods
+ """
+ return_list = []
+ if customer is None:
+ return return_list
+ cu = stripe.Customer.retrieve(customer.stripe_id)
+ pms = stripe.PaymentMethod.list(
+ customer=customer.stripe_id,
+ type="card",
+ )
+ default_source = None
+ if cu.default_source:
+ default_source = cu.default_source
+ else:
+ default_source = cu.invoice_settings.default_payment_method
+ for pm in pms.data:
+ return_list.append({
+ 'last4': pm.card.last4, 'brand': pm.card.brand, 'id': pm.id,
+ 'exp_year': pm.card.exp_year,
+ 'exp_month': '{:02d}'.format(pm.card.exp_month),
+ 'preferred': pm.id == default_source
+ })
+ return return_list
+
def compare_vat_numbers(self, vat1, vat2):
_vat1 = vat1.replace(" ", "").replace(".", "").replace("-","")
_vat2 = vat2.replace(" ", "").replace(".", "").replace("-","")
From b36afcb82818c7e0a54c4ffaa5a9af34cc7f7184 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 20:35:31 +0530
Subject: [PATCH 179/217] Simplify code for logged in one-time payments with
SCA
---
datacenterlight/views.py | 35 +++++------------------------------
1 file changed, 5 insertions(+), 30 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 3b05bd73..94d67b27 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -469,36 +469,11 @@ class PaymentOrderView(FormView):
return self.render_to_response(context)
id_payment_method = self.request.POST.get('id_payment_method',
None)
- if id_payment_method is None:
- card_id = address_form.cleaned_data.get('card')
- logger.debug("token is empty and card_id is %s" % card_id)
- try:
- user_card_detail = UserCardDetail.objects.get(id=card_id)
- if not request.user.has_perm(
- 'view_usercarddetail', user_card_detail
- ):
- raise UserCardDetail.DoesNotExist(
- _("{user} does not have permission to access the "
- "card").format(user=request.user.email)
- )
- except UserCardDetail.DoesNotExist as e:
- ex = str(e)
- logger.error("Card Id: {card_id}, Exception: {ex}".format(
- card_id=card_id, ex=ex
- )
- )
- msg = _("An error occurred. Details: {}".format(ex))
- messages.add_message(
- self.request, messages.ERROR, msg,
- extra_tags='make_charge_error'
- )
- return HttpResponseRedirect(
- reverse('datacenterlight:payment') + '#payment_error'
- )
- request.session['card_id'] = user_card_detail.id
- else:
- request.session["id_payment_method"] = id_payment_method
- logger.debug("id_payment_method is %s" % id_payment_method)
+ if id_payment_method == 'undefined':
+ # Probably user chose one of the previously saved cards
+ id_payment_method = address_form.cleaned_data.get('card')
+ request.session["id_payment_method"] = id_payment_method
+ logger.debug("id_payment_method is %s" % id_payment_method)
if request.user.is_authenticated():
this_user = {
'email': request.user.email,
From d2ebd3c473d9daa4670cf1f55a74e6e3868ea9f1 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 20:53:02 +0530
Subject: [PATCH 180/217] Do card association
---
datacenterlight/views.py | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 94d67b27..fcc423bd 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1127,7 +1127,8 @@ def create_incomplete_intent_request(request):
'generic_payment_type', None),
'generic_payment_details': request.session.get(
'generic_payment_details', None),
- 'user': request.session.get('user', None)
+ 'user': request.session.get('user', None),
+ 'id_payment_method': request.session.get('id_payment_method', None),
}
return json.dumps(req)
@@ -1213,6 +1214,15 @@ def do_provisioning_generic(
request, stripe_api_cus_id, card_details_response,
stripe_subscription_id, stripe_charge_id, gp_details,
billing_address_data):
+ stripe_utils = StripeUtils()
+ acc_result = stripe_utils.associate_customer_card(
+ stripe_api_cus_id, request['id_payment_method'],
+ set_as_default=True
+ )
+ logger.debug("Card %s associate result %s" % (
+ request['id_payment_method'],
+ acc_result.get('response_object')
+ ))
user = request.get('user', None)
logger.debug("generic_payment_type case")
custom_user, new_user = get_or_create_custom_user(
From 8c72b56f6caf86d6ed67fc12c50f1d8adbdaa278 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 21:03:24 +0530
Subject: [PATCH 181/217] Use setup_future_usage='off_session'
---
utils/stripe_utils.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index e7e8d9af..d04a24cb 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -524,7 +524,8 @@ class StripeUtils(object):
payment_intent_obj = stripe.PaymentIntent.create(
amount=amount,
currency='chf',
- customer=customer
+ customer=customer,
+ setup_future_usage='off_session'
)
return payment_intent_obj
From 7b71ba55f206c31e020bc9c71678b822bc8df38c Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 22:43:50 +0530
Subject: [PATCH 182/217] Rename token to id_payment_method
---
datacenterlight/views.py | 4 ++--
membership/models.py | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index fcc423bd..cf920579 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -504,7 +504,7 @@ class PaymentOrderView(FormView):
)
customer = StripeCustomer.create_stripe_api_customer(
email=user_email,
- token=id_payment_method,
+ id_payment_method=id_payment_method,
customer_name=user_name)
except CustomUser.DoesNotExist:
logger.debug(
@@ -515,7 +515,7 @@ class PaymentOrderView(FormView):
)
customer = StripeCustomer.create_stripe_api_customer(
email=user_email,
- token=id_payment_method,
+ id_payment_method=id_payment_method,
customer_name=user_name)
billing_address = address_form.save()
diff --git a/membership/models.py b/membership/models.py
index 079b60e0..8de99468 100644
--- a/membership/models.py
+++ b/membership/models.py
@@ -277,7 +277,7 @@ class StripeCustomer(models.Model):
return "%s - %s" % (self.stripe_id, self.user.email)
@classmethod
- def create_stripe_api_customer(cls, email=None, token=None,
+ def create_stripe_api_customer(cls, email=None, id_payment_method=None,
customer_name=None):
"""
This method creates a Stripe API customer with the given
From 6e6a57b3043bca51c39635768896e59c66e4dd37 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 22:45:02 +0530
Subject: [PATCH 183/217] Refactor price to charge => amount_to_charge
This is a common variable between the generic onetime and
subscription
---
datacenterlight/views.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index cf920579..3b83507b 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -584,6 +584,9 @@ class OrderConfirmationView(DetailView, FormView):
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
def get(self, request, *args, **kwargs):
context = {}
+ # this is amount to be charge/subscribed before VAT and discount
+ # and expressed in chf. To convert to cents, multiply by 100
+ amount_to_charge = 0
if (('specs' not in request.session or 'user' not in request.session)
and 'generic_payment_type' not in request.session):
return HttpResponseRedirect(reverse('datacenterlight:index'))
@@ -621,6 +624,7 @@ class OrderConfirmationView(DetailView, FormView):
'generic_payment_details':
request.session['generic_payment_details'],
})
+ amount_to_charge = request.session['generic_payment_details']['amount']
else:
vm_specs = request.session.get('specs')
user_vat_country = (
@@ -636,7 +640,7 @@ class OrderConfirmationView(DetailView, FormView):
)
vm_specs["price"] = price
vm_specs["price_after_discount"] = price - discount["amount"]
-
+ amount_to_charge = price
vat_number = request.session.get('billing_address_data').get("vat_number")
billing_address = BillingAddress.objects.get(id=request.session["billing_address_id"])
if vat_number:
@@ -689,8 +693,7 @@ class OrderConfirmationView(DetailView, FormView):
# the customer
stripe_utils = StripeUtils()
payment_intent_response = stripe_utils.get_payment_intent(
- int(request.session['generic_payment_details']['amount'] *
- 100),
+ int(amount_to_charge * 100),
customer=request.session['customer']
)
payment_intent = payment_intent_response.get(
From 9faf8978186bf24839540ba7df8c0e9be4beba4f Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 31 Dec 2020 22:46:15 +0530
Subject: [PATCH 184/217] Formatting and documentation
---
datacenterlight/views.py | 12 ++++++++----
membership/models.py | 3 ++-
2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 3b83507b..e3974e3e 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -602,9 +602,11 @@ class OrderConfirmationView(DetailView, FormView):
context['cc_last4'] = card_details_response['last4']
context['cc_brand'] = card_details_response['brand']
context['cc_exp_year'] = card_details_response['exp_year']
- context['cc_exp_month'] = '{:02d}'.format(card_details_response['exp_month'])
+ context['cc_exp_month'] = '{:02d}'.format(
+ card_details_response['exp_month'])
context['id_payment_method'] = payment_method
else:
+ # TODO check when we go through this case (to me, it seems useless)
card_id = self.request.session.get('card_id')
card_detail = UserCardDetail.objects.get(id=card_id)
context['cc_last4'] = card_detail.last4
@@ -619,7 +621,9 @@ class OrderConfirmationView(DetailView, FormView):
request.session["vat_validation_status"] == "not_needed"):
request.session['generic_payment_details']['vat_rate'] = 0
request.session['generic_payment_details']['vat_amount'] = 0
- request.session['generic_payment_details']['amount'] = request.session['generic_payment_details']['amount_before_vat']
+ request.session['generic_payment_details']['amount'] = (
+ request.session['generic_payment_details']['amount_before_vat']
+ )
context.update({
'generic_payment_details':
request.session['generic_payment_details'],
@@ -642,7 +646,8 @@ class OrderConfirmationView(DetailView, FormView):
vm_specs["price_after_discount"] = price - discount["amount"]
amount_to_charge = price
vat_number = request.session.get('billing_address_data').get("vat_number")
- billing_address = BillingAddress.objects.get(id=request.session["billing_address_id"])
+ billing_address = BillingAddress.objects.get(
+ id=request.session["billing_address_id"])
if vat_number:
validate_result = validate_vat_number(
stripe_customer_id=request.session['customer'],
@@ -656,7 +661,6 @@ class OrderConfirmationView(DetailView, FormView):
return HttpResponseRedirect(
reverse('datacenterlight:payment') + '#vat_error'
)
-
request.session["vat_validation_status"] = validate_result["status"]
if user_vat_country.lower() == "ch":
diff --git a/membership/models.py b/membership/models.py
index 8de99468..81054fb9 100644
--- a/membership/models.py
+++ b/membership/models.py
@@ -288,7 +288,8 @@ class StripeCustomer(models.Model):
stripe user.
"""
stripe_utils = StripeUtils()
- stripe_data = stripe_utils.create_customer(token, email, customer_name)
+ stripe_data = stripe_utils.create_customer(
+ id_payment_method, email, customer_name)
if stripe_data.get('response_object'):
stripe_cus_id = stripe_data.get('response_object').get('id')
return stripe_cus_id
From a32a5af5a34e68f0f9f665639ff404a74905f700 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 1 Jan 2021 00:07:23 +0530
Subject: [PATCH 185/217] Add code to differentiate between subscription and
non-subscription in js
---
datacenterlight/templates/datacenterlight/order_detail.html | 1 +
datacenterlight/views.py | 4 ++++
2 files changed, 5 insertions(+)
diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html
index b96d5123..d6c87266 100644
--- a/datacenterlight/templates/datacenterlight/order_detail.html
+++ b/datacenterlight/templates/datacenterlight/order_detail.html
@@ -337,5 +337,6 @@
var success_title = '{{ success_msg.msg_title }}';
var success_url = '{{ success_msg.redirect }}';
window.stripeKey = "{{stripe_key}}";
+ window.isSubscription = ("{{is_subscription}}" === 'true');
{%endblock%}
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index e3974e3e..a4458cc2 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -729,6 +729,10 @@ class OrderConfirmationView(DetailView, FormView):
reverse('datacenterlight:index')
},
'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
+ 'is_subscription': 'true' if (
+ 'generic_payment_type' not in request.session or
+ (request.session['generic_payment_details']['recurring'])
+ ) else 'false'
})
logger.debug("Request %s" % create_incomplete_intent_request(
self.request))
From 9524e03762d2331b0eb387a367364f1856035d47 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 1 Jan 2021 00:08:33 +0530
Subject: [PATCH 186/217] Pass user param with request dict
---
datacenterlight/views.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index a4458cc2..566668fb 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -788,7 +788,8 @@ class OrderConfirmationView(DetailView, FormView):
'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
+ 'generic_payment_details': generic_payment_details,
+ 'user': user
}
if 'id_payment_method' in request.session:
@@ -1089,7 +1090,7 @@ class OrderConfirmationView(DetailView, FormView):
msg = subscription_result.get('error')
return show_error(msg, self.request)
provisioning_response = do_provisioning(
- req, user, stripe_api_cus_id,
+ req, stripe_api_cus_id,
card_details_response, stripe_subscription_obj,
stripe_onetime_charge, gp_details, specs, vm_template_id,
template, request.session.get('billing_address_data'),
@@ -1405,6 +1406,9 @@ def do_provisioning(request, stripe_api_cus_id, card_details_response,
:param real_request:
:return:
"""
+
+ user = request.get('user', None)
+
# Create user if the user is not logged in and if he is not already
# registered
custom_user, new_user = get_or_create_custom_user(
From d8a674da3d2b1174288074e4d7a6cbf4aed5af15 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 1 Jan 2021 00:08:58 +0530
Subject: [PATCH 187/217] Remove unwanted code + doc
---
datacenterlight/views.py | 18 +++++-------------
1 file changed, 5 insertions(+), 13 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 566668fb..32fe4138 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -587,6 +587,7 @@ class OrderConfirmationView(DetailView, FormView):
# this is amount to be charge/subscribed before VAT and discount
# and expressed in chf. To convert to cents, multiply by 100
amount_to_charge = 0
+ vm_specs = None
if (('specs' not in request.session or 'user' not in request.session)
and 'generic_payment_type' not in request.session):
return HttpResponseRedirect(reverse('datacenterlight:index'))
@@ -875,18 +876,6 @@ class OrderConfirmationView(DetailView, FormView):
if gp_details['recurring']:
# generic recurring payment
logger.debug("Commencing a generic recurring payment")
- else:
- # generic one time payment
- logger.debug("Commencing a one time payment")
- charge_response = stripe_utils.make_charge(
- amount=gp_details['amount'],
- customer=stripe_api_cus_id
- )
- stripe_onetime_charge = charge_response.get('response_object')
- # Check if the payment was approved
- if not stripe_onetime_charge:
- msg = charge_response.get('error')
- return show_error(msg, self.request)
if ('generic_payment_type' not in request.session or
(request.session['generic_payment_details']['recurring'])):
recurring_interval = 'month'
@@ -1079,7 +1068,6 @@ class OrderConfirmationView(DetailView, FormView):
)
}
}
- #clear_all_session_vars(request)
return JsonResponse(context)
else:
logger.debug(
@@ -1089,6 +1077,10 @@ class OrderConfirmationView(DetailView, FormView):
"requires_source_action")
msg = subscription_result.get('error')
return show_error(msg, self.request)
+ # the code below is executed for
+ # a) subscription case
+ # b) the subscription object is active itself, without requiring
+ # SCA
provisioning_response = do_provisioning(
req, stripe_api_cus_id,
card_details_response, stripe_subscription_obj,
From 2c3d00f03f946a347fd4e56e63ae31300500c78e Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 1 Jan 2021 00:09:20 +0530
Subject: [PATCH 188/217] Pass correct stripe_customer_id
---
datacenterlight/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 32fe4138..9cce2e97 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1552,7 +1552,7 @@ def do_provisioning(request, stripe_api_cus_id, card_details_response,
}
create_vm(
- billing_address_data, stripe_customer_id, specs,
+ billing_address_data, custom_user.stripecustomer.id, specs,
stripe_subscription_obj, card_details_dict, request,
vm_template_id, template, user
)
From ba3c5ddd1dda0003cda4a8813f274e80ca3ffb68 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 1 Jan 2021 00:09:47 +0530
Subject: [PATCH 189/217] Use different js code for one-time and subscriptions
---
.../hosting/js/virtual_machine_detail.js | 169 +++++++++---------
1 file changed, 85 insertions(+), 84 deletions(-)
diff --git a/hosting/static/hosting/js/virtual_machine_detail.js b/hosting/static/hosting/js/virtual_machine_detail.js
index 5e13519c..72770182 100644
--- a/hosting/static/hosting/js/virtual_machine_detail.js
+++ b/hosting/static/hosting/js/virtual_machine_detail.js
@@ -92,101 +92,102 @@ $(document).ready(function() {
});
var create_vm_form = $('#virtual_machine_create_form');
- create_vm_form.submit(placeOrderPaymentIntent);
-
- function placeOrderPaymentIntent(e) {
- e.preventDefault();
- var stripe = Stripe(window.stripeKey);
- stripe.confirmCardPayment(
- window.paymentIntentSecret,
- {
- payment_method: window.pm_id
- }
- ).then(function(result) {
- window.result = result;
- fa_icon = $('.modal-icon > .fa');
- modal_btn = $('#createvm-modal-done-btn');
- if (result.error) {
- // Display error.message in your UI.
- modal_btn.attr('href', error_url).removeClass('hide');
- fa_icon.attr('class', 'fa fa-close');
- modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide');
- $('#createvm-modal-title').text(error_title);
- $('#createvm-modal-body').html(result.error.message + " " + error_msg);
- } else {
- // The payment has succeeded
- // Display a success message
- modal_btn.attr('href', success_url).removeClass('hide');
- fa_icon.attr('class', 'checkmark');
- $('#createvm-modal-title').text(success_title);
- $('#createvm-modal-body').html(success_msg);
- }
- });
- }
- /*
- create_vm_form.submit(function () {
- $('#btn-create-vm').prop('disabled', true);
- $.ajax({
- url: create_vm_form.attr('action'),
- type: 'POST',
- data: create_vm_form.serialize(),
- init: function(){
- ok_btn = $('#createvm-modal-done-btn');
- close_btn = $('#createvm-modal-close-btn');
- ok_btn.addClass('btn btn-success btn-ok btn-wide hide');
- close_btn.addClass('btn btn-danger btn-ok btn-wide hide');
- },
- success: function (data) {
- fa_icon = $('.modal-icon > .fa');
- modal_btn = $('#createvm-modal-done-btn');
- if (data.showSCA){
- console.log("Show SCA");
- var stripe = Stripe(data.STRIPE_PUBLISHABLE_KEY);
- stripe.confirmCardPayment(data.payment_intent_secret).then(function(result) {
- if (result.error) {
- // Display error.message in your UI.
- modal_btn.attr('href', data.error.redirect).removeClass('hide');
+ if (window.isSubscription) {
+ create_vm_form.submit(function () {
+ $('#btn-create-vm').prop('disabled', true);
+ $.ajax({
+ url: create_vm_form.attr('action'),
+ type: 'POST',
+ data: create_vm_form.serialize(),
+ init: function () {
+ ok_btn = $('#createvm-modal-done-btn');
+ close_btn = $('#createvm-modal-close-btn');
+ ok_btn.addClass('btn btn-success btn-ok btn-wide hide');
+ close_btn.addClass('btn btn-danger btn-ok btn-wide hide');
+ },
+ success: function (data) {
+ fa_icon = $('.modal-icon > .fa');
+ modal_btn = $('#createvm-modal-done-btn');
+ if (data.showSCA) {
+ console.log("Show SCA");
+ var stripe = Stripe(data.STRIPE_PUBLISHABLE_KEY);
+ stripe.confirmCardPayment(data.payment_intent_secret).then(function (result) {
+ if (result.error) {
+ // Display error.message in your UI.
+ modal_btn.attr('href', data.error.redirect).removeClass('hide');
+ fa_icon.attr('class', 'fa fa-close');
+ modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide');
+ $('#createvm-modal-title').text(data.error.msg_title);
+ $('#createvm-modal-body').html(data.error.msg_body);
+ } else {
+ // The payment has succeeded. Display a success message.
+ modal_btn.attr('href', data.success.redirect).removeClass('hide');
+ fa_icon.attr('class', 'checkmark');
+ $('#createvm-modal-title').text(data.success.msg_title);
+ $('#createvm-modal-body').html(data.success.msg_body);
+ }
+ });
+ $('#3Dsecure-modal').show();
+ } else {
+ $('#createvm-modal-title').text(data.msg_title);
+ $('#createvm-modal-body').html(data.msg_body);
+ if (data.redirect) {
+ modal_btn.attr('href', data.redirect).removeClass('hide');
+ } else {
+ modal_btn.attr('href', "");
+ }
+ if (data.status === true) {
+ fa_icon.attr('class', 'checkmark');
+ } else {
fa_icon.attr('class', 'fa fa-close');
modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide');
- $('#createvm-modal-title').text(data.error.msg_title);
- $('#createvm-modal-body').html(data.error.msg_body);
- } else {
- // The payment has succeeded. Display a success message.
- modal_btn.attr('href', data.success.redirect).removeClass('hide');
- fa_icon.attr('class', 'checkmark');
- $('#createvm-modal-title').text(data.success.msg_title);
- $('#createvm-modal-body').html(data.success.msg_body);
}
- });
- $('#3Dsecure-modal').show();
- } else {
- $('#createvm-modal-title').text(data.msg_title);
- $('#createvm-modal-body').html(data.msg_body);
- if (data.redirect) {
- modal_btn.attr('href', data.redirect).removeClass('hide');
- } else {
- modal_btn.attr('href', "");
}
- if (data.status === true) {
- fa_icon.attr('class', 'checkmark');
- } else {
- fa_icon.attr('class', 'fa fa-close');
- modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide');
- }
- }
- },
- error: function (xmlhttprequest, textstatus, message) {
+ },
+ error: function (xmlhttprequest, textstatus, message) {
fa_icon = $('.modal-icon > .fa');
fa_icon.attr('class', 'fa fa-close');
- if (typeof(create_vm_error_message) !== 'undefined') {
+ if (typeof (create_vm_error_message) !== 'undefined') {
$('#createvm-modal-body').text(create_vm_error_message);
}
$('#btn-create-vm').prop('disabled', false);
$('#createvm-modal-close-btn').removeClass('hide');
- }
+ }
+ });
+ return false;
});
- return false;
- });*/
+ } else {
+ create_vm_form.submit(placeOrderPaymentIntent);
+ function placeOrderPaymentIntent(e) {
+ e.preventDefault();
+ var stripe = Stripe(window.stripeKey);
+ stripe.confirmCardPayment(
+ window.paymentIntentSecret,
+ {
+ payment_method: window.pm_id
+ }
+ ).then(function(result) {
+ window.result = result;
+ fa_icon = $('.modal-icon > .fa');
+ modal_btn = $('#createvm-modal-done-btn');
+ if (result.error) {
+ // Display error.message in your UI.
+ modal_btn.attr('href', error_url).removeClass('hide');
+ fa_icon.attr('class', 'fa fa-close');
+ modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide');
+ $('#createvm-modal-title').text(error_title);
+ $('#createvm-modal-body').html(result.error.message + " " + error_msg);
+ } else {
+ // The payment has succeeded
+ // Display a success message
+ modal_btn.attr('href', success_url).removeClass('hide');
+ fa_icon.attr('class', 'checkmark');
+ $('#createvm-modal-title').text(success_title);
+ $('#createvm-modal-body').html(success_msg);
+ }
+ });
+ }
+ }
$('#createvm-modal').on('hidden.bs.modal', function () {
$(this).find('.modal-footer .btn').addClass('hide');
});
From e024a3a7a6096d8c2864178f71b6766feeffab39 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 1 Jan 2021 00:34:35 +0530
Subject: [PATCH 190/217] Do not create paymentintent for subscription
---
datacenterlight/views.py | 46 ++++++++++++++++++++++------------------
1 file changed, 25 insertions(+), 21 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 9cce2e97..464a9b34 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -694,24 +694,31 @@ class OrderConfirmationView(DetailView, FormView):
'keys': get_all_public_keys(self.request.user)
})
- # Obtain PaymentIntent so that we can initiate and charge/subscribe
- # the customer
- stripe_utils = StripeUtils()
- payment_intent_response = stripe_utils.get_payment_intent(
- int(amount_to_charge * 100),
- customer=request.session['customer']
- )
- payment_intent = payment_intent_response.get(
- 'response_object')
- if not payment_intent:
- logger.error("Could not create payment_intent %s" %
- str(payment_intent_response))
+ is_subscription = False
+ if ('generic_payment_type' not in request.session or
+ (request.session['generic_payment_details']['recurring'])):
+ # Obtain PaymentIntent so that we can initiate and charge
+ # the customer
+ is_subscription = True
+ logger.debug("CASE: Subscription")
else:
- logger.debug("payment_intent.client_secret = %s" %
- str(payment_intent.client_secret))
- context.update({
- 'payment_intent_secret': payment_intent.client_secret
- })
+ logger.debug("CASE: One time payment")
+ stripe_utils = StripeUtils()
+ payment_intent_response = stripe_utils.get_payment_intent(
+ int(amount_to_charge * 100),
+ customer=request.session['customer']
+ )
+ payment_intent = payment_intent_response.get(
+ 'response_object')
+ if not payment_intent:
+ logger.error("Could not create payment_intent %s" %
+ str(payment_intent_response))
+ else:
+ logger.debug("payment_intent.client_secret = %s" %
+ str(payment_intent.client_secret))
+ context.update({
+ 'payment_intent_secret': payment_intent.client_secret
+ })
context.update({
'site_url': reverse('datacenterlight:index'),
@@ -730,10 +737,7 @@ class OrderConfirmationView(DetailView, FormView):
reverse('datacenterlight:index')
},
'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
- 'is_subscription': 'true' if (
- 'generic_payment_type' not in request.session or
- (request.session['generic_payment_details']['recurring'])
- ) else 'false'
+ 'is_subscription': str(is_subscription).lower()
})
logger.debug("Request %s" % create_incomplete_intent_request(
self.request))
From 04003757dc2ce3fefb0e20279d2312632c4ae883 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 1 Jan 2021 00:43:28 +0530
Subject: [PATCH 191/217] Move log block to correct case
---
datacenterlight/views.py | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 464a9b34..3a295089 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -719,6 +719,24 @@ class OrderConfirmationView(DetailView, FormView):
context.update({
'payment_intent_secret': payment_intent.client_secret
})
+ logger.debug("Request %s" % create_incomplete_intent_request(
+ self.request))
+ logger.debug("%s" % str(payment_intent))
+ logger.debug("customer %s" % request.session['customer'])
+ logger.debug("card_details_response %s" % card_details_response)
+ logger.debug("request.session[generic_payment_details] %s" % request.session["generic_payment_details"])
+ logger.debug("request.session[billing_address_data] %s" % request.session["billing_address_data"])
+ IncompletePaymentIntents.objects.create(
+ request=create_incomplete_intent_request(self.request),
+ payment_intent_id=payment_intent.id,
+ stripe_api_cus_id=request.session['customer'],
+ card_details_response=json.dumps(card_details_response),
+ stripe_subscription_id=None,
+ stripe_charge_id=None,
+ gp_details=json.dumps(request.session["generic_payment_details"]),
+ billing_address_data=json.dumps(request.session["billing_address_data"])
+ )
+ logger.debug("IncompletePaymentIntent done")
context.update({
'site_url': reverse('datacenterlight:index'),
@@ -739,24 +757,6 @@ class OrderConfirmationView(DetailView, FormView):
'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
'is_subscription': str(is_subscription).lower()
})
- logger.debug("Request %s" % create_incomplete_intent_request(
- self.request))
- logger.debug("%s" % str(payment_intent))
- logger.debug("customer %s" % request.session['customer'])
- logger.debug("card_details_response %s" % card_details_response)
- logger.debug("request.session[generic_payment_details] %s" % request.session["generic_payment_details"])
- logger.debug("request.session[billing_address_data] %s" % request.session["billing_address_data"])
- IncompletePaymentIntents.objects.create(
- request=create_incomplete_intent_request(self.request),
- payment_intent_id=payment_intent.id,
- stripe_api_cus_id=request.session['customer'],
- card_details_response=json.dumps(card_details_response),
- stripe_subscription_id=None,
- stripe_charge_id=None,
- gp_details=json.dumps(request.session["generic_payment_details"]),
- billing_address_data=json.dumps(request.session["billing_address_data"])
- )
- logger.debug("IncompletePaymentIntent done")
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
From 9b84461a297592d56041d776fbc361b534a3b798 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 1 Jan 2021 00:58:11 +0530
Subject: [PATCH 192/217] Add logger messages
---
datacenterlight/views.py | 1 +
webhook/views.py | 2 ++
2 files changed, 3 insertions(+)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 3a295089..226985cf 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1403,6 +1403,7 @@ def do_provisioning(request, stripe_api_cus_id, card_details_response,
:return:
"""
+ logger.debug("do_provisioning")
user = request.get('user', None)
# Create user if the user is not logged in and if he is not already
diff --git a/webhook/views.py b/webhook/views.py
index 5760b0dd..2d2ce371 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -139,6 +139,8 @@ def handle_webhook(request):
invoice_obj.billing_reason == subscription_update""")
logger.debug("Start provisioning")
try:
+ logger.debug("Looking for subscription %s" %
+ invoice_obj.subscription)
stripe_subscription_obj = stripe.Subscription.retrieve(
invoice_obj.subscription)
try:
From e7462289f6b2e532a8e9915ccf4913848cec9c94 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 1 Jan 2021 01:03:05 +0530
Subject: [PATCH 193/217] Just log IncompleteSubscription does not exist error
Do not compare with db and send admin error message
---
webhook/views.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/webhook/views.py b/webhook/views.py
index 2d2ce371..34e668ee 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -197,8 +197,9 @@ def handle_webhook(request):
billing_address_data=billing_address_data,
real_request=None
)
- except (IncompleteSubscriptions.DoesNotExist,
- IncompleteSubscriptions.MultipleObjectsReturned) as ex:
+ except IncompleteSubscriptions.DoesNotExist as ex:
+ logger.error(str(ex))
+ except IncompleteSubscriptions.MultipleObjectsReturned as ex:
logger.error(str(ex))
email_data = {
'subject': "IncompleteSubscriptions error",
From 36505db5a2b205ac5e4727996071990d79e58235 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 1 Jan 2021 01:35:59 +0530
Subject: [PATCH 194/217] set default_payment_method
---
datacenterlight/views.py | 1 +
hosting/views.py | 1 +
utils/stripe_utils.py | 7 +++++--
3 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 226985cf..9f31952e 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -980,6 +980,7 @@ class OrderConfirmationView(DetailView, FormView):
discount['stripe_coupon_id']
else ""),
tax_rates=[stripe_tax_rate.tax_rate_id] if stripe_tax_rate else [],
+ default_payment_method=request.session['id_payment_method']
)
stripe_subscription_obj = subscription_result.get('response_object')
logger.debug(stripe_subscription_obj)
diff --git a/hosting/views.py b/hosting/views.py
index f18a22b7..441e33b5 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -1201,6 +1201,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView):
discount['stripe_coupon_id']
else ""),
tax_rates=[stripe_tax_rate.tax_rate_id] if stripe_tax_rate else [],
+ default_payment_method=request.session['id_payment_method']
)
stripe_subscription_obj = subscription_result.get('response_object')
latest_invoice = stripe.Invoice.retrieve(stripe_subscription_obj.latest_invoice)
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index d04a24cb..7ef306bf 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -329,10 +329,12 @@ class StripeUtils(object):
@handleStripeError
def subscribe_customer_to_plan(self, customer, plans, trial_end=None,
- coupon="", tax_rates=list()):
+ coupon="", tax_rates=list(),
+ default_payment_method=""):
"""
Subscribes the given customer to the list of given plans
+ :param default_payment_method:
:param tax_rates:
:param coupon:
:param customer: The stripe customer identifier
@@ -355,7 +357,8 @@ class StripeUtils(object):
customer=customer, items=plans, trial_end=trial_end,
coupon=coupon,
default_tax_rates=tax_rates,
- payment_behavior='allow_incomplete'
+ payment_behavior='allow_incomplete',
+ default_payment_method=default_payment_method
)
logger.debug("Done subscribing")
return subscription_result
From 31c5336e1829171267bdcf24c9715d7f020526c7 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 1 Jan 2021 01:59:41 +0530
Subject: [PATCH 195/217] Show cards directly from Stripe and dissociate using
PaymentMethod
---
hosting/views.py | 6 ++++--
utils/stripe_utils.py | 10 ++++++++--
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/hosting/views.py b/hosting/views.py
index 441e33b5..df497a64 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -565,9 +565,11 @@ class SettingsView(LoginRequiredMixin, FormView):
stripe_customer = None
if hasattr(user, 'stripecustomer'):
stripe_customer = user.stripecustomer
- cards_list = UserCardDetail.get_all_cards_list(
- stripe_customer=stripe_customer
+ stripe_utils = StripeUtils()
+ cards_list_request = stripe_utils.get_available_payment_methods(
+ stripe_customer
)
+ cards_list = cards_list_request.get('response_object')
context.update({
'cards_list': cards_list,
'stripe_key': settings.STRIPE_API_PUBLIC_KEY
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index 7ef306bf..09ffc17b 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -98,8 +98,14 @@ class StripeUtils(object):
@handleStripeError
def dissociate_customer_card(self, stripe_customer_id, card_id):
customer = stripe.Customer.retrieve(stripe_customer_id)
- card = customer.sources.retrieve(card_id)
- card.delete()
+ if card_id.startswith("pm"):
+ logger.debug("PaymentMethod %s detached %s" % (card_id,
+ stripe_customer_id))
+ customer.PaymentMethod.detach(card_id)
+ else:
+ logger.debug("card %s detached %s" % (card_id, stripe_customer_id))
+ card = customer.sources.retrieve(card_id)
+ card.delete()
@handleStripeError
def update_customer_card(self, customer_id, token):
From 7cd485bc6d77d1fedce8a87d6aca408619d8cd9e Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 1 Jan 2021 02:37:52 +0530
Subject: [PATCH 196/217] Fix issues on settings/card save/delete methods
---
hosting/urls.py | 2 +-
hosting/views.py | 37 +++++++++----------------------------
utils/stripe_utils.py | 4 +++-
3 files changed, 13 insertions(+), 30 deletions(-)
diff --git a/hosting/urls.py b/hosting/urls.py
index 5b2b87b0..e34d27d6 100644
--- a/hosting/urls.py
+++ b/hosting/urls.py
@@ -51,7 +51,7 @@ urlpatterns = [
name='choice_ssh_keys'),
url(r'delete_ssh_key/(?P\d+)/?$', SSHKeyDeleteView.as_view(),
name='delete_ssh_key'),
- url(r'delete_card/(?P\d+)/?$', SettingsView.as_view(),
+ url(r'delete_card/(?P[\w\-]+)/$', SettingsView.as_view(),
name='delete_card'),
url(r'create_ssh_key/?$', SSHKeyCreateView.as_view(),
name='create_ssh_key'),
diff --git a/hosting/views.py b/hosting/views.py
index df497a64..913a9643 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -595,33 +595,14 @@ class SettingsView(LoginRequiredMixin, FormView):
messages.add_message(request, messages.SUCCESS, msg)
return HttpResponseRedirect(reverse_lazy('hosting:settings'))
if 'delete_card' in request.POST:
- try:
- card = UserCardDetail.objects.get(pk=self.kwargs.get('pk'))
- if (request.user.has_perm(self.permission_required[0], card)
- and
- request.user
- .stripecustomer
- .usercarddetail_set
- .count() > 1):
- if card.card_id is not None:
- stripe_utils = StripeUtils()
- stripe_utils.dissociate_customer_card(
- request.user.stripecustomer.stripe_id,
- card.card_id
- )
- if card.preferred:
- UserCardDetail.set_default_card_from_stripe(
- request.user.stripecustomer.stripe_id
- )
- card.delete()
- msg = _("Card deassociation successful")
- messages.add_message(request, messages.SUCCESS, msg)
- else:
- msg = _("You are not permitted to do this operation")
- messages.add_message(request, messages.ERROR, msg)
- except UserCardDetail.DoesNotExist:
- msg = _("The selected card does not exist")
- messages.add_message(request, messages.ERROR, msg)
+ card = self.kwargs.get('pk')
+ stripe_utils = StripeUtils()
+ stripe_utils.dissociate_customer_card(
+ request.user.stripecustomer.stripe_id,
+ card
+ )
+ msg = _("Card deassociation successful")
+ messages.add_message(request, messages.SUCCESS, msg)
return HttpResponseRedirect(reverse_lazy('hosting:settings'))
form = self.get_form()
if form.is_valid():
@@ -697,7 +678,7 @@ class SettingsView(LoginRequiredMixin, FormView):
messages.add_message(request, messages.SUCCESS, msg)
else:
# TODO : Test this flow
- id_payment_method = form.cleaned_data.get('id_payment_method')
+ id_payment_method = request.POST.get('id_payment_method', None)
stripe_utils = StripeUtils()
card_details = stripe_utils.get_cards_details_from_payment_method(
id_payment_method
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index 09ffc17b..0bfb2412 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -101,7 +101,9 @@ class StripeUtils(object):
if card_id.startswith("pm"):
logger.debug("PaymentMethod %s detached %s" % (card_id,
stripe_customer_id))
- customer.PaymentMethod.detach(card_id)
+ pm = stripe.PaymentMethod.retrieve(card_id)
+ stripe.PaymentMethod.detach(card_id)
+ pm.delete()
else:
logger.debug("card %s detached %s" % (card_id, stripe_customer_id))
card = customer.sources.retrieve(card_id)
From 8deed169ca6c5d223f28bd63dce5e0e9ba474951 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 1 Jan 2021 11:12:56 +0530
Subject: [PATCH 197/217] Fix redirect url
Take user to virtual_machines page if the user ordered a VM
---
datacenterlight/views.py | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 9f31952e..ea39cd9c 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -1027,6 +1027,14 @@ class OrderConfirmationView(DetailView, FormView):
latest_invoice.payment_intent
)
# TODO: requires_attention is probably wrong value to compare
+ if request.user.is_authenticated():
+ if 'generic_payment_details' in request.session:
+ redirect_url = reverse('hosting:invoices')
+ else:
+ redirect_url = reverse('hosting:virtual_machines')
+ else:
+ redirect_url = reverse('datacenterlight:index')
+
if (pi.status == 'requires_attention' or
pi.status == 'requires_source_action'):
logger.debug("Display SCA authentication %s " % pi.status)
@@ -1037,11 +1045,7 @@ class OrderConfirmationView(DetailView, FormView):
'showSCA': True,
'success': {
'status': True,
- 'redirect': (
- reverse('hosting:invoices')
- if request.user.is_authenticated()
- else reverse('datacenterlight:index')
- ),
+ 'redirect': redirect_url,
'msg_title': str(_('Thank you for the order.')),
'msg_body': str(
_('Your product will be provisioned as soon as'
From 1d7c3f424cbd9ecc9c060279739e589654122d13 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Fri, 1 Jan 2021 11:17:07 +0530
Subject: [PATCH 198/217] Don't send admin email if IncompletePaymentIntent
lookup doesn't contain a value
---
webhook/views.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/webhook/views.py b/webhook/views.py
index 34e668ee..0a96d0b6 100644
--- a/webhook/views.py
+++ b/webhook/views.py
@@ -289,11 +289,11 @@ def handle_webhook(request):
billing_address_data=billing_address_data
)
incomplete_pm.save()
- except (IncompletePaymentIntents.DoesNotExist,
- IncompletePaymentIntents.MultipleObjectsReturned,
+ except IncompletePaymentIntents.DoesNotExist as ex:
+ logger.error(str(ex))
+ except (IncompletePaymentIntents.MultipleObjectsReturned,
Exception) as ex:
logger.error(str(ex))
- logger.debug(str(ex))
email_data = {
'subject': "IncompletePaymentIntents error",
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
From 44ebb7191604383e4fbfc162d8ed595a06ad0fe9 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Sat, 2 Jan 2021 09:02:37 +0530
Subject: [PATCH 199/217] Also clear id_payment_method from session
---
datacenterlight/utils.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py
index 8a087e3e..6a0e45ca 100644
--- a/datacenterlight/utils.py
+++ b/datacenterlight/utils.py
@@ -111,7 +111,8 @@ def clear_all_session_vars(request):
'token', 'customer', 'generic_payment_type',
'generic_payment_details', 'product_id',
'order_confirm_url', 'new_user_hosting_key_id',
- 'vat_validation_status', 'billing_address_id']:
+ 'vat_validation_status', 'billing_address_id',
+ 'id_payment_method']:
if session_var in request.session:
del request.session[session_var]
From 6c968fdbb8cb93a34ebffb8a2cb0fced620ac9ce Mon Sep 17 00:00:00 2001
From: PCoder
Date: Sat, 2 Jan 2021 09:02:58 +0530
Subject: [PATCH 200/217] Redirect to dcl payment
We no longer seem to use hosting payment
---
datacenterlight/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index ea39cd9c..5103f859 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -598,7 +598,7 @@ class OrderConfirmationView(DetailView, FormView):
payment_method
)
if not card_details.get('response_object'):
- return HttpResponseRedirect(reverse('hosting:payment'))
+ return HttpResponseRedirect(reverse('datacenterlight:payment'))
card_details_response = card_details['response_object']
context['cc_last4'] = card_details_response['last4']
context['cc_brand'] = card_details_response['brand']
From ec13a71866d73b6d8229efc9aa09148a59f1c9a5 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 7 Jan 2021 16:29:34 +0530
Subject: [PATCH 201/217] Reformat code
---
datacenterlight/views.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 5103f859..6f765da8 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -8,8 +8,9 @@ from django.contrib import messages
from django.contrib.auth import login, authenticate
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
-from django.http import HttpResponseRedirect, JsonResponse, Http404, \
- HttpResponse
+from django.http import (
+ HttpResponseRedirect, JsonResponse, Http404, HttpResponse
+)
from django.shortcuts import render
from django.utils.translation import get_language, ugettext_lazy as _
from django.views.decorators.cache import cache_control
From 1e67bef4f54cec9910fb1ab8c54ee571d8a786e1 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 7 Jan 2021 16:30:33 +0530
Subject: [PATCH 202/217] Remove unwanted code/comments
---
datacenterlight/views.py | 10 +++++++---
hosting/static/hosting/js/payment.js | 18 ------------------
hosting/views.py | 1 -
utils/stripe_utils.py | 1 -
4 files changed, 7 insertions(+), 23 deletions(-)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 6f765da8..3d3543c5 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -471,7 +471,6 @@ class PaymentOrderView(FormView):
id_payment_method = self.request.POST.get('id_payment_method',
None)
if id_payment_method == 'undefined':
- # Probably user chose one of the previously saved cards
id_payment_method = address_form.cleaned_data.get('card')
request.session["id_payment_method"] = id_payment_method
logger.debug("id_payment_method is %s" % id_payment_method)
@@ -1125,8 +1124,9 @@ class OrderConfirmationView(DetailView, FormView):
def create_incomplete_intent_request(request):
"""
- Persist session variables so that they could be pick up
- in the webhook for processing.
+ Creates a dictionary of all session variables so that they could be
+ picked up in the webhook for processing.
+
:param request:
:return:
"""
@@ -1233,6 +1233,10 @@ def do_provisioning_generic(
stripe_api_cus_id, request['id_payment_method'],
set_as_default=True
)
+ """
+ Identical to do_provisioning(), except for the fact that this
+ is specific to handling provisioning of the generic products
+ """
logger.debug("Card %s associate result %s" % (
request['id_payment_method'],
acc_result.get('response_object')
diff --git a/hosting/static/hosting/js/payment.js b/hosting/static/hosting/js/payment.js
index a2e2717a..3c4d67da 100644
--- a/hosting/static/hosting/js/payment.js
+++ b/hosting/static/hosting/js/payment.js
@@ -211,24 +211,6 @@ $(document).ready(function () {
}
});
window.card = cardNumberElement;
- /* stripe.confirmCardPayment(
- window.paymentIntentSecret,
- {
- payment_method: {card: cardNumberElement}
- }
- ).then(function(result) {
- window.result = result;
- if (result.error) {
- // Display error.message in your UI.
- var errorElement = document.getElementById('card-errors');
- errorElement.textContent = result.error.message;
- } else {
- // The payment has succeeded
- // Display a success message
- alert("Thanks for the order. Your product will be provisioned " +
- "as soon as we receive the payment. Thank you.");
- }
- }); */
}
function payWithStripe_new(e) {
e.preventDefault();
diff --git a/hosting/views.py b/hosting/views.py
index 913a9643..011da997 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -677,7 +677,6 @@ class SettingsView(LoginRequiredMixin, FormView):
msg = _("Billing address updated successfully")
messages.add_message(request, messages.SUCCESS, msg)
else:
- # TODO : Test this flow
id_payment_method = request.POST.get('id_payment_method', None)
stripe_utils = StripeUtils()
card_details = stripe_utils.get_cards_details_from_payment_method(
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index 0bfb2412..875a174e 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -111,7 +111,6 @@ class StripeUtils(object):
@handleStripeError
def update_customer_card(self, customer_id, token):
- # TODO replace token with payment intent
customer = stripe.Customer.retrieve(customer_id)
current_card_token = customer.default_source
customer.sources.retrieve(current_card_token).delete()
From c58302d90e1f01beaf085b8aaf82e34b5f2edf19 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 7 Jan 2021 16:30:48 +0530
Subject: [PATCH 203/217] Log error messages
---
datacenterlight/views.py | 4 ++++
opennebula_api/models.py | 2 ++
2 files changed, 6 insertions(+)
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 3d3543c5..e149bfc4 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -593,6 +593,7 @@ class OrderConfirmationView(DetailView, FormView):
return HttpResponseRedirect(reverse('datacenterlight:index'))
if 'id_payment_method' in self.request.session:
payment_method = self.request.session['id_payment_method']
+ logger.debug("id_payment_method: %s" % payment_method)
stripe_utils = StripeUtils()
card_details = stripe_utils.get_cards_details_from_payment_method(
payment_method
@@ -609,6 +610,7 @@ class OrderConfirmationView(DetailView, FormView):
else:
# TODO check when we go through this case (to me, it seems useless)
card_id = self.request.session.get('card_id')
+ logger.debug("NO id_payment_method, using card: %s" % card_id)
card_detail = UserCardDetail.objects.get(id=card_id)
context['cc_last4'] = card_detail.last4
context['cc_brand'] = card_detail.brand
@@ -1577,6 +1579,7 @@ def do_provisioning(request, stripe_api_cus_id, card_details_response,
def get_error_response_dict(msg, request):
+ logger.error(msg)
response = {
'status': False,
'redirect': "{url}#{section}".format(
@@ -1600,6 +1603,7 @@ def get_error_response_dict(msg, request):
def show_error(msg, request):
+ logger.error(msg)
messages.add_message(request, messages.ERROR, msg,
extra_tags='failed_payment')
return JsonResponse(get_error_response_dict(msg,request))
diff --git a/opennebula_api/models.py b/opennebula_api/models.py
index 92d5c568..2f76f423 100644
--- a/opennebula_api/models.py
+++ b/opennebula_api/models.py
@@ -154,6 +154,8 @@ class OpenNebulaManager():
protocol=settings.OPENNEBULA_PROTOCOL)
)
raise ConnectionRefusedError
+ except Exception as ex:
+ logger.error(str(ex))
def _get_user_pool(self):
try:
From 21f762d6b8fa23eb3e985b01ab1dea412d87dd9d Mon Sep 17 00:00:00 2001
From: PCoder
Date: Thu, 7 Jan 2021 16:39:00 +0530
Subject: [PATCH 204/217] Update Changelog for 3.0
---
Changelog | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Changelog b/Changelog
index b54713fe..8102c8b5 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,6 @@
+3.0: 2021-01-07
+ * 8393: Implement SCA for stripe payments (MR!745)
+ * 8691: Implment check_vm_templates management command (MR!744)
2.14: 2020-12-07
* 8692: Create a script that fixes django db for the order after celery error (MR!743)
2.13: 2020-12-02
From 640807eb6258b27ba9661f3c2daf0e8d2d0d85a3 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Tue, 12 Jan 2021 13:39:11 +0530
Subject: [PATCH 205/217] Don't get card from local db
---
hosting/views.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/hosting/views.py b/hosting/views.py
index 011da997..8b9f618c 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -580,7 +580,6 @@ class SettingsView(LoginRequiredMixin, FormView):
def post(self, request, *args, **kwargs):
if 'card' in request.POST and request.POST['card'] is not '':
card_id = escape(request.POST['card'])
- user_card_detail = UserCardDetail.objects.get(id=card_id)
UserCardDetail.set_default_card(
stripe_api_cus_id=request.user.stripecustomer.stripe_id,
stripe_source_id=user_card_detail.card_id
From 3b874901bce72f5b646350b3e939083a4138ba72 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Tue, 12 Jan 2021 13:40:03 +0530
Subject: [PATCH 206/217] Update using correct card details
---
hosting/views.py | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/hosting/views.py b/hosting/views.py
index 8b9f618c..81cb5de1 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -582,13 +582,23 @@ class SettingsView(LoginRequiredMixin, FormView):
card_id = escape(request.POST['card'])
UserCardDetail.set_default_card(
stripe_api_cus_id=request.user.stripecustomer.stripe_id,
- stripe_source_id=user_card_detail.card_id
+ stripe_source_id=card_id
)
+ stripe_utils = StripeUtils()
+ card_details = stripe_utils.get_cards_details_from_payment_method(
+ card_id
+ )
+ if not card_details.get('response_object'):
+ logger.debug("Could not find card %s in stripe" % card_id)
+ messages.add_message(request, messages.ERROR,
+ _("Could not set a default card."))
+ return HttpResponseRedirect(reverse_lazy('hosting:settings'))
+ card_details_response = card_details['response_object']
msg = _(
("Your {brand} card ending in {last4} set as "
"default card").format(
- brand=user_card_detail.brand,
- last4=user_card_detail.last4
+ brand=card_details_response['brand'],
+ last4=card_details_response['last4']
)
)
messages.add_message(request, messages.SUCCESS, msg)
From af09b343c0051912ca948f1ab79b0d7d60e05c01 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Tue, 12 Jan 2021 13:56:58 +0530
Subject: [PATCH 207/217] Update Changelog for 3.1
---
Changelog | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Changelog b/Changelog
index 8102c8b5..957a3018 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,5 @@
+3.1: 2021-01-11
+ * 8781: Fix error is setting a default card (MR!746)
3.0: 2021-01-07
* 8393: Implement SCA for stripe payments (MR!745)
* 8691: Implment check_vm_templates management command (MR!744)
From a5c83dd58931354299f78e16558539fd0f3adcef Mon Sep 17 00:00:00 2001
From: PCoder
Date: Sun, 7 Feb 2021 15:55:47 +0530
Subject: [PATCH 208/217] Update order confirmation text to better prepared for
payment dispute
---
hosting/locale/de/LC_MESSAGES/django.po | 86 +++++++++++++++------
hosting/templates/hosting/order_detail.html | 2 +-
2 files changed, 65 insertions(+), 23 deletions(-)
diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po
index 08bcdd7a..3767ea0f 100644
--- a/hosting/locale/de/LC_MESSAGES/django.po
+++ b/hosting/locale/de/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-11-15 16:40+0000\n"
+"POT-Creation-Date: 2021-02-07 10:19+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -211,6 +211,9 @@ msgstr "Bezahlbares VM Hosting in der Schweiz"
msgid "My Dashboard"
msgstr "Mein Dashboard"
+msgid "Welcome"
+msgstr ""
+
msgid "My VMs"
msgstr "Meine VMs"
@@ -364,6 +367,11 @@ msgstr "Abgelehnt"
msgid "Billed to"
msgstr "Rechnungsadresse"
+#, fuzzy
+#| msgid "Card Number"
+msgid "VAT Number"
+msgstr "Kreditkartennummer"
+
msgid "Payment method"
msgstr "Bezahlmethode"
@@ -391,6 +399,9 @@ msgstr "Festplattenkapazität"
msgid "Subtotal"
msgstr "Zwischensumme"
+msgid "VAT for"
+msgstr ""
+
msgid "VAT"
msgstr "Mehrwertsteuer"
@@ -424,18 +435,22 @@ msgstr "ZURÜCK ZUR LISTE"
msgid "Some problem encountered. Please try again later."
msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal."
+#, fuzzy
+#| msgid "Description"
+msgid "Subscriptions"
+msgstr "Beschreibung"
+
+#, fuzzy
+#| msgid "One time payment"
+msgid "One-time payments"
+msgstr "Einmalzahlung"
+
msgid "VM ID"
msgstr ""
msgid "IP Address"
msgstr "IP-Adresse"
-msgid "See Invoice"
-msgstr "Siehe Rechnung"
-
-msgid "Page"
-msgstr "Seite"
-
msgid "Log in"
msgstr "Anmelden"
@@ -480,11 +495,13 @@ msgstr "Bestellungsübersicht"
#, python-format
msgid ""
-"By clicking \"Place order\" this plan will charge your credit card account "
-"with %(vm_price)s CHF/month"
+"By clicking \"Place order\" you agree to our Terms of Service and "
+"this plan will charge your credit card account with %(vm_price)s CHF/month."
msgstr ""
-"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(vm_price)s CHF "
-"pro Monat belastet"
+"Indem Du auf "Bestellung aufgeben" klickst, erklärst Du dich mit unseren"
+" Nutzungsbedingungen einverstanden und Dein Kreditkartenkonto wird mit %(vm_price)s CHF/Monat belastet."
msgid "Place order"
msgstr "Bestellen"
@@ -504,6 +521,12 @@ msgstr "Schliessen"
msgid "Order Nr."
msgstr "Bestellung Nr."
+msgid "See Invoice"
+msgstr "Siehe Rechnung"
+
+msgid "Page"
+msgstr "Seite"
+
msgid "Your Order"
msgstr "Deine Bestellung"
@@ -572,6 +595,19 @@ msgstr "Absenden"
msgid "Password reset"
msgstr "Passwort zurücksetzen"
+#, fuzzy
+#| msgid "Key name"
+msgid "My Username"
+msgstr "Key-Name"
+
+msgid "Your VAT number has been verified"
+msgstr ""
+
+msgid ""
+"Your VAT number is under validation. VAT will be adjusted, once the "
+"validation is complete."
+msgstr ""
+
msgid "UPDATE"
msgstr "AKTUALISIEREN"
@@ -773,21 +809,15 @@ msgstr "Dein Passwort konnte nicht zurückgesetzt werden."
msgid "The reset password link is no longer valid."
msgstr "Der Link zum Zurücksetzen Deines Passwortes ist nicht mehr gültig."
+msgid "Could not set a default card."
+msgstr ""
+
msgid "Card deassociation successful"
msgstr "Die Verbindung mit der Karte wurde erfolgreich aufgehoben"
-msgid "You are not permitted to do this operation"
-msgstr "Du hast keine Erlaubnis um diese Operation durchzuführen"
-
-msgid "The selected card does not exist"
-msgstr "Die ausgewählte Karte existiert nicht"
-
msgid "Billing address updated successfully"
msgstr "Die Rechnungsadresse wurde erfolgreich aktualisiert"
-msgid "You seem to have already added this card"
-msgstr "Es scheint, als hättest du diese Karte bereits hinzugefügt"
-
#, python-brace-format
msgid "An error occurred while associating the card. Details: {details}"
msgstr ""
@@ -852,7 +882,8 @@ msgstr "Ungültige Speicher-Grösse"
#, python-brace-format
msgid "Incorrect pricing name. Please contact support{support_email}"
-msgstr "Ungültige Preisbezeichnung. Bitte kontaktiere den Support{support_email}"
+msgstr ""
+"Ungültige Preisbezeichnung. Bitte kontaktiere den Support{support_email}"
msgid ""
"We could not find the requested VM. Please "
@@ -871,7 +902,9 @@ msgstr "Fehler beenden VM"
msgid ""
"VM terminate action timed out. Please contact support@datacenterlight.ch for "
"further information."
-msgstr "VM beendet wegen Zeitüberschreitung. Bitte kontaktiere support@datacenterlight.ch für weitere Informationen."
+msgstr ""
+"VM beendet wegen Zeitüberschreitung. Bitte kontaktiere "
+"support@datacenterlight.ch für weitere Informationen."
#, python-format
msgid "Virtual Machine %(vm_name)s Cancelled"
@@ -882,6 +915,15 @@ msgstr ""
"Es gab einen Fehler bei der Bearbeitung Deine Anfrage. Bitte versuche es "
"noch einmal."
+#~ msgid "You are not permitted to do this operation"
+#~ msgstr "Du hast keine Erlaubnis um diese Operation durchzuführen"
+
+#~ msgid "The selected card does not exist"
+#~ msgstr "Die ausgewählte Karte existiert nicht"
+
+#~ msgid "You seem to have already added this card"
+#~ msgstr "Es scheint, als hättest du diese Karte bereits hinzugefügt"
+
#, python-format
#~ msgid "This key exists already with the name \"%(name)s\""
#~ msgstr "Der SSH-Key mit dem Name \"%(name)s\" existiert bereits"
diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html
index 9256271a..dee453d5 100644
--- a/hosting/templates/hosting/order_detail.html
+++ b/hosting/templates/hosting/order_detail.html
@@ -218,7 +218,7 @@
{% csrf_token %}
-
{% blocktrans with vm_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{ vm_price }} CHF/month{% endblocktrans %}.
+
{% blocktrans with vm_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" you agree to our Terms of Service and this plan will charge your credit card account with {{ vm_price }} CHF/month.{% endblocktrans %}.
From 63821813d4545e1839c2d1ceeaaf5d44b2208eae Mon Sep 17 00:00:00 2001
From: PCoder
Date: Sun, 7 Feb 2021 18:05:48 +0530
Subject: [PATCH 213/217] Fix translations
---
datacenterlight/locale/de/LC_MESSAGES/django.po | 1 +
1 file changed, 1 insertion(+)
diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po
index 7f266a3e..cd7fab99 100644
--- a/datacenterlight/locale/de/LC_MESSAGES/django.po
+++ b/datacenterlight/locale/de/LC_MESSAGES/django.po
@@ -466,6 +466,7 @@ msgid ""
"of Service and this plan will charge your credit card account with "
"%(total_price)s CHF/month"
msgstr ""
+"\n"
"Indem Du auf \"Bestellung aufgeben\" klickst, erklärst Du dich mit unseren Nutzungsbedingungen einverstanden und Dein Kreditkartenkonto wird mit %(total_price)s CHF/Monat belastet."
From 1d48dfb93b89677be70de4431964ee7184018ab7 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 29 Mar 2021 07:23:58 +0530
Subject: [PATCH 214/217] Filter invoices by paid status
---
hosting/views.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/hosting/views.py b/hosting/views.py
index ac5f81de..f55c8383 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -1315,7 +1315,8 @@ class InvoiceListView(LoginRequiredMixin, TemplateView):
logger.debug("User does not exist")
cu = self.request.user
invs = stripe.Invoice.list(customer=cu.stripecustomer.stripe_id,
- count=100)
+ count=100,
+ status='paid')
paginator = Paginator(invs.data, 10)
try:
invs_page = paginator.page(page)
From d26f2b0f69724c971ad0952509af4ab23363fe5e Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 30 Aug 2021 18:29:42 +0530
Subject: [PATCH 215/217] Normalize/convert ascii/ignore unicode characters for
homeDirectory
---
utils/ldap_manager.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/utils/ldap_manager.py b/utils/ldap_manager.py
index ee190732..fadcdbbe 100644
--- a/utils/ldap_manager.py
+++ b/utils/ldap_manager.py
@@ -3,6 +3,7 @@ import hashlib
import random
import ldap3
import logging
+import unidecode
from django.conf import settings
@@ -101,7 +102,7 @@ class LdapManager:
"uidNumber": [str(uidNumber)],
"gidNumber": [str(settings.LDAP_CUSTOMER_GROUP_ID)],
"loginShell": ["/bin/bash"],
- "homeDirectory": ["/home/{}".format(user).encode("utf-8")],
+ "homeDirectory": ["/home/{}".format(unicodedata.normalize('NFKD', user).encode('ascii','ignore'))],
"mail": email.encode("utf-8"),
"userPassword": [self._ssha_password(
password.encode("utf-8")
From 47d5c63e3b82bcbf1d49f9b597266fb96ef50774 Mon Sep 17 00:00:00 2001
From: PCoder
Date: Mon, 30 Aug 2021 18:38:58 +0530
Subject: [PATCH 216/217] Fix bad import
---
utils/ldap_manager.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/utils/ldap_manager.py b/utils/ldap_manager.py
index fadcdbbe..d40e931f 100644
--- a/utils/ldap_manager.py
+++ b/utils/ldap_manager.py
@@ -3,7 +3,7 @@ import hashlib
import random
import ldap3
import logging
-import unidecode
+import unicodedata
from django.conf import settings
From 5ce283318a51656e6a8e7ab342ed51ece66cd82f Mon Sep 17 00:00:00 2001
From: amal
Date: Mon, 27 Sep 2021 09:21:24 +0200
Subject: [PATCH 217/217] Fix poland country code in eu_countries
---
datacenterlight/utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py
index 6a0e45ca..4e8094c0 100644
--- a/datacenterlight/utils.py
+++ b/datacenterlight/utils.py
@@ -20,7 +20,7 @@ logger = logging.getLogger(__name__)
eu_countries = ['at', 'be', 'bg', 'ch', 'cy', 'cz', 'hr', 'dk',
'ee', 'fi', 'fr', 'mc', 'de', 'gr', 'hu', 'ie', 'it',
- 'lv', 'lu', 'mt', 'nl', 'po', 'pt', 'ro','sk', 'si', 'es',
+ 'lv', 'lu', 'mt', 'nl', 'pl', 'pt', 'ro','sk', 'si', 'es',
'se', 'gb']