diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 6b8710a7..00000000 --- a/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -.git diff --git a/Changelog b/Changelog index 44c4dee4..43d3495f 100644 --- a/Changelog +++ b/Changelog @@ -1,16 +1,3 @@ -3.2: 2021-02-07 - * 8816: Update order confirmation text to better prepared for payment dispute - * supportticket#22990: Fix: can't add a deleted card -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) -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) 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) diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 50b81cbb..00000000 --- a/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -FROM python:3.10.0-alpine3.15 - -WORKDIR /usr/src/app - -RUN apk add --update --no-cache \ - git \ - build-base \ - openldap-dev \ - python3-dev \ - libpq-dev \ - && rm -rf /var/cache/apk/* - -# FIX https://github.com/python-ldap/python-ldap/issues/432 -RUN echo 'INPUT ( libldap.so )' > /usr/lib/libldap_r.so - -COPY requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt -COPY ./ . diff --git a/Makefile b/Makefile index 68ff014e..67c0c15b 100644 --- a/Makefile +++ b/Makefile @@ -14,12 +14,6 @@ help: @echo ' make rsync_upload ' @echo ' make install_debian_packages ' -buildimage: - docker build -t dynamicweb:$$(git describe) . - -releaseimage: buildimage - ./release.sh - collectstatic: $(PY?) $(BASEDIR)/manage.py collectstatic diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index cd7fab99..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: 2021-02-07 11:10+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" @@ -451,43 +451,38 @@ msgstr "Dein Gesamtpreis" #, python-format msgid "" -" By clicking \"Place order\" you agree to our Terms of Service and " -"this plan will charge your credit card account with %(total_price)s CHF/year" +"By clicking \"Place order\" this plan will charge your credit card account " +"with %(total_price)s CHF/year" msgstr "" -"Indem Du auf \"Bestellung aufgeben\" klickst, erklärst Du dich mit unseren Nutzungsbedingungen einverstanden und Dein Kreditkartenkonto wird mit %(total_price)s CHF/Jahr belastet." +"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(total_price)s " +"CHF pro Jahr belastet" #, python-format msgid "" -"\n" -" By clicking \"Place order\" you agree to " -"our Terms " -"of Service and this plan will charge your credit card account with " -"%(total_price)s CHF/month" +"By clicking \"Place order\" 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." +"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(total_price)s " +"CHF pro Monat belastet" + +#, fuzzy, python-format +#| msgid "" +#| "By clicking \"Place order\" this payment will charge your credit card " +#| "account with a one time amount of %(total_price)s CHF" +msgid "" +"By clicking \"Place order\" this payment will charge your credit card " +"account with a one time amount of %(total_price)s CHF" +msgstr "" +"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit " +"%(vm_total_price)s CHF pro Monat belastet" #, python-format msgid "" -"By clicking \"Place order\" you agree to our Terms of Service and " -"this plan will charge your credit card account with %(total_price)s CHF" +"By clicking \"Place order\" this plan will charge your credit card account " +"with %(vm_total_price)s CHF/month" msgstr "" -"Indem Du auf \"Bestellung aufgeben\" klickst, erklärst Du dich mit unseren Nutzungsbedingungen einverstanden und Dein Kreditkartenkonto wird mit %(total_price)s CHF belastet." - -#, python-format -msgid "" -"By clicking \"Place order\" you agree to our Terms of Service and " -"this plan will charge your credit card account with %(vm_total_price)s CHF/" -"month" -msgstr "" -"Indem Du auf \"Bestellung aufgeben\" klickst, erklärst Du dich mit unseren Nutzungsbedingungen einverstanden und Dein Kreditkartenkonto wird mit %(vm_total_price)s CHF/Monat belastet" +"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit " +"%(vm_total_price)s CHF pro Monat belastet" msgid "Place order" msgstr "Bestellen" @@ -606,22 +601,16 @@ msgid "Incorrect pricing name. Please contact support{support_email}" msgstr "" "Ungültige Preisbezeichnung. Bitte kontaktiere den Support{support_email}" +#, python-brace-format +msgid "{user} does not have permission to access the card" +msgstr "{user} hat keine Erlaubnis auf diese Karte zuzugreifen" + +msgid "An error occurred. Details: {}" +msgstr "Ein Fehler ist aufgetreten. Details: {}" + msgid "Confirm Order" msgstr "Bestellung Bestätigen" -#, fuzzy -#| msgid "Thank you!" -msgid "Thank you !" -msgstr "Vielen Dank!" - -msgid "Your product will be provisioned as soon as we receive the payment." -msgstr "" - -#, python-brace-format -msgid "An error occurred while associating the card. Details: {details}" -msgstr "" -"Beim Verbinden der Karte ist ein Fehler aufgetreten. Details: {details}" - msgid "Error." msgstr "" @@ -632,21 +621,10 @@ msgstr "" "Es ist ein Fehler bei der Zahlung betreten. Du wirst nach dem Schliessen vom " "Popup zur Bezahlseite weitergeleitet." -msgid "Thank you for the order." -msgstr "Danke für Deine Bestellung." - -msgid "" -"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" +#, python-brace-format +msgid "An error occurred while associating the card. Details: {details}" msgstr "" - -msgid "" -"Your VM will be up and running in a few moments. We will send you a " -"confirmation email as soon as it is ready." -msgstr "" -"Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du " -"auf sie zugreifen kannst." +"Beim Verbinden der Karte ist ein Fehler aufgetreten. Details: {details}" msgid " This is a monthly recurring plan." msgstr "Dies ist ein monatlich wiederkehrender Plan." @@ -686,31 +664,15 @@ msgstr "" "Du wirst bald eine Bestätigungs-E-Mail über die Zahlung erhalten. Du kannst " "jederzeit unter info@ungleich.ch kontaktieren." -#, python-format -#~ msgid "" -#~ "By clicking \"Place order\" this plan will charge your credit card " -#~ "account with %(total_price)s CHF/month" -#~ msgstr "" -#~ "Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit " -#~ "%(total_price)s CHF pro Monat belastet" +msgid "Thank you for the order." +msgstr "Danke für Deine Bestellung." -#, fuzzy, python-format -#~| msgid "" -#~| "By clicking \"Place order\" this payment will charge your credit card " -#~| "account with a one time amount of %(total_price)s CHF" -#~ msgid "" -#~ "By clicking \"Place order\" this payment will charge your credit card " -#~ "account with a one time amount of %(total_price)s CHF" -#~ msgstr "" -#~ "Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit " -#~ "%(vm_total_price)s CHF pro Monat belastet" - -#, python-brace-format -#~ msgid "{user} does not have permission to access the card" -#~ msgstr "{user} hat keine Erlaubnis auf diese Karte zuzugreifen" - -#~ msgid "An error occurred. Details: {}" -#~ msgstr "Ein Fehler ist aufgetreten. Details: {}" +msgid "" +"Your VM will be up and running in a few moments. We will send you a " +"confirmation email as soon as it is ready." +msgstr "" +"Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du " +"auf sie zugreifen kannst." #~ msgid "Price" #~ msgstr "Preise" @@ -786,6 +748,9 @@ msgstr "" #~ "Wir werden dann sobald als möglich Ihren Beta-Zugang erstellen und Sie " #~ "daraufhin kontaktieren.Bis dahin bitten wir Sie um etwas Geduld." +#~ msgid "Thank you!" +#~ msgstr "Vielen Dank!" + #~ msgid "Thank you for order! Our team will contact you via email" #~ msgstr "" #~ "Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich " diff --git a/datacenterlight/management/commands/check_vm_templates.py b/datacenterlight/management/commands/check_vm_templates.py deleted file mode 100644 index db36fde8..00000000 --- a/datacenterlight/management/commands/check_vm_templates.py +++ /dev/null @@ -1,65 +0,0 @@ -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] - 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( - template_id=vm_template.opennebula_vm_template_id, - specs=specs, - ssh_key='\n'.join(pub_keys), - vm_name=vm_name - ) - 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_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 %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( - datetime.datetime.now(), '%Y%m%d%H%M%S' - ) - 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)) - self.stdout.write(self.style.SUCCESS("Done")) diff --git a/datacenterlight/management/commands/fix_vm_after_celery_error.py b/datacenterlight/management/commands/fix_vm_after_celery_error.py deleted file mode 100644 index 0cfdb423..00000000 --- a/datacenterlight/management/commands/fix_vm_after_celery_error.py +++ /dev/null @@ -1,76 +0,0 @@ -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 - -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('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'] - order_id = options['order_id'] - user_str = options['user'] - specs_str = options['specs'] - template_str = options['template'] - - json_acceptable_string = user_str.replace("'", "\"") - user_dict = 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_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'], - } - cu = CustomUser.objects.get(username=user.get('username')) - # Create OpenNebulaManager - self.stdout.write( - self.style.SUCCESS( - 'Connecting using %s' % (cu.username) - ) - ) - manager = OpenNebulaManager(email=cu.username, password=cu.password) - 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) - ) - ) - ) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 899b506f..f080da90 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -56,6 +56,11 @@ 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') @@ -87,8 +92,107 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): if vm_id is None: raise Exception("Could not create VM") - handle_metadata_and_emails(order_id, vm_id, manager, user, specs, - template) + # 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) except Exception as e: logger.error(str(e)) try: @@ -110,127 +214,3 @@ 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': ['dcl-orders@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) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 1f7a3cda..d8eb4934 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -2,14 +2,6 @@ {% load staticfiles bootstrap3 i18n custom_tags humanize %} {% block content %} -
{% if messages %}
@@ -278,16 +270,15 @@ {% if generic_payment_details %} {% if generic_payment_details.recurring %} {% if generic_payment_details.recurring_interval == 'year' %} -
{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %} By clicking "Place order" you agree to our Terms of Service and this plan will charge your credit card account with {{ total_price }} CHF/year{% endblocktrans %}.
+
{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{total_price}} CHF/year{% endblocktrans %}.
{% else %} -
{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %} - By clicking "Place order" you agree to our Terms of Service and this plan will charge your credit card account with {{ total_price }} CHF/month{% endblocktrans %}.
+
{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{total_price}} CHF/month{% endblocktrans %}.
{% endif %} {% else %} -
{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" you agree to our Terms of Service and this plan will charge your credit card account with {{ total_price }} CHF{% endblocktrans %}.
+
{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" this payment will charge your credit card account with a one time amount of {{total_price}} CHF{% endblocktrans %}.
{% endif %} {% else %} -
{% blocktrans with vm_total_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_total_price }} CHF/month{% endblocktrans %}.
+
{% blocktrans with vm_total_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{vm_total_price}} CHF/month{% endblocktrans %}.
{% endif %}
@@ -330,14 +321,5 @@ {%endblock%} diff --git a/datacenterlight/templatetags/custom_tags.py b/datacenterlight/templatetags/custom_tags.py index 120cabbf..8003be0e 100644 --- a/datacenterlight/templatetags/custom_tags.py +++ b/datacenterlight/templatetags/custom_tags.py @@ -72,29 +72,25 @@ def get_line_item_from_hosting_order_charge(hosting_order_id): :param hosting_order_id: the HostingOrder id :return: """ - try: - hosting_order = HostingOrder.objects.get(id = hosting_order_id) - if hosting_order.stripe_charge_id: - return mark_safe(""" - {product_name} - {created_at} - {total} - - {see_invoice_text} - - """.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}), + hosting_order = HostingOrder.objects.get(id = hosting_order_id) + if hosting_order.stripe_charge_id: + return mark_safe(""" + {product_name} + {created_at} + {total} + + {see_invoice_text} + + """.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: - return "" - except Exception as ex: - logger.error("Error %s" % str(ex)) + see_invoice_text=_("See Invoice") + )) + else: return "" @@ -114,7 +110,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 if line_data.plan is not None else "" + plan_name = line_data.plan.name start_date = line_data.period.start end_date = line_data.period.end is_first = False diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 4e8094c0..97bfef4c 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', 'pl', 'pt', 'ro','sk', 'si', 'es', + 'lv', 'lu', 'mt', 'nl', 'po', 'pt', 'ro','sk', 'si', 'es', 'se', 'gb'] @@ -38,7 +38,6 @@ 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'], @@ -103,6 +102,8 @@ 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: @@ -111,8 +112,7 @@ 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', - 'id_payment_method']: + 'vat_validation_status', 'billing_address_id']: if session_var in request.session: del request.session[session_var] diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 5bf68e0a..26b9bad0 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -1,4 +1,3 @@ -import json import logging import stripe @@ -8,9 +7,7 @@ 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 from django.shortcuts import render from django.utils.translation import get_language, ugettext_lazy as _ from django.views.decorators.cache import cache_control @@ -22,8 +19,9 @@ from hosting.forms import ( ) from hosting.models import ( HostingBill, HostingOrder, UserCardDetail, GenericProduct, UserHostingKey, - StripeTaxRate, IncompleteSubscriptions, IncompletePaymentIntents) + StripeTaxRate) 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, @@ -264,11 +262,9 @@ class PaymentOrderView(FormView): stripe_customer = user.stripecustomer else: stripe_customer = None - stripe_utils = StripeUtils() - cards_list_request = stripe_utils.get_available_payment_methods( - stripe_customer + cards_list = UserCardDetail.get_all_cards_list( + stripe_customer=stripe_customer ) - cards_list = cards_list_request.get('response_object') context.update({'cards_list': cards_list}) else: billing_address_form = BillingAddressFormSignup( @@ -314,12 +310,6 @@ class PaymentOrderView(FormView): @cache_control(no_cache=True, must_revalidate=True, no_store=True) def get(self, request, *args, **kwargs): request.session.pop('vat_validation_status') - request.session.pop('card_id') - request.session.pop('token') - 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)) if (('type' in request.GET and request.GET['type'] == 'generic') or 'product_slug' in kwargs): request.session['generic_payment_type'] = 'generic' @@ -468,20 +458,42 @@ class PaymentOrderView(FormView): context['generic_payment_form'] = generic_payment_form context['billing_address_form'] = address_form return self.render_to_response(context) - id_payment_method = self.request.POST.get('id_payment_method', - None) - if id_payment_method == 'undefined': - 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) + token = address_form.cleaned_data.get('token') + if token is '': + card_id = address_form.cleaned_data.get('card') + 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['token'] = token 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'), - id_payment_method=id_payment_method + email=this_user.get('email'), token=token ) else: user_email = address_form.cleaned_data.get('email') @@ -504,7 +516,7 @@ class PaymentOrderView(FormView): ) customer = StripeCustomer.create_stripe_api_customer( email=user_email, - id_payment_method=id_payment_method, + token=token, customer_name=user_name) except CustomUser.DoesNotExist: logger.debug( @@ -515,7 +527,7 @@ class PaymentOrderView(FormView): ) customer = StripeCustomer.create_stripe_api_customer( email=user_email, - id_payment_method=id_payment_method, + token=token, customer_name=user_name) billing_address = address_form.save() @@ -584,33 +596,24 @@ 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 - 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')) - if 'id_payment_method' in self.request.session: - payment_method = self.request.session['id_payment_method'] - logger.debug("id_payment_method: %s" % payment_method) + if 'token' in self.request.session: + token = self.request.session['token'] stripe_utils = StripeUtils() - card_details = stripe_utils.get_cards_details_from_payment_method( - payment_method + card_details = stripe_utils.get_cards_details_from_token( + token ) if not card_details.get('response_object'): - return HttpResponseRedirect(reverse('datacenterlight:payment')) + return HttpResponseRedirect(reverse('hosting:payment')) card_details_response = card_details['response_object'] 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['id_payment_method'] = payment_method + context['cc_exp_month'] = '{:02d}'.format(card_details_response['exp_month']) 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 @@ -624,14 +627,11 @@ 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'], }) - amount_to_charge = request.session['generic_payment_details']['amount'] else: vm_specs = request.session.get('specs') user_vat_country = ( @@ -647,10 +647,9 @@ 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"]) + 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'], @@ -664,6 +663,7 @@ 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": @@ -695,51 +695,6 @@ class OrderConfirmationView(DetailView, FormView): 'form': UserHostingKeyForm(request=self.request), 'keys': get_all_public_keys(self.request.user) }) - - 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("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 - }) - 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'), 'page_header_text': _('Confirm Order'), @@ -747,67 +702,42 @@ class OrderConfirmationView(DetailView, FormView): request.session.get('billing_address_data') ), '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, - 'is_subscription': str(is_subscription).lower() }) 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() - 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, - 'user': user - } - if 'id_payment_method' in request.session: - card_details = stripe_utils.get_cards_details_from_payment_method( - request.session.get('id_payment_method') + if 'token' in request.session: + card_details = stripe_utils.get_cards_details_from_token( + request.session.get('token') ) - logger.debug( - "card_details=%s" % (card_details)) if not card_details.get('response_object'): msg = card_details.get('error') - return show_error(msg, self.request) + 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) card_details_response = card_details['response_object'] card_details_dict = { 'last4': card_details_response['last4'], @@ -822,7 +752,7 @@ class OrderConfirmationView(DetailView, FormView): ) if not ucd: acc_result = stripe_utils.associate_customer_card( - stripe_api_cus_id, request.session['id_payment_method'], + stripe_api_cus_id, request.session['token'], set_as_default=True ) if acc_result['response_object'] is None: @@ -832,22 +762,30 @@ class OrderConfirmationView(DetailView, FormView): details=acc_result['error'] ) ) - 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) + 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) elif 'card_id' in request.session: card_id = request.session.get('card_id') user_card_detail = UserCardDetail.objects.get(id=card_id) @@ -856,11 +794,6 @@ 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 = { 'status': False, @@ -878,15 +811,44 @@ class OrderConfirmationView(DetailView, FormView): if ('generic_payment_type' in request.session and self.request.session['generic_payment_type'] == 'generic'): gp_details = self.request.session['generic_payment_details'] - logger.debug("gp_details=%s" % gp_details) 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') + 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) + if ('generic_payment_type' not in request.session or (request.session['generic_payment_details']['recurring'])): recurring_interval = 'month' - logger.debug("'generic_payment_type' not in request.session or" - "(request.session['generic_payment_details']['recurring']") if 'generic_payment_details' in request.session: vat_percent = request.session['generic_payment_details']['vat_rate'] vat_country = request.session['generic_payment_details']['vat_country'] @@ -935,7 +897,6 @@ class OrderConfirmationView(DetailView, FormView): app='dcl', price=amount_to_be_charged ) - logger.debug(specs) stripe_plan = stripe_utils.get_or_create_stripe_plan( amount=amount_to_be_charged, name=plan_name, @@ -982,131 +943,245 @@ 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) - latest_invoice = stripe.Invoice.retrieve( - stripe_subscription_obj.latest_invoice) - subscription_status = '' - if stripe_subscription_obj: - subscription_status = stripe_subscription_obj.status - # Check if the subscription was approved and is active if (stripe_subscription_obj is None - or (stripe_subscription_obj.status != 'active' - and stripe_subscription_obj.status != 'incomplete')): + or stripe_subscription_obj.status != 'active'): # 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') - 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') + 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) + + # 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 ) - pi = stripe.PaymentIntent.retrieve( - 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') + 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 (pi.status == 'requires_attention' or - pi.status == 'requires_source_action'): - logger.debug("Display SCA authentication %s " % pi.status) - context = { - 'sid': stripe_subscription_obj.id, - 'payment_intent_secret': pi.client_secret, - 'STRIPE_PUBLISHABLE_KEY': settings.STRIPE_API_PUBLIC_KEY, - 'showSCA': True, - 'success': { - 'status': True, - 'redirect': redirect_url, - 'msg_title': str(_('Thank you for the order.')), - 'msg_body': str( - _('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, - '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: - 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) - # 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, - stripe_onetime_charge, gp_details, specs, vm_template_id, - template, request.session.get('billing_address_data'), - self.request + 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 ) - if (provisioning_response and - 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, 'redirect': ( @@ -1122,488 +1197,3 @@ class OrderConfirmationView(DetailView, FormView): } return JsonResponse(response) - - -def create_incomplete_intent_request(request): - """ - Creates a dictionary of all session variables so that they could be - picked up in the webhook for processing. - - :param request: - :return: - """ - 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), - 'id_payment_method': request.session.get('id_payment_method', 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=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(email)) - password = CustomUser.get_random_password() - base_url = "{0}://{1}".format(request['scheme'], - request['host']) - custom_user = CustomUser.register( - name, password, - email, - app='dcl', base_url=base_url, send_email=True, - account_details=password - ) - 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 - new_user = authenticate(username=custom_user.email, - password=password) - logger.debug("User %s is authenticated" % custom_user.email) - 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=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) - return custom_user, new_user - - -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) - 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 - ) - else: - 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, - card_details=card_details_response - ) - UserCardDetail.save_default_card_local( - custom_user.stripecustomer.stripe_id, - ucd.card_id - ) - card_details_dict = { - 'last4': ucd.last4, - '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): - stripe_utils = StripeUtils() - acc_result = stripe_utils.associate_customer_card( - 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') - )) - 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) - 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 - }) - 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': ['dcl-orders@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 HttpResponse(status=200) - - -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: - """ - - logger.debug("do_provisioning") - 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( - 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)) - - 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() - 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_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 - 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': ['dcl-orders@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") - 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, - '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} - - user = { - 'name': custom_user.name, - 'email': custom_user.email, - 'username': custom_user.username, - 'pass': custom_user.password, - 'request_scheme': request['scheme'], - 'request_host': request['host'], - 'language': request['language'], - } - - create_vm( - billing_address_data, custom_user.stripecustomer.id, specs, - stripe_subscription_obj, card_details_dict, request, - vm_template_id, template, user - ) - - if real_request: - clear_all_session_vars(real_request) - - -def get_error_response_dict(msg, request): - logger.error(msg) - 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 response - - -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/digitalglarus/locale/de/LC_MESSAGES/django.po b/digitalglarus/locale/de/LC_MESSAGES/django.po index ef7a46b5..ec96f5dc 100644 --- a/digitalglarus/locale/de/LC_MESSAGES/django.po +++ b/digitalglarus/locale/de/LC_MESSAGES/django.po @@ -376,6 +376,8 @@ msgid "" " digitalglarus.ch
\n" " hack4lgarus.ch
\n" " ipv6onlyhosting.com
\n" +" ipv6onlyhosting.ch
\n" +" ipv6onlyhosting.net
\n" " django-hosting.ch
\n" " rails-hosting.ch
\n" " node-hosting.ch
\n" @@ -634,8 +636,8 @@ msgstr "" "Internetangebot der ungleich glarus ag, welches unter den nachfolgenden " "Domains erreichbar ist:

ungleich.ch
datacenterlight.ch
devuanhosting.com
devuanhosting.ch
digitalglarus.ch
hack4lgarus." -"ch
ipv6onlyhosting.com
django-hosting.ch
rails-hosting.ch" -"
node-hosting.ch
blog." +"ch
ipv6onlyhosting.com
ipv6onlyhosting.ch
ipv6onlyhosting.net
django-hosting.ch
rails-hosting.ch
node-hosting.ch
blog." "ungleich.ch

Der Datenschutzbeauftragte des Verantwortlichen ist:

Sanghee Kim
ungleich glarus ag
Bahnhofstrasse 1
8783 " "Linthal (CH)
E-Mail: sanghee." @@ -836,4 +838,3 @@ msgstr "" #~ msgid "index/?$" #~ msgstr "index/?$" - diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 62fe2897..03013ea5 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -631,6 +631,8 @@ GOOGLE_ANALYTICS_PROPERTY_IDS = { 'datacenterlight.ch': 'UA-62285904-8', 'devuanhosting.ch': 'UA-62285904-9', 'devuanhosting.com': 'UA-62285904-9', + 'ipv6onlyhosting.ch': 'UA-62285904-10', + 'ipv6onlyhosting.net': 'UA-62285904-10', 'ipv6onlyhosting.com': 'UA-62285904-10', 'comic.ungleich.ch': 'UA-62285904-13', '127.0.0.1:8000': 'localhost', diff --git a/dynamicweb/settings/prod.py b/dynamicweb/settings/prod.py index 0590ca27..445748ad 100644 --- a/dynamicweb/settings/prod.py +++ b/dynamicweb/settings/prod.py @@ -28,7 +28,9 @@ ALLOWED_HOSTS = [ ".devuanhosting.ch", ".devuanhosting.com", ".digitalezukunft.ch", + ".ipv6onlyhosting.ch", ".ipv6onlyhosting.com", + ".ipv6onlyhosting.net", ".digitalglarus.ch", ".hack4glarus.ch", ".xn--nglarus-n2a.ch" diff --git a/hosting/forms.py b/hosting/forms.py index 8df2bd3e..947cee44 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -2,7 +2,6 @@ import datetime import logging import subprocess import tempfile -import xml from django import forms from django.conf import settings @@ -208,7 +207,7 @@ class UserHostingKeyForm(forms.ModelForm): logger.debug( "Not a correct ssh format {error}".format(error=str(cpe))) raise forms.ValidationError(KEY_ERROR_MESSAGE) - return xml.sax.saxutils.escape(openssh_pubkey_str) + return openssh_pubkey_str def clean_name(self): INVALID_NAME_MESSAGE = _("Comma not accepted in the name of the key") diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 11c4a2a5..08bcdd7a 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: 2021-02-07 10:19+0000\n" +"POT-Creation-Date: 2019-11-15 16:40+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -211,9 +211,6 @@ msgstr "Bezahlbares VM Hosting in der Schweiz" msgid "My Dashboard" msgstr "Mein Dashboard" -msgid "Welcome" -msgstr "" - msgid "My VMs" msgstr "Meine VMs" @@ -367,11 +364,6 @@ msgstr "Abgelehnt" msgid "Billed to" msgstr "Rechnungsadresse" -#, fuzzy -#| msgid "Card Number" -msgid "VAT Number" -msgstr "Kreditkartennummer" - msgid "Payment method" msgstr "Bezahlmethode" @@ -399,9 +391,6 @@ msgstr "Festplattenkapazität" msgid "Subtotal" msgstr "Zwischensumme" -msgid "VAT for" -msgstr "" - msgid "VAT" msgstr "Mehrwertsteuer" @@ -435,22 +424,18 @@ 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" @@ -495,13 +480,11 @@ msgstr "Bestellungsübersicht" #, python-format msgid "" -"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." +"By clicking \"Place order\" this plan will charge your credit card account " +"with %(vm_price)s CHF/month" msgstr "" -"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." +"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(vm_price)s CHF " +"pro Monat belastet" msgid "Place order" msgstr "Bestellen" @@ -521,12 +504,6 @@ msgstr "Schliessen" msgid "Order Nr." msgstr "Bestellung Nr." -msgid "See Invoice" -msgstr "Siehe Rechnung" - -msgid "Page" -msgstr "Seite" - msgid "Your Order" msgstr "Deine Bestellung" @@ -595,19 +572,6 @@ 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" @@ -809,15 +773,21 @@ 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 "" @@ -882,8 +852,7 @@ 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 " @@ -902,9 +871,7 @@ 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" @@ -915,15 +882,6 @@ 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/migrations/0062_incompletesubscriptions.py b/hosting/migrations/0062_incompletesubscriptions.py deleted file mode 100644 index 0405e086..00000000 --- a/hosting/migrations/0062_incompletesubscriptions.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- 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), - ), - ] diff --git a/hosting/migrations/0063_auto_20201223_0612.py b/hosting/migrations/0063_auto_20201223_0612.py deleted file mode 100644 index eb4ca9d4..00000000 --- a/hosting/migrations/0063_auto_20201223_0612.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- 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/migrations/0064_incompletepaymentintents.py b/hosting/migrations/0064_incompletepaymentintents.py deleted file mode 100644 index 868e053e..00000000 --- a/hosting/migrations/0064_incompletepaymentintents.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- 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), - ), - ] diff --git a/hosting/migrations/0065_auto_20201231_1041.py b/hosting/migrations/0065_auto_20201231_1041.py deleted file mode 100644 index 936ccab1..00000000 --- a/hosting/migrations/0065_auto_20201231_1041.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- 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 48238afe..1061cf54 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -1,3 +1,4 @@ +import decimal import json import logging import os @@ -169,12 +170,8 @@ class HostingOrder(AssignPermissionsMixin, models.Model): def set_stripe_charge(self, stripe_charge): self.stripe_charge_id = stripe_charge.id - 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.last4 = stripe_charge.source.last4 + self.cc_brand = stripe_charge.source.brand self.save() def set_subscription_id(self, subscription_id, cc_details): @@ -676,11 +673,7 @@ class UserCardDetail(AssignPermissionsMixin, models.Model): stripe_utils = StripeUtils() cus_response = stripe_utils.get_customer(stripe_api_cus_id) cu = cus_response['response_object'] - 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.default_source = stripe_source_id cu.save() UserCardDetail.save_default_card_local( stripe_api_cus_id, stripe_source_id @@ -748,35 +741,3 @@ class StripeTaxRate(AssignPermissionsMixin, models.Model): display_name = models.CharField(max_length=100) percentage = models.FloatField(default=0) 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.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() - - -class IncompleteSubscriptions(AssignPermissionsMixin, models.Model): - created_at = models.DateTimeField(auto_now_add=True) - 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) - 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/hosting/static/hosting/js/payment.js b/hosting/static/hosting/js/payment.js index 3c4d67da..12d64cf9 100644 --- a/hosting/static/hosting/js/payment.js +++ b/hosting/static/hosting/js/payment.js @@ -84,72 +84,68 @@ $(document).ready(function () { var hasCreditcard = window.hasCreditcard || false; if (!hasCreditcard && window.stripeKey) { var stripe = Stripe(window.stripeKey); - 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")' - } - ], - 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', - 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 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")' } - 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); + ], + 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', + 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 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'); @@ -167,7 +163,7 @@ $(document).ready(function () { if (parts.length === 2) return parts.pop().split(";").shift(); } - function submitBillingForm(pmId) { + function submitBillingForm() { var billing_form = $('#billing-form'); var recurring_input = $('#id_generic_payment_form-recurring'); billing_form.append(''); @@ -178,41 +174,12 @@ $(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(); - - 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; - } - function payWithStripe_new(e) { + $form_new.submit(payWithStripe); + function payWithStripe(e) { e.preventDefault(); function stripeTokenHandler(token) { @@ -230,14 +197,82 @@ $(document).ready(function () { } else { var process_text = "Processing"; if (typeof window.processing_text !== 'undefined') { - process_text = window.processing_text; + process_text = window.processing_text } +lear $form_new.find('[type=submit]').html(process_text + ' '); // Send the token to your server stripeTokenHandler(result.token); } }); + + stripe.createPaymentMethod({ + type: 'card', + card: cardNumberElement + }).then(function(result) { + if (result.error) { + showError(result.error.message); + } else { + // Send paymentMethod.id to server + fetch('/ajax/confirm_payment', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + payment_method_id: result.paymentMethod.id + }) + }).then(function(result) { + // Handle server response (see Step 3) + result.json().then(function(json) { + handleServerResponse(json); + }); + }); + } + }); + + function showError(msg) { + var errorElement = document.getElementById('card-errors'); + errorElement.textContent = msg; + } + + function handleServerResponse(response) { + if (response.error) { + // Show error from server on payment form + showError(response.error.message); + } else if (response.requires_action) { + // Use Stripe.js to handle required card action + handleAction(response); + } else { + // Show success message + } + } + + function handleAction(response) { + stripe.handleCardAction( + response.payment_intent_client_secret + ).then(function(result) { + if (result.error) { + // Show error in payment form + } else { + // The card action has been handled + // The PaymentIntent can be confirmed again on the server + fetch('/ajax/confirm_payment', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + payment_intent_id: result.paymentIntent.id + }) + }).then(function(confirmResult) { + return confirmResult.json(); + }).then(handleServerResponse); + } + }); + } + } /* Form validation */ diff --git a/hosting/static/hosting/js/virtual_machine_detail.js b/hosting/static/hosting/js/virtual_machine_detail.js index 72770182..4a60d32b 100644 --- a/hosting/static/hosting/js/virtual_machine_detail.js +++ b/hosting/static/hosting/js/virtual_machine_detail.js @@ -92,102 +92,47 @@ $(document).ready(function() { }); var create_vm_form = $('#virtual_machine_create_form'); - 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'); - } - } - }, - error: function (xmlhttprequest, textstatus, message) { + 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'); + $('#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) { 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; + } }); - } 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); - } - }); - } - } + return false; + }); $('#createvm-modal').on('hidden.bs.modal', function () { $(this).find('.modal-footer .btn').addClass('hide'); }); diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index dee453d5..9256271a 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" you agree to our Terms of Service and 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" this plan will charge your credit card account with {{ vm_price }} CHF/month{% endblocktrans %}.