Compare commits

..

1 commit

Author SHA1 Message Date
PCoder
8472bdd097 Add django error logger 2020-06-11 10:11:46 +05:30
51 changed files with 928 additions and 2861 deletions

View file

@ -1,2 +0,0 @@
.git
.env

View file

@ -1,25 +1,3 @@
3.4: 2022-04-14
* 11566: Fix for ungleich.ch product section alignment
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)
2.12: 2020-06-23
* 7894: Show one time payment invoices (MR!738)
2.11: 2020-06-11
* Bugfix: Correct the wrong constant name (caused payment to go thru and showing error and VMs not instantiated)
2.10.8: 2020-06-10 2.10.8: 2020-06-10
* #8102: Refactor MAX_TIME_TO_WAIT_FOR_VM_TERMINATE to increase time to poll whether VM has been terminated or not (MR!737) * #8102: Refactor MAX_TIME_TO_WAIT_FOR_VM_TERMINATE to increase time to poll whether VM has been terminated or not (MR!737)
2.10.7: 2020-05-25 2.10.7: 2020-05-25

View file

@ -1,32 +0,0 @@
# FROM python:3.10.0-alpine3.15
FROM python:3.5-alpine3.12
WORKDIR /usr/src/app
RUN apk add --update --no-cache \
git \
build-base \
openldap-dev \
python3-dev \
postgresql-dev \
jpeg-dev \
libxml2-dev \
libxslt-dev \
libmemcached-dev \
zlib-dev \
&& rm -rf /var/cache/apk/*
## For alpine 3.15 replace postgresql-dev with libpq-dev
# FIX https://github.com/python-ldap/python-ldap/issues/432
RUN echo 'INPUT ( libldap.so )' > /usr/lib/libldap_r.so
COPY requirements.txt ./
# Pillow seems to need LIBRARY_PATH set as follows: (see: https://github.com/python-pillow/Pillow/issues/1763#issuecomment-222383534)
RUN LIBRARY_PATH=/lib:/usr/lib /bin/sh -c "pip install --no-cache-dir -r requirements.txt"
COPY ./ .
COPY entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh" ]

View file

@ -14,12 +14,6 @@ help:
@echo ' make rsync_upload ' @echo ' make rsync_upload '
@echo ' make install_debian_packages ' @echo ' make install_debian_packages '
buildimage:
docker build -t dynamicweb:$$(git describe) .
releaseimage: buildimage
./release.sh
collectstatic: collectstatic:
$(PY?) $(BASEDIR)/manage.py collectstatic $(PY?) $(BASEDIR)/manage.py collectstatic

View file

@ -31,10 +31,9 @@ class ContactView(FormView):
return context return context
def form_valid(self, form): def form_valid(self, form):
print("alplora contactusform") form.save()
#form.save() form.send_email(email_to='info@alplora.ch')
#form.send_email(email_to='info@alplora.ch') messages.add_message(self.request, messages.SUCCESS, self.success_message)
#messages.add_message(self.request, messages.SUCCESS, self.success_message)
return render(self.request, 'alplora/contact_success.html', {}) return render(self.request, 'alplora/contact_success.html', {})

View file

@ -1,23 +0,0 @@
#!/bin/sh
if [ $# -lt 1 ]; then
echo "$0 imageversion [push]"
echo "Version could be: $(git describe --always)"
echo "If push is specified, also push to our harbor"
exit 1
fi
tagprefix=harbor.k8s.ungleich.ch/ungleich-public/dynamicweb
version=$1; shift
tag=${tagprefix}:${version}
set -ex
docker build -t "${tag}" .
push=$1; shift
if [ "$push" ]; then
docker push "${tag}"
fi

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: 2018-03-30 23:22+0000\n"
"Last-Translator: b'Anonymous User <coder.purple+25@gmail.com>'\n" "Last-Translator: b'Anonymous User <coder.purple+25@gmail.com>'\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -451,43 +451,38 @@ msgstr "Dein Gesamtpreis"
#, python-format #, python-format
msgid "" msgid ""
" By clicking \"Place order\" you agree to our <a href=\"https://" "By clicking \"Place order\" this plan will charge your credit card account "
"datacenterlight.ch/en-us/cms/terms-of-service/\">Terms of Service</a> and " "with %(total_price)s CHF/year"
"this plan will charge your credit card account with %(total_price)s CHF/year"
msgstr "" msgstr ""
"Indem Du auf \"Bestellung aufgeben\" klickst, erklärst Du dich mit unseren <a href=\"https://" "Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(total_price)s "
"datacenterlight.ch/en-us/cms/terms-of-service/\">Nutzungsbedingungen</a> einverstanden und Dein Kreditkartenkonto wird mit %(total_price)s CHF/Jahr belastet." "CHF pro Jahr belastet"
#, python-format #, python-format
msgid "" msgid ""
"\n" "By clicking \"Place order\" this plan will charge your credit card account "
" By clicking \"Place order\" you agree to " "with %(total_price)s CHF/month"
"our <a href=\"https://datacenterlight.ch/en-us/cms/terms-of-service/\">Terms "
"of Service</a> and this plan will charge your credit card account with "
"%(total_price)s CHF/month"
msgstr "" msgstr ""
"\n" "Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(total_price)s "
"Indem Du auf \"Bestellung aufgeben\" klickst, erklärst Du dich mit unseren <a href=\"https://" "CHF pro Monat belastet"
"datacenterlight.ch/en-us/cms/terms-of-service/\">Nutzungsbedingungen</a> einverstanden und Dein Kreditkartenkonto wird mit %(total_price)s CHF/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 #, python-format
msgid "" msgid ""
"By clicking \"Place order\" you agree to our <a href=\"https://" "By clicking \"Place order\" this plan will charge your credit card account "
"datacenterlight.ch/en-us/cms/terms-of-service/\">Terms of Service</a> and " "with %(vm_total_price)s CHF/month"
"this plan will charge your credit card account with %(total_price)s CHF"
msgstr "" msgstr ""
"Indem Du auf \"Bestellung aufgeben\" klickst, erklärst Du dich mit unseren <a href=\"https://" "Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit "
"datacenterlight.ch/de/cms/terms-of-service/\">Nutzungsbedingungen</a> einverstanden und Dein Kreditkartenkonto wird mit %(total_price)s CHF belastet." "%(vm_total_price)s CHF pro Monat belastet"
#, python-format
msgid ""
"By clicking \"Place order\" you agree to our <a href=\"https://"
"datacenterlight.ch/en-us/cms/terms-of-service/\">Terms of Service</a> 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 <a href=\"https://"
"datacenterlight.ch/de/cms/terms-of-service/\">Nutzungsbedingungen</a> einverstanden und Dein Kreditkartenkonto wird mit %(vm_total_price)s CHF/Monat belastet"
msgid "Place order" msgid "Place order"
msgstr "Bestellen" msgstr "Bestellen"
@ -606,22 +601,16 @@ msgid "Incorrect pricing name. Please contact support{support_email}"
msgstr "" msgstr ""
"Ungültige Preisbezeichnung. Bitte kontaktiere den Support{support_email}" "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" msgid "Confirm Order"
msgstr "Bestellung Bestätigen" 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." msgid "Error."
msgstr "" msgstr ""
@ -632,21 +621,10 @@ msgstr ""
"Es ist ein Fehler bei der Zahlung betreten. Du wirst nach dem Schliessen vom " "Es ist ein Fehler bei der Zahlung betreten. Du wirst nach dem Schliessen vom "
"Popup zur Bezahlseite weitergeleitet." "Popup zur Bezahlseite weitergeleitet."
msgid "Thank you for the order." #, python-brace-format
msgstr "Danke für Deine Bestellung." msgid "An error occurred while associating the card. Details: {details}"
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"
msgstr "" msgstr ""
"Beim Verbinden der Karte ist ein Fehler aufgetreten. Details: {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 " This is a monthly recurring plan." msgid " This is a monthly recurring plan."
msgstr "Dies ist ein monatlich wiederkehrender 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 " "Du wirst bald eine Bestätigungs-E-Mail über die Zahlung erhalten. Du kannst "
"jederzeit unter info@ungleich.ch kontaktieren." "jederzeit unter info@ungleich.ch kontaktieren."
#, python-format msgid "Thank you for the order."
#~ msgid "" msgstr "Danke für Deine Bestellung."
#~ "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"
#, fuzzy, python-format msgid ""
#~| msgid "" "Your VM will be up and running in a few moments. We will send you a "
#~| "By clicking \"Place order\" this payment will charge your credit card " "confirmation email as soon as it is ready."
#~| "account with a one time amount of %(total_price)s CHF" msgstr ""
#~ msgid "" "Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du "
#~ "By clicking \"Place order\" this payment will charge your credit card " "auf sie zugreifen kannst."
#~ "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 "Price" #~ msgid "Price"
#~ msgstr "Preise" #~ msgstr "Preise"
@ -786,6 +748,9 @@ msgstr ""
#~ "Wir werden dann sobald als möglich Ihren Beta-Zugang erstellen und Sie " #~ "Wir werden dann sobald als möglich Ihren Beta-Zugang erstellen und Sie "
#~ "daraufhin kontaktieren.Bis dahin bitten wir Sie um etwas Geduld." #~ "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" #~ msgid "Thank you for order! Our team will contact you via email"
#~ msgstr "" #~ msgstr ""
#~ "Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich " #~ "Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich "

View file

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

View file

@ -1,54 +0,0 @@
from django.core.management.base import BaseCommand
from datacenterlight.tasks import handle_metadata_and_emails
from datacenterlight.models import StripePlan
from opennebula_api.models import OpenNebulaManager
from membership.models import CustomUser
from hosting.models import GenericProduct
import logging
import json
import sys
import stripe
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = '''Stripe plans created before version 3.4 saved the plan name like generic-{subscription_id}-amount. This
command aims at replacing this with the actual product name
'''
def handle(self, *args, **options):
cnt = 0
self.stdout.write(
self.style.SUCCESS(
'In Fix generic stripe plan product names'
)
)
plans_to_change = StripePlan.objects.filter(stripe_plan_id__startswith='generic')
for plan in plans_to_change:
response = input("Press 'y' to continue: ")
# Check if the user entered 'y'
if response.lower() == 'y':
plan_name = plan.stripe_plan_id
first_index_hyphen = plan_name.index("-") + 1
product_id = plan_name[
first_index_hyphen:(plan_name[first_index_hyphen:].index("-")) + first_index_hyphen]
gp = GenericProduct.objects.get(id=product_id)
if gp:
cnt += 1
# update stripe
sp = stripe.Plan.retrieve(plan_name)
pr = stripe.Product.retrieve(sp.product)
pr.name = gp.product_name
pr.save()
# update local
spl = StripePlan.objects.get(stripe_plan_id=plan_name)
spl.stripe_plan_name = gp.product_name
spl.save()
print("%s. %s => %s" % (cnt, plan_name, gp.product_name))
else:
print("Invalid input. Please try again.")
sys.exit()
print("Done")

View file

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

View file

@ -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)) "Running create_vm_task on {}".format(current_task.request.hostname))
vm_id = None vm_id = None
try: try:
final_price = (
specs.get('total_price') if 'total_price' in specs
else specs.get('price')
)
if 'pass' in user: if 'pass' in user:
on_user = user.get('username') on_user = user.get('username')
on_pass = user.get('pass') 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: if vm_id is None:
raise Exception("Could not create VM") raise Exception("Could not create VM")
handle_metadata_and_emails(order_id, vm_id, manager, user, specs, # Update HostingOrder with the created vm_id
template) 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: except Exception as e:
logger.error(str(e)) logger.error(str(e))
try: try:
@ -110,127 +214,3 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id):
return return
return vm_id 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)

View file

@ -2,14 +2,6 @@
{% load staticfiles bootstrap3 i18n custom_tags humanize %} {% load staticfiles bootstrap3 i18n custom_tags humanize %}
{% block content %} {% block content %}
<script>
{% if payment_intent_secret %}
console.log("payment_intent_secret");
window.paymentIntentSecret = "{{payment_intent_secret}}";
{% else %}
console.log("No payment_intent_secret");
{% endif %}
</script>
<div id="order-detail{{order.pk}}" class="order-detail-container"> <div id="order-detail{{order.pk}}" class="order-detail-container">
{% if messages %} {% if messages %}
<div class="alert alert-warning"> <div class="alert alert-warning">
@ -111,61 +103,58 @@
</p> </p>
{% endif %} {% endif %}
</div> </div>
{% if generic_payment_details.exclude_vat_calculations %} <div class="col-sm-12">
{% else %} <hr class="thin-hr">
<div class="col-sm-12"> </div>
<hr class="thin-hr"> <div class="col-sm-9">
<p>
<strong class="text-uppercase">{% trans "Price Before VAT" %}</strong>
<strong class="pull-right">{{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF</strong>
</p>
</div>
<div class="col-sm-12">
<hr class="thin-hr">
</div>
<div class="col-sm-9">
<div class="row">
<div class="col-md-4 col-sm-4 col-xs-4">
<p><span></span></p>
</div>
<div class="col-md-3 col-sm-3 col-xs-4">
<p class="text-right"><strong class="cmf-ord-heading">{% trans "Pre VAT" %}</strong></p>
</div>
<div class="col-md-5 col-sm-5 col-xs-4 header-no-left-padding">
<p class="text-right"><strong class="cmf-ord-heading">{% trans "VAT for" %} {{generic_payment_details.vat_country}} ({{generic_payment_details.vat_rate}}%)</strong></p>
</div>
</div> </div>
<div class="col-sm-9"> <div class="row">
<p> <div class="col-md-4 col-sm-4 col-xs-4">
<strong class="text-uppercase">{% trans "Price Before VAT" %}</strong> <p><span>Price</span></p>
<strong class="pull-right">{{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF</strong> </div>
</p> <div class="col-md-3 col-sm-3 col-xs-4">
<p><span class="pull-right" >{{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF</span></p>
</div>
<div class="col-md-5 col-sm-5 col-xs-4">
<p><span class="pull-right">{{generic_payment_details.amount|floatformat:2|intcomma}} CHF</span></p>
</div>
</div> </div>
<div class="col-sm-12"> </div>
<hr class="thin-hr"> <div class="col-sm-12">
<hr class="thin-hr">
</div>
<div class="col-sm-9">
<div class="row">
<div class="col-md-4 col-sm-4 col-xs-4">
<p><strong>Total</strong></p>
</div>
<div class="col-md-3 col-sm-3 col-xs-4">
<p><strong class="pull-right">{{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF</strong></p>
</div>
<div class="col-md-5 col-sm-5 col-xs-4">
<p><strong class="pull-right">{{generic_payment_details.amount|floatformat:2|intcomma}} CHF</strong></p>
</div>
</div> </div>
<div class="col-sm-9"> </div>
<div class="row">
<div class="col-md-4 col-sm-4 col-xs-4">
<p><span></span></p>
</div>
<div class="col-md-3 col-sm-3 col-xs-4">
<p class="text-right"><strong class="cmf-ord-heading">{% trans "Pre VAT" %}</strong></p>
</div>
<div class="col-md-5 col-sm-5 col-xs-4 header-no-left-padding">
<p class="text-right"><strong class="cmf-ord-heading">{% trans "VAT for" %} {{generic_payment_details.vat_country}} ({{generic_payment_details.vat_rate}}%)</strong></p>
</div>
</div>
<div class="row">
<div class="col-md-4 col-sm-4 col-xs-4">
<p><span>Price</span></p>
</div>
<div class="col-md-3 col-sm-3 col-xs-4">
<p><span class="pull-right" >{{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF</span></p>
</div>
<div class="col-md-5 col-sm-5 col-xs-4">
<p><span class="pull-right">{{generic_payment_details.amount|floatformat:2|intcomma}} CHF</span></p>
</div>
</div>
</div>
<div class="col-sm-12">
<hr class="thin-hr">
</div>
<div class="col-sm-9">
<div class="row">
<div class="col-md-4 col-sm-4 col-xs-4">
<p><strong>Total</strong></p>
</div>
<div class="col-md-3 col-sm-3 col-xs-4">
<p><strong class="pull-right">{{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF</strong></p>
</div>
<div class="col-md-5 col-sm-5 col-xs-4">
<p><strong class="pull-right">{{generic_payment_details.amount|floatformat:2|intcomma}} CHF</strong></p>
</div>
</div>
</div>
{% endif %}
<div class="col-sm-12"> <div class="col-sm-12">
<hr class="thin-hr"> <hr class="thin-hr">
</div> </div>
@ -278,16 +267,15 @@
{% if generic_payment_details %} {% if generic_payment_details %}
{% if generic_payment_details.recurring %} {% if generic_payment_details.recurring %}
{% if generic_payment_details.recurring_interval == 'year' %} {% if generic_payment_details.recurring_interval == 'year' %}
<div class="dcl-place-order-text">{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %} By clicking "Place order" you agree to our <a href="https://datacenterlight.ch/en-us/cms/terms-of-service/">Terms of Service</a> and this plan will charge your credit card account with {{ total_price }} CHF/year{% endblocktrans %}.</div> <div class="dcl-place-order-text">{% 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 %}.</div>
{% else %} {% else %}
<div class="dcl-place-order-text">{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %} <div class="dcl-place-order-text">{% 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 %}.</div>
By clicking "Place order" you agree to our <a href="https://datacenterlight.ch/en-us/cms/terms-of-service/">Terms of Service</a> and this plan will charge your credit card account with {{ total_price }} CHF/month{% endblocktrans %}.</div>
{% endif %} {% endif %}
{% else %} {% else %}
<div class="dcl-place-order-text">{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" you agree to our <a href="https://datacenterlight.ch/en-us/cms/terms-of-service/">Terms of Service</a> and this plan will charge your credit card account with {{ total_price }} CHF{% endblocktrans %}.</div> <div class="dcl-place-order-text">{% 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 %}.</div>
{% endif %} {% endif %}
{% else %} {% else %}
<div class="dcl-place-order-text">{% blocktrans with vm_total_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" you agree to our <a href="https://datacenterlight.ch/en-us/cms/terms-of-service/">Terms of Service</a> and this plan will charge your credit card account with {{ vm_total_price }} CHF/month{% endblocktrans %}.</div> <div class="dcl-place-order-text">{% 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 %}.</div>
{% endif %} {% endif %}
</div> </div>
<div class="col-sm-4 order-confirm-btn text-right"> <div class="col-sm-4 order-confirm-btn text-right">
@ -330,14 +318,5 @@
<script type="text/javascript"> <script type="text/javascript">
{% trans "Some problem encountered. Please try again later." as err_msg %} {% trans "Some problem encountered. Please try again later." as err_msg %}
var create_vm_error_message = '{{err_msg|safe}}'; var create_vm_error_message = '{{err_msg|safe}}';
var pm_id = '{{id_payment_method}}';
var error_url = '{{ error_msg.redirect }}';
var error_msg = '{{ error_msg.msg_body }}';
var error_title = '{{ error_msg.msg_title }}';
var success_msg = '{{ success_msg.msg_body }}';
var success_title = '{{ success_msg.msg_title }}';
var success_url = '{{ success_msg.redirect }}';
window.stripeKey = "{{stripe_key}}";
window.isSubscription = ("{{is_subscription}}" === 'true');
</script> </script>
{%endblock%} {%endblock%}

View file

@ -6,7 +6,7 @@ from django.core.urlresolvers import resolve, reverse
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import activate, get_language, ugettext_lazy as _ from django.utils.translation import activate, get_language, ugettext_lazy as _
from hosting.models import GenericProduct, HostingOrder from hosting.models import GenericProduct
from utils.hosting_utils import get_ip_addresses from utils.hosting_utils import get_ip_addresses
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -63,42 +63,6 @@ def escaped_line_break(value):
return value.replace("\\n", "\n") return value.replace("\\n", "\n")
@register.filter('get_line_item_from_hosting_order_charge')
def get_line_item_from_hosting_order_charge(hosting_order_id):
"""
Returns ready-to-use "html" line item to be shown for a charge in the
invoice list page
:param hosting_order_id: the HostingOrder id
:return:
"""
try:
print("Hositng order id = %s" % hosting_order_id)
hosting_order = HostingOrder.objects.get(id = hosting_order_id)
if hosting_order.stripe_charge_id:
return mark_safe("""
<td class="xs-td-inline">{product_name}</td>
<td class="xs-td-inline">{created_at}</td>
<td class="xs-td-inline">{total}</td>
<td class="text-right last-td">
<a class="btn btn-order-detail" href="{receipt_url}" target="_blank">{see_invoice_text}</a>
</td>
""".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))
return ""
@register.filter('get_line_item_from_stripe_invoice') @register.filter('get_line_item_from_stripe_invoice')
def get_line_item_from_stripe_invoice(invoice): def get_line_item_from_stripe_invoice(invoice):
""" """
@ -115,7 +79,7 @@ def get_line_item_from_stripe_invoice(invoice):
plan_name = "" plan_name = ""
for line_data in invoice["lines"]["data"]: for line_data in invoice["lines"]["data"]:
if is_first: 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 start_date = line_data.period.start
end_date = line_data.period.end end_date = line_data.period.end
is_first = False is_first = False
@ -145,7 +109,7 @@ def get_line_item_from_stripe_invoice(invoice):
""".format( """.format(
vm_id=vm_id if vm_id > 0 else "", vm_id=vm_id if vm_id > 0 else "",
ip_addresses=mark_safe(get_ip_addresses(vm_id)) if vm_id > 0 else ip_addresses=mark_safe(get_ip_addresses(vm_id)) if vm_id > 0 else
mark_safe(get_product_name(plan_name)) if plan_name.startswith("generic-") else plan_name, mark_safe(get_product_name(plan_name)),
period=mark_safe("%s &mdash; %s" % ( period=mark_safe("%s &mdash; %s" % (
datetime.datetime.fromtimestamp(start_date).strftime('%Y-%m-%d'), datetime.datetime.fromtimestamp(start_date).strftime('%Y-%m-%d'),
datetime.datetime.fromtimestamp(end_date).strftime('%Y-%m-%d'))), datetime.datetime.fromtimestamp(end_date).strftime('%Y-%m-%d'))),
@ -161,7 +125,8 @@ def get_product_name(plan_name):
product_name = "" product_name = ""
if plan_name and plan_name.startswith("generic-"): if plan_name and plan_name.startswith("generic-"):
first_index_hyphen = plan_name.index("-") + 1 first_index_hyphen = plan_name.index("-") + 1
product_id = plan_name[first_index_hyphen:(plan_name[first_index_hyphen:].index("-")) + first_index_hyphen] product_id = plan_name[first_index_hyphen:
(plan_name[first_index_hyphen:].index("-")) + first_index_hyphen]
try: try:
product = GenericProduct.objects.get(id=product_id) product = GenericProduct.objects.get(id=product_id)
product_name = product.product_name product_name = product.product_name

View file

@ -20,7 +20,7 @@ logger = logging.getLogger(__name__)
eu_countries = ['at', 'be', 'bg', 'ch', 'cy', 'cz', 'hr', 'dk', eu_countries = ['at', 'be', 'bg', 'ch', 'cy', 'cz', 'hr', 'dk',
'ee', 'fi', 'fr', 'mc', 'de', 'gr', 'hu', 'ie', 'it', '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'] 'se', 'gb']
@ -38,7 +38,6 @@ def get_cms_integration(name):
def create_vm(billing_address_data, stripe_customer_id, specs, def create_vm(billing_address_data, stripe_customer_id, specs,
stripe_subscription_obj, card_details_dict, request, stripe_subscription_obj, card_details_dict, request,
vm_template_id, template, user): vm_template_id, template, user):
logger.debug("In create_vm")
billing_address = BillingAddress( billing_address = BillingAddress(
cardholder_name=billing_address_data['cardholder_name'], cardholder_name=billing_address_data['cardholder_name'],
street_address=billing_address_data['street_address'], 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) create_vm_task.delay(vm_template_id, user, specs, template, order.id)
clear_all_session_vars(request)
def clear_all_session_vars(request): def clear_all_session_vars(request):
if request.session is not None: if request.session is not None:
@ -111,8 +112,7 @@ def clear_all_session_vars(request):
'token', 'customer', 'generic_payment_type', 'token', 'customer', 'generic_payment_type',
'generic_payment_details', 'product_id', 'generic_payment_details', 'product_id',
'order_confirm_url', 'new_user_hosting_key_id', 'order_confirm_url', 'new_user_hosting_key_id',
'vat_validation_status', 'billing_address_id', 'vat_validation_status', 'billing_address_id']:
'id_payment_method']:
if session_var in request.session: if session_var in request.session:
del request.session[session_var] del request.session[session_var]

File diff suppressed because it is too large Load diff

View file

@ -376,6 +376,8 @@ msgid ""
" digitalglarus.ch<br/>\n" " digitalglarus.ch<br/>\n"
" hack4lgarus.ch<br/>\n" " hack4lgarus.ch<br/>\n"
" ipv6onlyhosting.com<br/>\n" " ipv6onlyhosting.com<br/>\n"
" ipv6onlyhosting.ch<br/>\n"
" ipv6onlyhosting.net<br/>\n"
" django-hosting.ch<br/>\n" " django-hosting.ch<br/>\n"
" rails-hosting.ch<br/>\n" " rails-hosting.ch<br/>\n"
" node-hosting.ch<br/>\n" " node-hosting.ch<br/>\n"
@ -634,8 +636,8 @@ msgstr ""
"Internetangebot der ungleich glarus ag, welches unter den nachfolgenden " "Internetangebot der ungleich glarus ag, welches unter den nachfolgenden "
"Domains erreichbar ist:<br/><br/>ungleich.ch<br/>datacenterlight.ch<br/" "Domains erreichbar ist:<br/><br/>ungleich.ch<br/>datacenterlight.ch<br/"
">devuanhosting.com<br/>devuanhosting.ch<br/>digitalglarus.ch<br/>hack4lgarus." ">devuanhosting.com<br/>devuanhosting.ch<br/>digitalglarus.ch<br/>hack4lgarus."
"ch<br/>ipv6onlyhosting.com<br/>django-hosting.ch<br/>rails-hosting.ch" "ch<br/>ipv6onlyhosting.com<br/>ipv6onlyhosting.ch<br/>ipv6onlyhosting.net<br/"
"<br/>node-hosting.ch<br/>blog." ">django-hosting.ch<br/>rails-hosting.ch<br/>node-hosting.ch<br/>blog."
"ungleich.ch<br/><br/>Der Datenschutzbeauftragte des Verantwortlichen ist:<br/" "ungleich.ch<br/><br/>Der Datenschutzbeauftragte des Verantwortlichen ist:<br/"
"><br/>Sanghee Kim<br/>ungleich glarus ag<br/>Bahnhofstrasse 1<br/>8783 " "><br/>Sanghee Kim<br/>ungleich glarus ag<br/>Bahnhofstrasse 1<br/>8783 "
"Linthal (CH)<br/>E-Mail: <a href=\"mailto:sanghee.kim@ungleich.ch\">sanghee." "Linthal (CH)<br/>E-Mail: <a href=\"mailto:sanghee.kim@ungleich.ch\">sanghee."
@ -836,4 +838,3 @@ msgstr ""
#~ msgid "index/?$" #~ msgid "index/?$"
#~ msgstr "index/?$" #~ msgstr "index/?$"

View file

@ -835,10 +835,9 @@ class ContactView(FormView):
success_message = _('Message Successfully Sent') success_message = _('Message Successfully Sent')
def form_valid(self, form): def form_valid(self, form):
print("digital glarus contactusform") form.save()
#form.save() form.send_email()
#form.send_email() messages.add_message(self.request, messages.SUCCESS, self.success_message)
#messages.add_message(self.request, messages.SUCCESS, self.success_message)
return super(ContactView, self).form_valid(form) return super(ContactView, self).form_valid(form)

View file

@ -56,9 +56,6 @@ dotenv.load_dotenv("{0}/.env".format(PROJECT_DIR))
from multisite import SiteID from multisite import SiteID
RECAPTCHA_PUBLIC_KEY = env('RECAPTCHA_PUBLIC_KEY')
RECAPTCHA_PRIVATE_KEY = env('RECAPTCHA_PRIVATE_KEY')
UNGLEICH_BLOG_SITE_ID = int_env("UNGLEICH_BLOG_SITE_ID") UNGLEICH_BLOG_SITE_ID = int_env("UNGLEICH_BLOG_SITE_ID")
SITE_ID = SiteID(default=(UNGLEICH_BLOG_SITE_ID if SITE_ID = SiteID(default=(UNGLEICH_BLOG_SITE_ID if
UNGLEICH_BLOG_SITE_ID > 0 else 1)) UNGLEICH_BLOG_SITE_ID > 0 else 1))
@ -128,7 +125,6 @@ INSTALLED_APPS = (
'djangocms_file', 'djangocms_file',
'djangocms_picture', 'djangocms_picture',
'djangocms_video', 'djangocms_video',
'django_recaptcha',
# 'djangocms_flash', # 'djangocms_flash',
# 'djangocms_googlemap', # 'djangocms_googlemap',
# 'djangocms_inherit', # 'djangocms_inherit',
@ -635,6 +631,8 @@ GOOGLE_ANALYTICS_PROPERTY_IDS = {
'datacenterlight.ch': 'UA-62285904-8', 'datacenterlight.ch': 'UA-62285904-8',
'devuanhosting.ch': 'UA-62285904-9', 'devuanhosting.ch': 'UA-62285904-9',
'devuanhosting.com': 'UA-62285904-9', 'devuanhosting.com': 'UA-62285904-9',
'ipv6onlyhosting.ch': 'UA-62285904-10',
'ipv6onlyhosting.net': 'UA-62285904-10',
'ipv6onlyhosting.com': 'UA-62285904-10', 'ipv6onlyhosting.com': 'UA-62285904-10',
'comic.ungleich.ch': 'UA-62285904-13', 'comic.ungleich.ch': 'UA-62285904-13',
'127.0.0.1:8000': 'localhost', '127.0.0.1:8000': 'localhost',
@ -686,6 +684,17 @@ if ENABLE_LOGGING:
} }
} }
loggers_dict.update(logger_item) loggers_dict.update(logger_item)
if not 'django' in MODULES_TO_LOG:
loggers_dict.update(
{
'django': {
'handlers': ['django_error'],
'level': 'ERROR',
'propagate': True
}
}
)
custom_handler_item = { custom_handler_item = {
'custom_file': { 'custom_file': {
@ -699,6 +708,18 @@ if ENABLE_LOGGING:
'maxBytes': 1024 * 1024 * 5, 'maxBytes': 1024 * 1024 * 5,
'backupCount': 10, 'backupCount': 10,
'formatter': 'standard', 'formatter': 'standard',
},
'django_error': {
'level': 'ERROR',
'class': 'logging.handlers.RotatingFileHandler',
'filename':
"{PROJECT_DIR}/logs/django-error.log".format(
LEVEL=LOG_LEVEL.lower(),
PROJECT_DIR=PROJECT_DIR
),
'maxBytes': 1024 * 1024 * 5,
'backupCount': 10,
'formatter': 'standard',
} }
} }
handlers_dict.update(custom_handler_item) handlers_dict.update(custom_handler_item)
@ -777,9 +798,3 @@ if DEBUG:
from .local import * # flake8: noqa from .local import * # flake8: noqa
else: else:
from .prod import * # flake8: noqa from .prod import * # flake8: noqa
# Try to load dynamic configuration, if it exists
try:
from .dynamic import * # flake8: noqa
except ImportError:
pass

View file

@ -28,7 +28,9 @@ ALLOWED_HOSTS = [
".devuanhosting.ch", ".devuanhosting.ch",
".devuanhosting.com", ".devuanhosting.com",
".digitalezukunft.ch", ".digitalezukunft.ch",
".ipv6onlyhosting.ch",
".ipv6onlyhosting.com", ".ipv6onlyhosting.com",
".ipv6onlyhosting.net",
".digitalglarus.ch", ".digitalglarus.ch",
".hack4glarus.ch", ".hack4glarus.ch",
".xn--nglarus-n2a.ch" ".xn--nglarus-n2a.ch"

View file

@ -1,19 +0,0 @@
#!/bin/sh
set -uex
cd /usr/src/app/
cat > dynamicweb/settings/dynamic.py <<EOF
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': '${POSTGRES_DB}',
'USER': '${POSTGRES_USER}',
'PASSWORD': '${POSTGRES_PASSWORD}',
'HOST': '${POSTGRES_HOST}',
'PORT': '5432',
}
}
EOF
exec "$@"

View file

@ -2,7 +2,6 @@ import datetime
import logging import logging
import subprocess import subprocess
import tempfile import tempfile
import xml
from django import forms from django import forms
from django.conf import settings from django.conf import settings
@ -208,7 +207,7 @@ class UserHostingKeyForm(forms.ModelForm):
logger.debug( logger.debug(
"Not a correct ssh format {error}".format(error=str(cpe))) "Not a correct ssh format {error}".format(error=str(cpe)))
raise forms.ValidationError(KEY_ERROR_MESSAGE) raise forms.ValidationError(KEY_ERROR_MESSAGE)
return xml.sax.saxutils.escape(openssh_pubkey_str) return openssh_pubkey_str
def clean_name(self): def clean_name(self):
INVALID_NAME_MESSAGE = _("Comma not accepted in the name of the key") INVALID_NAME_MESSAGE = _("Comma not accepted in the name of the key")

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -211,9 +211,6 @@ msgstr "Bezahlbares VM Hosting in der Schweiz"
msgid "My Dashboard" msgid "My Dashboard"
msgstr "Mein Dashboard" msgstr "Mein Dashboard"
msgid "Welcome"
msgstr ""
msgid "My VMs" msgid "My VMs"
msgstr "Meine VMs" msgstr "Meine VMs"
@ -367,11 +364,6 @@ msgstr "Abgelehnt"
msgid "Billed to" msgid "Billed to"
msgstr "Rechnungsadresse" msgstr "Rechnungsadresse"
#, fuzzy
#| msgid "Card Number"
msgid "VAT Number"
msgstr "Kreditkartennummer"
msgid "Payment method" msgid "Payment method"
msgstr "Bezahlmethode" msgstr "Bezahlmethode"
@ -399,9 +391,6 @@ msgstr "Festplattenkapazität"
msgid "Subtotal" msgid "Subtotal"
msgstr "Zwischensumme" msgstr "Zwischensumme"
msgid "VAT for"
msgstr ""
msgid "VAT" msgid "VAT"
msgstr "Mehrwertsteuer" msgstr "Mehrwertsteuer"
@ -435,22 +424,18 @@ msgstr "ZURÜCK ZUR LISTE"
msgid "Some problem encountered. Please try again later." msgid "Some problem encountered. Please try again later."
msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal." 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" msgid "VM ID"
msgstr "" msgstr ""
msgid "IP Address" msgid "IP Address"
msgstr "IP-Adresse" msgstr "IP-Adresse"
msgid "See Invoice"
msgstr "Siehe Rechnung"
msgid "Page"
msgstr "Seite"
msgid "Log in" msgid "Log in"
msgstr "Anmelden" msgstr "Anmelden"
@ -495,13 +480,11 @@ msgstr "Bestellungsübersicht"
#, python-format #, python-format
msgid "" msgid ""
"By clicking \"Place order\" you agree to our <a href=\"https://" "By clicking \"Place order\" this plan will charge your credit card account "
"datacenterlight.ch/en-us/cms/terms-of-service/\">Terms of Service</a> and " "with %(vm_price)s CHF/month"
"this plan will charge your credit card account with %(vm_price)s CHF/month."
msgstr "" msgstr ""
"Indem Du auf \"Bestellung aufgeben\" klickst, erklärst Du dich mit unseren" "Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(vm_price)s CHF "
" <a href=\"https://" "pro Monat belastet"
"datacenterlight.ch/de/cms/terms-of-service/\">Nutzungsbedingungen</a> einverstanden und Dein Kreditkartenkonto wird mit %(vm_price)s CHF/Monat belastet."
msgid "Place order" msgid "Place order"
msgstr "Bestellen" msgstr "Bestellen"
@ -521,12 +504,6 @@ msgstr "Schliessen"
msgid "Order Nr." msgid "Order Nr."
msgstr "Bestellung Nr." msgstr "Bestellung Nr."
msgid "See Invoice"
msgstr "Siehe Rechnung"
msgid "Page"
msgstr "Seite"
msgid "Your Order" msgid "Your Order"
msgstr "Deine Bestellung" msgstr "Deine Bestellung"
@ -595,19 +572,6 @@ msgstr "Absenden"
msgid "Password reset" msgid "Password reset"
msgstr "Passwort zurücksetzen" 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" msgid "UPDATE"
msgstr "AKTUALISIEREN" msgstr "AKTUALISIEREN"
@ -809,15 +773,21 @@ msgstr "Dein Passwort konnte nicht zurückgesetzt werden."
msgid "The reset password link is no longer valid." msgid "The reset password link is no longer valid."
msgstr "Der Link zum Zurücksetzen Deines Passwortes ist nicht mehr gültig." 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" msgid "Card deassociation successful"
msgstr "Die Verbindung mit der Karte wurde erfolgreich aufgehoben" 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" msgid "Billing address updated successfully"
msgstr "Die Rechnungsadresse wurde erfolgreich aktualisiert" 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 #, python-brace-format
msgid "An error occurred while associating the card. Details: {details}" msgid "An error occurred while associating the card. Details: {details}"
msgstr "" msgstr ""
@ -882,8 +852,7 @@ msgstr "Ungültige Speicher-Grösse"
#, python-brace-format #, python-brace-format
msgid "Incorrect pricing name. Please contact support{support_email}" msgid "Incorrect pricing name. Please contact support{support_email}"
msgstr "" msgstr "Ungültige Preisbezeichnung. Bitte kontaktiere den Support{support_email}"
"Ungültige Preisbezeichnung. Bitte kontaktiere den Support{support_email}"
msgid "" msgid ""
"We could not find the requested VM. Please " "We could not find the requested VM. Please "
@ -902,9 +871,7 @@ msgstr "Fehler beenden VM"
msgid "" msgid ""
"VM terminate action timed out. Please contact support@datacenterlight.ch for " "VM terminate action timed out. Please contact support@datacenterlight.ch for "
"further information." "further information."
msgstr "" msgstr "VM beendet wegen Zeitüberschreitung. Bitte kontaktiere support@datacenterlight.ch für weitere Informationen."
"VM beendet wegen Zeitüberschreitung. Bitte kontaktiere "
"support@datacenterlight.ch für weitere Informationen."
#, python-format #, python-format
msgid "Virtual Machine %(vm_name)s Cancelled" msgid "Virtual Machine %(vm_name)s Cancelled"
@ -915,15 +882,6 @@ msgstr ""
"Es gab einen Fehler bei der Bearbeitung Deine Anfrage. Bitte versuche es " "Es gab einen Fehler bei der Bearbeitung Deine Anfrage. Bitte versuche es "
"noch einmal." "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 #, python-format
#~ msgid "This key exists already with the name \"%(name)s\"" #~ msgid "This key exists already with the name \"%(name)s\""
#~ msgstr "Der SSH-Key mit dem Name \"%(name)s\" existiert bereits" #~ msgstr "Der SSH-Key mit dem Name \"%(name)s\" existiert bereits"

View file

@ -1,144 +0,0 @@
from django.core.management.base import BaseCommand
import datetime
import csv
import logging
import stripe
from hosting.models import VATRates
from utils.hosting_utils import get_vat_rate_for_country
from django.conf import settings
from membership.models import CustomUser, StripeCustomer
stripe.api_key = settings.STRIPE_API_PRIVATE_KEY
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = '''CH vat rate changes on 2024-01-01 from 7.7% to 8.1%. This commands makes the necessary changes'''
def handle(self, *args, **options):
MAKE_MODIFS=False
try:
country_to_change = 'CH'
currency_to_change = 'CHF'
new_rate = 0.081
user_country_vat_rate = get_vat_rate_for_country(country_to_change)
logger.debug("Existing VATRate for %s %s " % (country_to_change, user_country_vat_rate))
vat_rate = VATRates.objects.get(
territory_codes=country_to_change, start_date__isnull=False, stop_date=None
)
logger.debug("VAT rate for %s is %s" % (country_to_change, vat_rate.rate))
logger.debug("vat_rate object = %s" % vat_rate)
logger.debug("Create end date for the VATRate %s" % vat_rate.id)
# if MAKE_MODIFS:
# vat_rate.stop_date = datetime.date(2023, 12, 31)
# vat_rate.save()
# print("Creating a new VATRate for CH")
# obj, created = VATRates.objects.get_or_create(
# start_date=datetime.date(2024, 1, 1),
# stop_date=None,
# territory_codes=country_to_change,
# currency_code=currency_to_change,
# rate=new_rate,
# rate_type="standard",
# description="Switzerland standard VAT (added manually on %s)" % datetime.datetime.now()
# )
# if created:
# logger.debug("Created new VAT Rate for %s with the new rate %s" % (country_to_change, new_rate))
# logger.debug(obj)
# else:
# logger.debug("VAT Rate for %s already exists with the rate %s" % (country_to_change, new_rate))
#
logger.debug("Getting all subscriptions of %s that need a VAT Rate change")
subscriptions = stripe.Subscription.list(limit=100) # Increase the limit to 100 per page (maximum)
ch_subs = []
while subscriptions:
for subscription in subscriptions:
if len(subscription.default_tax_rates) > 0 and subscription.default_tax_rates[0].jurisdiction and subscription.default_tax_rates[0].jurisdiction.lower() == 'ch':
ch_subs.append(subscription)
elif len(subscription.default_tax_rates) > 0:
print("subscription %s belongs to %s" % (subscription.id, subscription.default_tax_rates[0].jurisdiction))
else:
print("subscription %s does not have a tax rate" % subscription.id)
if subscriptions.has_more:
print("FETCHING MORE")
subscriptions = stripe.Subscription.list(limit=100, starting_after=subscriptions.data[-1])
else:
break
logger.debug("There are %s ch subscription that need VAT rate update" % len(ch_subs))
# CSV column headers
csv_headers = [
"customer_name",
"customer_email",
"stripe_customer_id",
"subscription_id",
"subscription_name",
"amount",
"vat_rate"
]
# CSV file name
csv_filename = "ch_subscriptions_change_2024.csv"
# Write subscription data to CSV file
with open(csv_filename, mode='w', newline='') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=csv_headers)
writer.writeheader()
for subscription in ch_subs:
subscription_id = subscription["id"]
stripe_customer_id = subscription.get("customer", "")
vat_rate = subscription.get("tax_percent", "")
c_user = CustomUser.objects.get(
id=StripeCustomer.objects.filter(stripe_id=stripe_customer_id)[0].user.id)
if c_user:
customer_name = c_user.name.encode('utf-8')
customer_email = c_user.email
items = subscription.get("items", {}).get("data", [])
for item in items:
subscription_name = item.get("plan", {}).get("id", "")
amount = item.get("plan", {}).get("amount", "")
# Convert amount to a proper format (e.g., cents to dollars)
amount_in_chf = amount / 100 # Adjust this conversion as needed
# Writing to CSV
writer.writerow({
"customer_name": customer_name,
"customer_email": customer_email,
"stripe_customer_id": stripe_customer_id,
"subscription_id": subscription_id,
"subscription_name": subscription_name,
"amount": amount_in_chf,
"vat_rate": vat_rate # Fill in VAT rate if available
})
else:
print("No customuser for %s %s" % (stripe_customer_id, subscription_id))
if MAKE_MODIFS:
print("Making modifications now")
tax_rate_obj = stripe.TaxRate.create(
display_name="VAT",
description="VAT for %s" % country_to_change,
jurisdiction=country_to_change,
percentage=new_rate,
inclusive=False,
)
stripe_tax_rate = StripeTaxRate.objects.create(
display_name=tax_rate_obj.display_name,
description=tax_rate_obj.description,
jurisdiction=tax_rate_obj.jurisdiction,
percentage=tax_rate_obj.percentage,
inclusive=False,
tax_rate_id=tax_rate_obj.id
)
for ch_sub in ch_subs:
ch_sub.default_tax_rates = [stripe_tax_rate.tax_rate_id]
ch_sub.save()
logger.debug("Default tax rate updated for %s" % ch_sub.id)
else:
print("Not making any modifications because MAKE_MODIFS=False")
except Exception as e:
print(" *** Error occurred. Details {}".format(str(e)))

View file

@ -1,143 +0,0 @@
from django.core.management.base import BaseCommand
import datetime
import csv
import logging
import stripe
from hosting.models import VATRates, StripeTaxRate
from utils.hosting_utils import get_vat_rate_for_country
from django.conf import settings
from membership.models import CustomUser, StripeCustomer
stripe.api_key = settings.STRIPE_API_PRIVATE_KEY
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = '''FI vat rate changes on 2024-09-01 from 24% to 25.5%. This commands makes the necessary changes'''
def handle(self, *args, **options):
MAKE_MODIFS=False
try:
country_to_change = 'FI'
currency_to_change = 'EUR'
new_rate = 25.5
user_country_vat_rate = get_vat_rate_for_country(country_to_change)
logger.debug("Existing VATRate for %s %s " % (country_to_change, user_country_vat_rate))
vat_rate = VATRates.objects.get(
territory_codes=country_to_change, start_date__isnull=False, stop_date=None
)
logger.debug("VAT rate for %s is %s" % (country_to_change, vat_rate.rate))
logger.debug("vat_rate object = %s" % vat_rate)
logger.debug("Create end date for the VATRate %s" % vat_rate.id)
#if MAKE_MODIFS:
# vat_rate.stop_date = datetime.date(2024, 8, 31)
# vat_rate.save()
# print("Creating a new VATRate for FI")
# obj, created = VATRates.objects.get_or_create(
# start_date=datetime.date(2024, 9, 1),
# stop_date=None,
# territory_codes=country_to_change,
# currency_code=currency_to_change,
# rate=new_rate * 0.01,
# rate_type="standard",
# description="FINLAND standard VAT (added manually on %s)" % datetime.datetime.now()
# )
# if created:
# logger.debug("Created new VAT Rate for %s with the new rate %s" % (country_to_change, new_rate))
# logger.debug(obj)
# else:
# logger.debug("VAT Rate for %s already exists with the rate %s" % (country_to_change, new_rate))
logger.debug("Getting all subscriptions of %s that need a VAT Rate change")
subscriptions = stripe.Subscription.list(limit=100) # Increase the limit to 100 per page (maximum)
fi_subs = []
while subscriptions:
for subscription in subscriptions:
if len(subscription.default_tax_rates) > 0 and subscription.default_tax_rates[0].jurisdiction and subscription.default_tax_rates[0].jurisdiction.lower() == 'fi':
fi_subs.append(subscription)
elif len(subscription.default_tax_rates) > 0:
print("subscription %s belongs to %s" % (subscription.id, subscription.default_tax_rates[0].jurisdiction))
else:
print("subscription %s does not have a tax rate" % subscription.id)
if subscriptions.has_more:
print("FETCHING MORE")
subscriptions = stripe.Subscription.list(limit=100, starting_after=subscriptions.data[-1])
else:
break
logger.debug("There are %s FI subscription that need VAT rate update" % len(fi_subs))
# CSV column headers
csv_headers = [
"customer_name",
"customer_email",
"stripe_customer_id",
"subscription_id",
"subscription_name",
"amount",
"vat_rate"
]
# CSV file name
csv_filename = "fi_subscriptions_change_2024.csv"
# Write subscription data to CSV file
with open(csv_filename, mode='w', newline='') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=csv_headers)
writer.writeheader()
for subscription in fi_subs:
subscription_id = subscription["id"]
stripe_customer_id = subscription.get("customer", "")
vat_rate = subscription.get("tax_percent", "")
c_user = CustomUser.objects.get(
id=StripeCustomer.objects.filter(stripe_id=stripe_customer_id)[0].user.id)
if c_user:
customer_name = c_user.name.encode('utf-8')
customer_email = c_user.email
items = subscription.get("items", {}).get("data", [])
for item in items:
subscription_name = item.get("plan", {}).get("id", "")
amount = item.get("plan", {}).get("amount", "")
# Convert amount to a proper format (e.g., cents to dollars)
amount_in_chf = amount / 100 # Adjust this conversion as needed
# Writing to CSV
writer.writerow({
"customer_name": customer_name,
"customer_email": customer_email,
"stripe_customer_id": stripe_customer_id,
"subscription_id": subscription_id,
"subscription_name": subscription_name,
"amount": amount_in_chf,
"vat_rate": vat_rate # Fill in VAT rate if available
})
else:
print("No customuser for %s %s" % (stripe_customer_id, subscription_id))
if MAKE_MODIFS:
print("Making modifications now")
tax_rate_obj = stripe.TaxRate.create(
display_name="VAT",
description="VAT for %s" % country_to_change,
jurisdiction=country_to_change,
percentage=new_rate,
inclusive=False,
)
stripe_tax_rate = StripeTaxRate.objects.create(
display_name=tax_rate_obj.display_name,
description=tax_rate_obj.description,
jurisdiction=tax_rate_obj.jurisdiction,
percentage=tax_rate_obj.percentage,
inclusive=False,
tax_rate_id=tax_rate_obj.id
)
for fi_sub in fi_subs:
fi_sub.default_tax_rates = [stripe_tax_rate.tax_rate_id]
fi_sub.save()
logger.debug("Default tax rate updated for %s" % fi_sub.id)
else:
print("Not making any modifications because MAKE_MODIFS=False")
except Exception as e:
print(" *** Error occurred. Details {}".format(str(e)))

View file

@ -1,33 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2020-06-30 19:12
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('hosting', '0059_stripetaxrate'),
]
operations = [
migrations.RunSQL(
sql=["update hosting_vatrates set stop_date = '2020-06-30' where territory_codes = 'DE' and rate = '0.19'"],
reverse_sql=[
"update hosting_vatrates set stop_date = null where stop_date = '2020-06-30' and territory_codes = 'DE' and rate = '0.19'"],
),
migrations.RunSQL(
sql=[
"insert into hosting_vatrates (start_date, stop_date, territory_codes, currency_code, rate, rate_type, description) values ('2020-07-01',null,'DE', 'EUR', '0.16', 'standard', 'Germany (member state) standard VAT rate - COVID 19 reduced rate')"],
reverse_sql=[
"delete from hosting_vatrates where description = 'Germany (member state) standard VAT rate - COVID 19 reduced rate' and start_date = '2020-07-01' and territory_codes = 'DE'" ],
),
migrations.RunSQL(
sql=[
"update hosting_stripetaxrate set description = 'VAT for DE pre-COVID-19' where description = 'VAT for DE'"],
reverse_sql=[
"update hosting_stripetaxrate set description = 'VAT for DE' where description = 'VAT for DE pre-COVID-19'"],
),
]

View file

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2020-07-21 16:32
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hosting', '0060_update_DE_vat_covid-19'),
]
operations = [
migrations.AddField(
model_name='genericproduct',
name='exclude_vat_calculations',
field=models.BooleanField(default=False, help_text='When checked VAT calculations are excluded for this product'),
),
]

View file

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

View file

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

View file

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

View file

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

View file

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2023-07-27 08:12
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hosting', '0065_auto_20201231_1041'),
]
operations = [
migrations.AlterField(
model_name='genericproduct',
name='product_price',
field=models.DecimalField(decimal_places=2, max_digits=10),
),
migrations.AlterField(
model_name='genericproduct',
name='product_vat',
field=models.DecimalField(decimal_places=4, default=0, max_digits=10),
),
]

View file

@ -1,3 +1,4 @@
import decimal
import json import json
import logging import logging
import os import os
@ -75,28 +76,21 @@ class GenericProduct(AssignPermissionsMixin, models.Model):
) )
product_description = models.CharField(max_length=500, default="") product_description = models.CharField(max_length=500, default="")
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
product_price = models.DecimalField(max_digits=10, decimal_places=2) product_price = models.DecimalField(max_digits=6, decimal_places=2)
product_vat = models.DecimalField(max_digits=10, decimal_places=4, default=0) product_vat = models.DecimalField(max_digits=6, decimal_places=4, default=0)
product_is_subscription = models.BooleanField(default=True) product_is_subscription = models.BooleanField(default=True)
product_subscription_interval = models.CharField( product_subscription_interval = models.CharField(
max_length=10, default="month", max_length=10, default="month",
help_text="Choose between `year` and `month`") help_text="Choose between `year` and `month`")
exclude_vat_calculations = models.BooleanField(
default=False,
help_text="When checked VAT calculations are excluded for this product"
)
def __str__(self): def __str__(self):
return self.product_name return self.product_name
def get_actual_price(self, vat_rate=None): def get_actual_price(self, vat_rate=None):
if self.exclude_vat_calculations: VAT = vat_rate if vat_rate is not None else self.product_vat
return round(float(self.product_price), 2) return round(
else: float(self.product_price) + float(self.product_price) * float(VAT), 2
VAT = vat_rate if vat_rate is not None else self.product_vat )
return round(
float(self.product_price) + float(self.product_price) * float(VAT), 2
)
class HostingOrder(AssignPermissionsMixin, models.Model): class HostingOrder(AssignPermissionsMixin, models.Model):
@ -169,12 +163,8 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
def set_stripe_charge(self, stripe_charge): def set_stripe_charge(self, stripe_charge):
self.stripe_charge_id = stripe_charge.id self.stripe_charge_id = stripe_charge.id
if stripe_charge.source is None: self.last4 = stripe_charge.source.last4
self.last4 = stripe_charge.payment_method_details.card.last4 self.cc_brand = stripe_charge.source.brand
self.cc_brand = stripe_charge.payment_method_details.card.brand
else:
self.last4 = stripe_charge.source.last4
self.cc_brand = stripe_charge.source.brand
self.save() self.save()
def set_subscription_id(self, subscription_id, cc_details): def set_subscription_id(self, subscription_id, cc_details):
@ -676,11 +666,7 @@ class UserCardDetail(AssignPermissionsMixin, models.Model):
stripe_utils = StripeUtils() stripe_utils = StripeUtils()
cus_response = stripe_utils.get_customer(stripe_api_cus_id) cus_response = stripe_utils.get_customer(stripe_api_cus_id)
cu = cus_response['response_object'] cu = cus_response['response_object']
if stripe_source_id.startswith("pm"): cu.default_source = stripe_source_id
# card is a payment method
cu.invoice_settings.default_payment_method = stripe_source_id
else:
cu.default_source = stripe_source_id
cu.save() cu.save()
UserCardDetail.save_default_card_local( UserCardDetail.save_default_card_local(
stripe_api_cus_id, stripe_source_id stripe_api_cus_id, stripe_source_id
@ -748,35 +734,3 @@ class StripeTaxRate(AssignPermissionsMixin, models.Model):
display_name = models.CharField(max_length=100) display_name = models.CharField(max_length=100)
percentage = models.FloatField(default=0) percentage = models.FloatField(default=0)
description = models.CharField(max_length=100) description = models.CharField(max_length=100)
class 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()

View file

@ -84,72 +84,68 @@ $(document).ready(function () {
var hasCreditcard = window.hasCreditcard || false; var hasCreditcard = window.hasCreditcard || false;
if (!hasCreditcard && window.stripeKey) { if (!hasCreditcard && window.stripeKey) {
var stripe = Stripe(window.stripeKey); var stripe = Stripe(window.stripeKey);
if (window.pm_id) { var element_style = {
fonts: [{
} else { family: 'lato-light',
var element_style = { src: 'url(https://cdn.jsdelivr.net/font-lato/2.0/Lato/Lato-Light.woff) format("woff2")'
fonts: [{ }, {
family: 'lato-light', family: 'lato-regular',
src: 'url(https://cdn.jsdelivr.net/font-lato/2.0/Lato/Lato-Light.woff) format("woff2")' src: 'url(https://cdn.jsdelivr.net/font-lato/2.0/Lato/Lato-Regular.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 cardNumberElement = elements.create('cardNumber', { ],
style: credit_card_text_style, locale: window.current_lan
placeholder: enter_ccard_text };
}); var elements = stripe.elements(element_style);
cardNumberElement.mount('#card-number-element'); var credit_card_text_style = {
base: {
var cardExpiryElement = elements.create('cardExpiry', { iconColor: '#666EE8',
style: credit_card_text_style color: '#31325F',
}); lineHeight: '25px',
cardExpiryElement.mount('#card-expiry-element'); fontWeight: 300,
fontFamily: "'lato-light', sans-serif",
var cardCvcElement = elements.create('cardCvc', { fontSize: '14px',
style: credit_card_text_style '::placeholder': {
}); color: '#777'
cardCvcElement.mount('#card-cvc-element');
cardNumberElement.on('change', function (event) {
if (event.brand) {
setBrandIcon(event.brand);
} }
}); },
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'); var submit_form_btn = $('#payment_button_with_creditcard');
@ -167,7 +163,7 @@ $(document).ready(function () {
if (parts.length === 2) return parts.pop().split(";").shift(); if (parts.length === 2) return parts.pop().split(";").shift();
} }
function submitBillingForm(pmId) { function submitBillingForm() {
var billing_form = $('#billing-form'); var billing_form = $('#billing-form');
var recurring_input = $('#id_generic_payment_form-recurring'); var recurring_input = $('#id_generic_payment_form-recurring');
billing_form.append('<input type="hidden" name="generic_payment_form-product_name" value="' + $('#id_generic_payment_form-product_name').val() + '" />'); billing_form.append('<input type="hidden" name="generic_payment_form-product_name" value="' + $('#id_generic_payment_form-product_name').val() + '" />');
@ -178,40 +174,11 @@ $(document).ready(function () {
billing_form.append('<input type="hidden" name="generic_payment_form-recurring" value="' + (recurring_input.prop('checked') ? 'on' : '') + '" />'); billing_form.append('<input type="hidden" name="generic_payment_form-recurring" value="' + (recurring_input.prop('checked') ? 'on' : '') + '" />');
} }
billing_form.append('<input type="hidden" name="generic_payment_form-description" value="' + $('#id_generic_payment_form-description').val() + '" />'); billing_form.append('<input type="hidden" name="generic_payment_form-description" value="' + $('#id_generic_payment_form-description').val() + '" />');
billing_form.append('<input type="hidden" name="id_payment_method" value="' + pmId + '" />');
billing_form.submit(); billing_form.submit();
} }
var $form_new = $('#payment-form-new'); var $form_new = $('#payment-form-new');
$form_new.submit(payWithPaymentIntent); $form_new.submit(payWithStripe_new);
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) { function payWithStripe_new(e) {
e.preventDefault(); e.preventDefault();
@ -230,7 +197,7 @@ $(document).ready(function () {
} else { } else {
var process_text = "Processing"; var process_text = "Processing";
if (typeof window.processing_text !== 'undefined') { if (typeof window.processing_text !== 'undefined') {
process_text = window.processing_text; process_text = window.processing_text
} }
$form_new.find('[type=submit]').html(process_text + ' <i class="fa fa-spinner fa-pulse"></i>'); $form_new.find('[type=submit]').html(process_text + ' <i class="fa fa-spinner fa-pulse"></i>');

View file

@ -92,117 +92,50 @@ $(document).ready(function() {
}); });
var create_vm_form = $('#virtual_machine_create_form'); var create_vm_form = $('#virtual_machine_create_form');
if (window.isSubscription) { create_vm_form.submit(function () {
create_vm_form.submit(function () { $('#btn-create-vm').prop('disabled', true);
$('#btn-create-vm').prop('disabled', true); $.ajax({
$.ajax({ url: create_vm_form.attr('action'),
url: create_vm_form.attr('action'), type: 'POST',
type: 'POST', data: create_vm_form.serialize(),
data: create_vm_form.serialize(), init: function(){
init: function () { ok_btn = $('#createvm-modal-done-btn');
ok_btn = $('#createvm-modal-done-btn'); close_btn = $('#createvm-modal-close-btn');
close_btn = $('#createvm-modal-close-btn'); ok_btn.addClass('btn btn-success btn-ok btn-wide hide');
ok_btn.addClass('btn btn-success btn-ok btn-wide hide'); close_btn.addClass('btn btn-danger btn-ok btn-wide hide');
close_btn.addClass('btn btn-danger btn-ok btn-wide hide'); },
}, success: function (data) {
success: function (data) { fa_icon = $('.modal-icon > .fa');
fa_icon = $('.modal-icon > .fa'); modal_btn = $('#createvm-modal-done-btn');
modal_btn = $('#createvm-modal-done-btn'); $('#createvm-modal-title').text(data.msg_title);
if (data.showSCA) { $('#createvm-modal-body').html(data.msg_body);
console.log("Show SCA"); if (data.redirect) {
var stripe = Stripe(data.STRIPE_PUBLISHABLE_KEY); modal_btn.attr('href', data.redirect).removeClass('hide');
stripe.confirmCardPayment(data.payment_intent_secret).then(function (result) { } else {
if (result.error) { modal_btn.attr('href', "");
// Display error.message in your UI. }
modal_btn.attr('href', data.error.redirect).removeClass('hide'); if (data.status === true) {
fa_icon.attr('class', 'fa fa-close'); fa_icon.attr('class', 'checkmark');
modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide'); } else {
$('#createvm-modal-title').text(data.error.msg_title); fa_icon.attr('class', 'fa fa-close');
$('#createvm-modal-body').html(data.error.msg_body); modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide');
} else { }
// The payment has succeeded. Display a success message. },
modal_btn.attr('href', data.success.redirect).removeClass('hide'); error: function (xmlhttprequest, textstatus, message) {
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) {
fa_icon = $('.modal-icon > .fa'); fa_icon = $('.modal-icon > .fa');
fa_icon.attr('class', 'fa fa-close'); 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); $('#createvm-modal-body').text(create_vm_error_message);
} }
$('#btn-create-vm').prop('disabled', false); $('#btn-create-vm').prop('disabled', false);
$('#createvm-modal-close-btn').removeClass('hide'); $('#createvm-modal-close-btn').removeClass('hide');
} }
});
return false;
}); });
} else { return false;
create_vm_form.submit(placeOrderPaymentIntent); });
function placeOrderPaymentIntent(e) {
e.preventDefault();
var stripe = Stripe(window.stripeKey);
stripe.confirmCardPayment(
window.paymentIntentSecret,
{
payment_method: window.pm_id
}
).then(function(result) {
window.result = result;
fa_icon = $('.modal-icon > .fa');
modal_btn = $('#createvm-modal-done-btn');
if (result.error) {
// Display error.message in your UI.
modal_btn.attr('href', error_url).removeClass('hide');
fa_icon.attr('class', 'fa fa-close');
modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide');
$('#createvm-modal-title').text(error_title);
$('#createvm-modal-body').html(result.error.message + " " + error_msg);
} else {
// The payment has succeeded
// Display a success message
modal_btn.attr('href', success_url).removeClass('hide');
fa_icon.attr('class', 'checkmark');
$('#createvm-modal-title').text(success_title);
$('#createvm-modal-body').html(success_msg);
}
});
}
}
$('#createvm-modal').on('hidden.bs.modal', function () { $('#createvm-modal').on('hidden.bs.modal', function () {
$(this).find('.modal-footer .btn').addClass('hide'); $(this).find('.modal-footer .btn').addClass('hide');
}); })
// Toggle subscription and one-time payments div
$('#li-one-time-charges').click(function() {
console.log("li-one-time-charges clicked");
$('#subscriptions').hide();
$('#one-time-charges').show();
});
$('#li-subscriptions').click(function() {
console.log("li-subscriptions clicked");
$('#one-time-charges').hide();
$('#subscriptions').show();
});
}); });
window.onload = function () { window.onload = function () {

View file

@ -15,11 +15,6 @@
<div class="dashboard-subtitle"></div> <div class="dashboard-subtitle"></div>
</div> </div>
<ul class="nav nav-tabs">
<li class="active" id="li-subscriptions"><a href="#">{% trans "Subscriptions" %}</a></li>
<li id="li-one-time-charges"><a href="#">{% trans "One-time payments" %}</a></li>
</ul>
<div class="subscriptions" id="subscriptions">
<table class="table table-switch"> <table class="table table-switch">
<thead> <thead>
<tr> <tr>
@ -71,60 +66,6 @@
{% endif %} {% endif %}
</ul> </ul>
{% endif %} {% endif %}
</div>
<div id="one-time-charges" class="one-time-charges" style="display: none;">
<table class="table table-switch">
<thead>
<tr>
<th>{% trans "Product" %}</th>
<th>{% trans "Date" %}</th>
<th>{% trans "Amount" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for ho, stripe_charge_data in invs_charge %}
<tr>
{{ ho.id | get_line_item_from_hosting_order_charge }}
</tr>
{% endfor %}
</tbody>
</table>
{% if invs_charge.has_other_pages %}
<ul class="pagination">
{% if invs_charge.has_previous %}
{% if user_email %}
<li><a href="?page={{ invs_charge.previous_page_number }}&user_email={{user_email}}">&laquo;</a></li>
{% else %}
<li><a href="?page={{ invs_charge.previous_page_number }}">&laquo;</a></li>
{% endif %}
{% else %}
<li class="disabled"><span>&laquo;</span></li>
{% endif %}
{% for i in invs_charge.paginator.page_range %}
{% if invs_charge.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% else %}
{% if user_email %}
<li><a href="?page={{ i }}&user_email={{user_email}}">{{ i }}</a></li>
{% else %}
<li><a href="?page={{ i }}">{{ i }}</a></li>
{% endif %}
{% endif %}
{% endfor %}
{% if invs_charge.has_next %}
{% if user_email %}
<li><a href="?page={{ invs_charge.next_page_number }}&user_email={{user_email}}">&raquo;</a></li>
{% else %}
<li><a href="?page={{ invs_charge.next_page_number }}">&raquo;</a></li>
{% endif %}
{% else %}
<li class="disabled"><span>&raquo;</span></li>
{% endif %}
</ul>
{% endif %}
</div>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -218,7 +218,7 @@
{% csrf_token %} {% csrf_token %}
<div class="row"> <div class="row">
<div class="col-sm-8"> <div class="col-sm-8">
<div class="dcl-place-order-text">{% blocktrans with vm_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" you agree to our <a href="https://datacenterlight.ch/en-us/cms/terms-of-service/">Terms of Service</a> and this plan will charge your credit card account with {{ vm_price }} CHF/month.{% endblocktrans %}.</div> <div class="dcl-place-order-text">{% 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 %}.</div>
</div> </div>
<div class="col-sm-4 order-confirm-btn text-right"> <div class="col-sm-4 order-confirm-btn text-right">
<button class="btn choice-btn" id="btn-create-vm" data-href="{% url 'hosting:order-confirmation' %}" data-toggle="modal" data-target="#createvm-modal"> <button class="btn choice-btn" id="btn-create-vm" data-href="{% url 'hosting:order-confirmation' %}" data-toggle="modal" data-target="#createvm-modal">

View file

@ -46,7 +46,7 @@
<div class="vm-vmid"> <div class="vm-vmid">
<div class="vm-item-subtitle">{% trans "Current Pricing" %}</div> <div class="vm-item-subtitle">{% trans "Current Pricing" %}</div>
<div class="vm-item-lg">{{order.price|floatformat:2|intcomma}} CHF/{% if order.generic_product %}{% trans order.generic_product.product_subscription_interval %}{% else %}{% trans "Month" %}{% endif %}</div> <div class="vm-item-lg">{{order.price|floatformat:2|intcomma}} CHF/{% if order.generic_product %}{% trans order.generic_product.product_subscription_interval %}{% else %}{% trans "Month" %}{% endif %}</div>
{% if inv_url %}<a class="btn btn-vm-invoice" href="{{inv_url}}" target="_blank">{% trans "See Invoice" %}</a>{%else%}{% trans "No invoice as of now" %}{% endif %} <a class="btn btn-vm-invoice" href="{{inv_url}}" target="_blank">{% trans "See Invoice" %}</a>
</div> </div>
</div> </div>
<div class="vm-detail-item"> <div class="vm-detail-item">

View file

@ -51,7 +51,7 @@ urlpatterns = [
name='choice_ssh_keys'), name='choice_ssh_keys'),
url(r'delete_ssh_key/(?P<pk>\d+)/?$', SSHKeyDeleteView.as_view(), url(r'delete_ssh_key/(?P<pk>\d+)/?$', SSHKeyDeleteView.as_view(),
name='delete_ssh_key'), name='delete_ssh_key'),
url(r'delete_card/(?P<pk>[\w\-]+)/$', SettingsView.as_view(), url(r'delete_card/(?P<pk>\d+)/?$', SettingsView.as_view(),
name='delete_card'), name='delete_card'),
url(r'create_ssh_key/?$', SSHKeyCreateView.as_view(), url(r'create_ssh_key/?$', SSHKeyCreateView.as_view(),
name='create_ssh_key'), name='create_ssh_key'),

View file

@ -1,7 +1,6 @@
import logging import logging
import uuid import uuid
from datetime import datetime from datetime import datetime
from urllib.parse import quote
from time import sleep from time import sleep
import stripe import stripe
@ -14,7 +13,6 @@ from django.core.exceptions import ValidationError
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.core.urlresolvers import reverse_lazy, reverse from django.core.urlresolvers import reverse_lazy, reverse
from django.db.models import Q
from django.http import ( from django.http import (
Http404, HttpResponseRedirect, HttpResponse, JsonResponse Http404, HttpResponseRedirect, HttpResponse, JsonResponse
) )
@ -554,31 +552,20 @@ class SettingsView(LoginRequiredMixin, FormView):
Check if the user already saved contact details. If so, then show Check if the user already saved contact details. If so, then show
the form populated with those details, to let user change them. the form populated with those details, to let user change them.
""" """
username = self.request.GET.get('username')
if self.request.user.is_admin and username:
user = CustomUser.objects.get(username=username)
else:
user = self.request.user
return form_class( return form_class(
instance=user.billing_addresses.first(), instance=self.request.user.billing_addresses.first(),
**self.get_form_kwargs()) **self.get_form_kwargs())
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(SettingsView, self).get_context_data(**kwargs) context = super(SettingsView, self).get_context_data(**kwargs)
# Get user # Get user
username = self.request.GET.get('username') user = self.request.user
if self.request.user.is_admin and username:
user = CustomUser.objects.get(username=username)
else:
user = self.request.user
stripe_customer = None stripe_customer = None
if hasattr(user, 'stripecustomer'): if hasattr(user, 'stripecustomer'):
stripe_customer = user.stripecustomer stripe_customer = user.stripecustomer
stripe_utils = StripeUtils() cards_list = UserCardDetail.get_all_cards_list(
cards_list_request = stripe_utils.get_available_payment_methods( stripe_customer=stripe_customer
stripe_customer
) )
cards_list = cards_list_request.get('response_object')
context.update({ context.update({
'cards_list': cards_list, 'cards_list': cards_list,
'stripe_key': settings.STRIPE_API_PUBLIC_KEY 'stripe_key': settings.STRIPE_API_PUBLIC_KEY
@ -589,38 +576,48 @@ class SettingsView(LoginRequiredMixin, FormView):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
if 'card' in request.POST and request.POST['card'] is not '': if 'card' in request.POST and request.POST['card'] is not '':
card_id = escape(request.POST['card']) card_id = escape(request.POST['card'])
user_card_detail = UserCardDetail.objects.get(id=card_id)
UserCardDetail.set_default_card( UserCardDetail.set_default_card(
stripe_api_cus_id=request.user.stripecustomer.stripe_id, stripe_api_cus_id=request.user.stripecustomer.stripe_id,
stripe_source_id=card_id stripe_source_id=user_card_detail.card_id
) )
stripe_utils = StripeUtils()
card_details = stripe_utils.get_cards_details_from_payment_method(
card_id
)
if not card_details.get('response_object'):
logger.debug("Could not find card %s in stripe" % card_id)
messages.add_message(request, messages.ERROR,
_("Could not set a default card."))
return HttpResponseRedirect(reverse_lazy('hosting:settings'))
card_details_response = card_details['response_object']
msg = _( msg = _(
("Your {brand} card ending in {last4} set as " ("Your {brand} card ending in {last4} set as "
"default card").format( "default card").format(
brand=card_details_response['brand'], brand=user_card_detail.brand,
last4=card_details_response['last4'] last4=user_card_detail.last4
) )
) )
messages.add_message(request, messages.SUCCESS, msg) messages.add_message(request, messages.SUCCESS, msg)
return HttpResponseRedirect(reverse_lazy('hosting:settings')) return HttpResponseRedirect(reverse_lazy('hosting:settings'))
if 'delete_card' in request.POST: if 'delete_card' in request.POST:
card = self.kwargs.get('pk') try:
stripe_utils = StripeUtils() card = UserCardDetail.objects.get(pk=self.kwargs.get('pk'))
stripe_utils.dissociate_customer_card( if (request.user.has_perm(self.permission_required[0], card)
request.user.stripecustomer.stripe_id, and
card request.user
) .stripecustomer
msg = _("Card deassociation successful") .usercarddetail_set
messages.add_message(request, messages.SUCCESS, msg) .count() > 1):
if card.card_id is not None:
stripe_utils = StripeUtils()
stripe_utils.dissociate_customer_card(
request.user.stripecustomer.stripe_id,
card.card_id
)
if card.preferred:
UserCardDetail.set_default_card_from_stripe(
request.user.stripecustomer.stripe_id
)
card.delete()
msg = _("Card deassociation successful")
messages.add_message(request, messages.SUCCESS, msg)
else:
msg = _("You are not permitted to do this operation")
messages.add_message(request, messages.ERROR, msg)
except UserCardDetail.DoesNotExist:
msg = _("The selected card does not exist")
messages.add_message(request, messages.ERROR, msg)
return HttpResponseRedirect(reverse_lazy('hosting:settings')) return HttpResponseRedirect(reverse_lazy('hosting:settings'))
form = self.get_form() form = self.get_form()
if form.is_valid(): if form.is_valid():
@ -695,49 +692,51 @@ class SettingsView(LoginRequiredMixin, FormView):
msg = _("Billing address updated successfully") msg = _("Billing address updated successfully")
messages.add_message(request, messages.SUCCESS, msg) messages.add_message(request, messages.SUCCESS, msg)
else: else:
id_payment_method = request.POST.get('id_payment_method', None) token = form.cleaned_data.get('token')
stripe_utils = StripeUtils() stripe_utils = StripeUtils()
card_details = stripe_utils.get_cards_details_from_payment_method( card_details = stripe_utils.get_cards_details_from_token(
id_payment_method token
) )
if not card_details.get('response_object'): if not card_details.get('response_object'):
form.add_error("__all__", card_details.get('error')) form.add_error("__all__", card_details.get('error'))
return self.render_to_response(self.get_context_data()) return self.render_to_response(self.get_context_data())
stripe_customer = StripeCustomer.get_or_create( stripe_customer = StripeCustomer.get_or_create(
email=request.user.email, id_payment_method=id_payment_method email=request.user.email, token=token
) )
card = card_details['response_object'] card = card_details['response_object']
acc_result = stripe_utils.associate_customer_card( if UserCardDetail.get_user_card_details(stripe_customer, card):
request.user.stripecustomer.stripe_id, msg = _('You seem to have already added this card')
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']
)
)
messages.add_message(request, messages.ERROR, msg) messages.add_message(request, messages.ERROR, msg)
return self.render_to_response(self.get_context_data()) else:
preferred = False acc_result = stripe_utils.associate_customer_card(
if stripe_customer.usercarddetail_set.count() == 0: request.user.stripecustomer.stripe_id, token
preferred = True )
UserCardDetail.create( if acc_result['response_object'] is None:
stripe_customer=stripe_customer, msg = _(
last4=card['last4'], 'An error occurred while associating the card.'
brand=card['brand'], ' Details: {details}'.format(
fingerprint=card['fingerprint'], details=acc_result['error']
exp_month=card['exp_month'], )
exp_year=card['exp_year'], )
card_id=card['card_id'], messages.add_message(request, messages.ERROR, msg)
preferred=preferred return self.render_to_response(self.get_context_data())
) preferred = False
msg = _( if stripe_customer.usercarddetail_set.count() == 0:
"Successfully associated the card with your account" preferred = True
) UserCardDetail.create(
messages.add_message(request, messages.SUCCESS, msg) stripe_customer=stripe_customer,
last4=card['last4'],
brand=card['brand'],
fingerprint=card['fingerprint'],
exp_month=card['exp_month'],
exp_year=card['exp_year'],
card_id=card['card_id'],
preferred=preferred
)
msg = _(
"Successfully associated the card with your account"
)
messages.add_message(request, messages.SUCCESS, msg)
return self.render_to_response(self.get_context_data()) return self.render_to_response(self.get_context_data())
else: else:
billing_address_data = form.cleaned_data billing_address_data = form.cleaned_data
@ -1078,13 +1077,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView):
billing_address_data = request.session.get('billing_address_data') billing_address_data = request.session.get('billing_address_data')
vm_template_id = template.get('id', 1) vm_template_id = template.get('id', 1)
stripe_api_cus_id = request.user.stripecustomer.stripe_id stripe_api_cus_id = request.user.stripecustomer.stripe_id
logger.debug("template=%s specs=%s stripe_customer_id=%s " if 'token' in self.request.session:
"billing_address_data=%s vm_template_id=%s "
"stripe_api_cus_id=%s" % (
template, specs, stripe_customer_id, billing_address_data,
vm_template_id, stripe_api_cus_id)
)
if 'id_payment_method' in self.request.session:
card_details = stripe_utils.get_cards_details_from_token( card_details = stripe_utils.get_cards_details_from_token(
request.session['token'] request.session['token']
) )
@ -1101,7 +1094,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView):
) )
if not ucd: if not ucd:
acc_result = stripe_utils.associate_customer_card( 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 set_as_default=True
) )
if acc_result['response_object'] is None: if acc_result['response_object'] is None:
@ -1199,24 +1192,8 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView):
discount['stripe_coupon_id'] discount['stripe_coupon_id']
else ""), else ""),
tax_rates=[stripe_tax_rate.tax_rate_id] if stripe_tax_rate 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') stripe_subscription_obj = subscription_result.get('response_object')
latest_invoice = stripe.Invoice.retrieve(stripe_subscription_obj.latest_invoice)
ret = stripe.PaymentIntent.confirm(
latest_invoice.payment_intent
)
if ret.status == 'requires_source_action' or ret.status == 'requires_action':
pi = stripe.PaymentIntent.retrieve(
latest_invoice.payment_intent
)
context = {
'sid': stripe_subscription_obj.id,
'payment_intent_secret': pi.client_secret,
'STRIPE_PUBLISHABLE_KEY': settings.STRIPE_API_PUBLIC_KEY,
'showSCA': True
}
return JsonResponse(context)
# Check if the subscription was approved and is active # Check if the subscription was approved and is active
if (stripe_subscription_obj is None or if (stripe_subscription_obj is None or
stripe_subscription_obj.status != 'active'): stripe_subscription_obj.status != 'active'):
@ -1310,11 +1287,10 @@ class InvoiceListView(LoginRequiredMixin, TemplateView):
page = self.request.GET.get('page', 1) page = self.request.GET.get('page', 1)
context = super(InvoiceListView, self).get_context_data(**kwargs) context = super(InvoiceListView, self).get_context_data(**kwargs)
invs_page = None invs_page = None
invs_page_charges = None
if ('user_email' in self.request.GET if ('user_email' in self.request.GET
and self.request.user.email == settings.ADMIN_EMAIL): and self.request.user.email == settings.ADMIN_EMAIL):
user_email = self.request.GET['user_email'] user_email = self.request.GET['user_email']
context['user_email'] = '%s' % quote(user_email) context['user_email'] = user_email
logger.debug( logger.debug(
"user_email = {}".format(user_email) "user_email = {}".format(user_email)
) )
@ -1324,8 +1300,7 @@ class InvoiceListView(LoginRequiredMixin, TemplateView):
logger.debug("User does not exist") logger.debug("User does not exist")
cu = self.request.user cu = self.request.user
invs = stripe.Invoice.list(customer=cu.stripecustomer.stripe_id, invs = stripe.Invoice.list(customer=cu.stripecustomer.stripe_id,
count=100, count=100)
status='paid')
paginator = Paginator(invs.data, 10) paginator = Paginator(invs.data, 10)
try: try:
invs_page = paginator.page(page) invs_page = paginator.page(page)
@ -1333,21 +1308,6 @@ class InvoiceListView(LoginRequiredMixin, TemplateView):
invs_page = paginator.page(1) invs_page = paginator.page(1)
except EmptyPage: except EmptyPage:
invs_page = paginator.page(paginator.num_pages) invs_page = paginator.page(paginator.num_pages)
hosting_orders = HostingOrder.objects.filter(
customer=cu.stripecustomer).filter(
Q(subscription_id=None) | Q(subscription_id='')
).order_by('-created_at')
stripe_chgs = []
for ho in hosting_orders:
stripe_chgs.append({ho: stripe.Charge.retrieve(ho.stripe_charge_id)})
paginator_charges = Paginator(stripe_chgs, 10)
try:
invs_page_charges = paginator_charges.page(page)
except PageNotAnInteger:
invs_page_charges = paginator_charges.page(1)
except EmptyPage:
invs_page_charges = paginator_charges.page(paginator_charges.num_pages)
else: else:
try: try:
invs = stripe.Invoice.list( invs = stripe.Invoice.list(
@ -1361,27 +1321,10 @@ class InvoiceListView(LoginRequiredMixin, TemplateView):
invs_page = paginator.page(1) invs_page = paginator.page(1)
except EmptyPage: except EmptyPage:
invs_page = paginator.page(paginator.num_pages) invs_page = paginator.page(paginator.num_pages)
hosting_orders = HostingOrder.objects.filter(
customer=self.request.user.stripecustomer).filter(
Q(subscription_id=None) | Q(subscription_id='')
).order_by('-created_at')
stripe_chgs = []
for ho in hosting_orders:
stripe_chgs.append(
{ho: stripe.Charge.retrieve(ho.stripe_charge_id)})
paginator_charges = Paginator(stripe_chgs, 10)
try:
invs_page_charges = paginator_charges.page(page)
except PageNotAnInteger:
invs_page_charges = paginator_charges.page(1)
except EmptyPage:
invs_page_charges = paginator_charges.page(
paginator_charges.num_pages)
except Exception as ex: except Exception as ex:
logger.error(str(ex)) logger.error(str(ex))
invs_page = None invs_page = None
context["invs"] = invs_page context["invs"] = invs_page
context["invs_charge"] = invs_page_charges
return context return context
@method_decorator(decorators) @method_decorator(decorators)
@ -1544,12 +1487,7 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView):
ordering = '-id' ordering = '-id'
def get_queryset(self): def get_queryset(self):
username = self.request.GET.get('username') owner = self.request.user
if self.request.user.is_admin and username:
user = CustomUser.objects.get(username=username)
else:
user = self.request.user
owner = user
manager = OpenNebulaManager(email=owner.username, manager = OpenNebulaManager(email=owner.username,
password=owner.password) password=owner.password)
try: try:
@ -1708,11 +1646,7 @@ class VirtualMachineView(LoginRequiredMixin, View):
login_url = reverse_lazy('hosting:login') login_url = reverse_lazy('hosting:login')
def get_object(self): def get_object(self):
username = self.request.GET.get('username') owner = self.request.user
if self.request.user.is_admin and username:
owner = CustomUser.objects.get(username=username)
else:
owner = self.request.user
vm = None vm = None
manager = OpenNebulaManager( manager = OpenNebulaManager(
email=owner.username, email=owner.username,
@ -1768,10 +1702,7 @@ class VirtualMachineView(LoginRequiredMixin, View):
subscription=hosting_order.subscription_id, subscription=hosting_order.subscription_id,
count=1 count=1
) )
if stripe_obj.data: inv_url = stripe_obj.data[0].hosted_invoice_url
inv_url = stripe_obj.data[0].hosted_invoice_url
else:
inv_url = ''
elif hosting_order.stripe_charge_id: elif hosting_order.stripe_charge_id:
stripe_obj = stripe.Charge.retrieve( stripe_obj = stripe.Charge.retrieve(
hosting_order.stripe_charge_id hosting_order.stripe_charge_id
@ -1932,7 +1863,7 @@ class VirtualMachineView(LoginRequiredMixin, View):
'subject': ("Deleted " if response['status'] 'subject': ("Deleted " if response['status']
else "ERROR deleting ") + admin_msg_sub, else "ERROR deleting ") + admin_msg_sub,
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
'to': ['dcl-orders@ungleich.ch'], 'to': ['info@ungleich.ch'],
'body': "\n".join( 'body': "\n".join(
["%s=%s" % (k, v) for (k, v) in admin_email_body.items()]), ["%s=%s" % (k, v) for (k, v) in admin_email_body.items()]),
} }

View file

@ -277,7 +277,7 @@ class StripeCustomer(models.Model):
return "%s - %s" % (self.stripe_id, self.user.email) return "%s - %s" % (self.stripe_id, self.user.email)
@classmethod @classmethod
def create_stripe_api_customer(cls, email=None, id_payment_method=None, def create_stripe_api_customer(cls, email=None, token=None,
customer_name=None): customer_name=None):
""" """
This method creates a Stripe API customer with the given This method creates a Stripe API customer with the given
@ -288,8 +288,7 @@ class StripeCustomer(models.Model):
stripe user. stripe user.
""" """
stripe_utils = StripeUtils() stripe_utils = StripeUtils()
stripe_data = stripe_utils.create_customer( stripe_data = stripe_utils.create_customer(token, email, customer_name)
id_payment_method, email, customer_name)
if stripe_data.get('response_object'): if stripe_data.get('response_object'):
stripe_cus_id = stripe_data.get('response_object').get('id') stripe_cus_id = stripe_data.get('response_object').get('id')
return stripe_cus_id return stripe_cus_id
@ -297,7 +296,7 @@ class StripeCustomer(models.Model):
return None return None
@classmethod @classmethod
def get_or_create(cls, email=None, token=None, id_payment_method=None): def get_or_create(cls, email=None, token=None):
""" """
Check if there is a registered stripe customer with that email Check if there is a registered stripe customer with that email
or create a new one or create a new one

View file

@ -154,8 +154,6 @@ class OpenNebulaManager():
protocol=settings.OPENNEBULA_PROTOCOL) protocol=settings.OPENNEBULA_PROTOCOL)
) )
raise ConnectionRefusedError raise ConnectionRefusedError
except Exception as ex:
logger.error(str(ex))
def _get_user_pool(self): def _get_user_pool(self):
try: try:

View file

@ -1,21 +0,0 @@
#!/bin/sh
# Nico Schottelius, 2021-12-17
current=$(git describe --dirty)
last_tag=$(git describe --tags --abbrev=0)
registry=harbor.ungleich.svc.p10.k8s.ooo/ungleich-public
image_url=$registry/dynamicweb:${current}
if echo $current | grep -q -e 'dirty$'; then
echo Refusing to release a dirty tree build
exit 1
fi
if [ "$current" != "$last_tag" ]; then
echo "Last tag ($last_tag) is not current version ($current)"
echo "Only release proper versions"
exit 1
fi
docker tag dynamicweb:${current} ${image_url}
docker push ${image_url}

View file

@ -83,7 +83,7 @@ stripe==2.41.0
wheel==0.29.0 wheel==0.29.0
django-admin-honeypot==1.0.0 django-admin-honeypot==1.0.0
coverage==4.3.4 coverage==4.3.4
git+https://github.com/ungleich/python-oca.git#egg=oca git+https://github.com/ungleich/python-oca.git#egg=python-oca
djangorestframework==3.6.3 djangorestframework==3.6.3
flake8==3.3.0 flake8==3.3.0
python-memcached==1.58 python-memcached==1.58

View file

@ -134,6 +134,8 @@
digitalglarus.ch<br/> digitalglarus.ch<br/>
hack4lgarus.ch<br/> hack4lgarus.ch<br/>
ipv6onlyhosting.com<br/> ipv6onlyhosting.com<br/>
ipv6onlyhosting.ch<br/>
ipv6onlyhosting.net<br/>
django-hosting.ch<br/> django-hosting.ch<br/>
rails-hosting.ch<br/> rails-hosting.ch<br/>
node-hosting.ch<br/> node-hosting.ch<br/>

View file

@ -19,15 +19,13 @@
<script> <script>
document.addEventListener("DOMContentLoaded", function() { $( document ).ready(function() {
var equalizer = ".sameheight-{{product_instance.pk}}"; var equalizer = ".sameheight-{{product_instance.pk}}"
var elements = document.querySelectorAll(equalizer); var heights = $(equalizer).map(function() {
var heights = Array.from(elements).map(function(el) { return $(this).height();
return el.offsetHeight; }).get(),
});
var maxHeight = Math.max(...heights); maxHeight = Math.max.apply(null, heights);
Array.from(elements).forEach(function(el) { $(equalizer).height(maxHeight);
el.style.height = maxHeight + "px";
});
}); });
</script> </script>

View file

@ -25,10 +25,9 @@ class ContactView(FormView):
success_message = _('Message Successfully Sent') success_message = _('Message Successfully Sent')
def form_valid(self, form): def form_valid(self, form):
print("ungleich_page contactusform") form.save()
#form.save() form.send_email()
#form.send_email() messages.add_message(self.request, messages.SUCCESS, self.success_message)
#messages.add_message(self.request, messages.SUCCESS, self.success_message)
return super(ContactView, self).form_valid(form) return super(ContactView, self).form_valid(form)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):

View file

@ -1,8 +1,7 @@
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.db import models from django.db import models
# Old: http://xml.coverpages.org/country3166.html # http://xml.coverpages.org/country3166.html
# 2023-12-29: Updated list of countries from https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes
COUNTRIES = ( COUNTRIES = (
('AD', _('Andorra')), ('AD', _('Andorra')),
('AE', _('United Arab Emirates')), ('AE', _('United Arab Emirates')),
@ -11,6 +10,7 @@ COUNTRIES = (
('AI', _('Anguilla')), ('AI', _('Anguilla')),
('AL', _('Albania')), ('AL', _('Albania')),
('AM', _('Armenia')), ('AM', _('Armenia')),
('AN', _('Netherlands Antilles')),
('AO', _('Angola')), ('AO', _('Angola')),
('AQ', _('Antarctica')), ('AQ', _('Antarctica')),
('AR', _('Argentina')), ('AR', _('Argentina')),
@ -18,7 +18,6 @@ COUNTRIES = (
('AT', _('Austria')), ('AT', _('Austria')),
('AU', _('Australia')), ('AU', _('Australia')),
('AW', _('Aruba')), ('AW', _('Aruba')),
('AX', _('Aland Islands')),
('AZ', _('Azerbaijan')), ('AZ', _('Azerbaijan')),
('BA', _('Bosnia and Herzegovina')), ('BA', _('Bosnia and Herzegovina')),
('BB', _('Barbados')), ('BB', _('Barbados')),
@ -29,13 +28,11 @@ COUNTRIES = (
('BH', _('Bahrain')), ('BH', _('Bahrain')),
('BI', _('Burundi')), ('BI', _('Burundi')),
('BJ', _('Benin')), ('BJ', _('Benin')),
('BL', _('St. Barts')),
('BM', _('Bermuda')), ('BM', _('Bermuda')),
('BN', _('Brunei')), ('BN', _('Brunei Darussalam')),
('BO', _('Bolivia')), ('BO', _('Bolivia')),
('BQ', _('Caribbean Netherlands')),
('BR', _('Brazil')), ('BR', _('Brazil')),
('BS', _('Bahamas')), ('BS', _('Bahama')),
('BT', _('Bhutan')), ('BT', _('Bhutan')),
('BV', _('Bouvet Island')), ('BV', _('Bouvet Island')),
('BW', _('Botswana')), ('BW', _('Botswana')),
@ -43,12 +40,11 @@ COUNTRIES = (
('BZ', _('Belize')), ('BZ', _('Belize')),
('CA', _('Canada')), ('CA', _('Canada')),
('CC', _('Cocos (Keeling) Islands')), ('CC', _('Cocos (Keeling) Islands')),
('CD', _('Congo - Kinshasa')),
('CF', _('Central African Republic')), ('CF', _('Central African Republic')),
('CG', _('Congo - Brazzaville')), ('CG', _('Congo')),
('CH', _('Switzerland')), ('CH', _('Switzerland')),
('CI', _('Ivory Coast')), ('CI', _('Ivory Coast')),
('CK', _('Cook Islands')), ('CK', _('Cook Iislands')),
('CL', _('Chile')), ('CL', _('Chile')),
('CM', _('Cameroon')), ('CM', _('Cameroon')),
('CN', _('China')), ('CN', _('China')),
@ -56,10 +52,9 @@ COUNTRIES = (
('CR', _('Costa Rica')), ('CR', _('Costa Rica')),
('CU', _('Cuba')), ('CU', _('Cuba')),
('CV', _('Cape Verde')), ('CV', _('Cape Verde')),
('CW', _('Curacao')),
('CX', _('Christmas Island')), ('CX', _('Christmas Island')),
('CY', _('Cyprus')), ('CY', _('Cyprus')),
('CZ', _('Czechia')), ('CZ', _('Czech Republic')),
('DE', _('Germany')), ('DE', _('Germany')),
('DJ', _('Djibouti')), ('DJ', _('Djibouti')),
('DK', _('Denmark')), ('DK', _('Denmark')),
@ -75,16 +70,16 @@ COUNTRIES = (
('ET', _('Ethiopia')), ('ET', _('Ethiopia')),
('FI', _('Finland')), ('FI', _('Finland')),
('FJ', _('Fiji')), ('FJ', _('Fiji')),
('FK', _('Falkland Islands')), ('FK', _('Falkland Islands (Malvinas)')),
('FM', _('Micronesia')), ('FM', _('Micronesia')),
('FO', _('Faroe Islands')), ('FO', _('Faroe Islands')),
('FR', _('France')), ('FR', _('France')),
('FX', _('France, Metropolitan')),
('GA', _('Gabon')), ('GA', _('Gabon')),
('GB', _('United Kingdom')), ('GB', _('United Kingdom (Great Britain)')),
('GD', _('Grenada')), ('GD', _('Grenada')),
('GE', _('Georgia')), ('GE', _('Georgia')),
('GF', _('French Guiana')), ('GF', _('French Guiana')),
('GG', _('Guernsey')),
('GH', _('Ghana')), ('GH', _('Ghana')),
('GI', _('Gibraltar')), ('GI', _('Gibraltar')),
('GL', _('Greenland')), ('GL', _('Greenland')),
@ -98,7 +93,7 @@ COUNTRIES = (
('GU', _('Guam')), ('GU', _('Guam')),
('GW', _('Guinea-Bissau')), ('GW', _('Guinea-Bissau')),
('GY', _('Guyana')), ('GY', _('Guyana')),
('HK', _('Hong Kong SAR China')), ('HK', _('Hong Kong')),
('HM', _('Heard & McDonald Islands')), ('HM', _('Heard & McDonald Islands')),
('HN', _('Honduras')), ('HN', _('Honduras')),
('HR', _('Croatia')), ('HR', _('Croatia')),
@ -107,14 +102,12 @@ COUNTRIES = (
('ID', _('Indonesia')), ('ID', _('Indonesia')),
('IE', _('Ireland')), ('IE', _('Ireland')),
('IL', _('Israel')), ('IL', _('Israel')),
('IM', _('Isle of Man')),
('IN', _('India')), ('IN', _('India')),
('IO', _('British Indian Ocean Territory')), ('IO', _('British Indian Ocean Territory')),
('IQ', _('Iraq')), ('IQ', _('Iraq')),
('IR', _('Iran')), ('IR', _('Islamic Republic of Iran')),
('IS', _('Iceland')), ('IS', _('Iceland')),
('IT', _('Italy')), ('IT', _('Italy')),
('JE', _('Jersey')),
('JM', _('Jamaica')), ('JM', _('Jamaica')),
('JO', _('Jordan')), ('JO', _('Jordan')),
('JP', _('Japan')), ('JP', _('Japan')),
@ -124,14 +117,14 @@ COUNTRIES = (
('KI', _('Kiribati')), ('KI', _('Kiribati')),
('KM', _('Comoros')), ('KM', _('Comoros')),
('KN', _('St. Kitts and Nevis')), ('KN', _('St. Kitts and Nevis')),
('KP', _('North Korea')), ('KP', _('Korea, Democratic People\'s Republic of')),
('KR', _('South Korea')), ('KR', _('Korea, Republic of')),
('KW', _('Kuwait')), ('KW', _('Kuwait')),
('KY', _('Cayman Islands')), ('KY', _('Cayman Islands')),
('KZ', _('Kazakhstan')), ('KZ', _('Kazakhstan')),
('LA', _('Laos')), ('LA', _('Lao People\'s Democratic Republic')),
('LB', _('Lebanon')), ('LB', _('Lebanon')),
('LC', _('St. Lucia')), ('LC', _('Saint Lucia')),
('LI', _('Liechtenstein')), ('LI', _('Liechtenstein')),
('LK', _('Sri Lanka')), ('LK', _('Sri Lanka')),
('LR', _('Liberia')), ('LR', _('Liberia')),
@ -139,23 +132,20 @@ COUNTRIES = (
('LT', _('Lithuania')), ('LT', _('Lithuania')),
('LU', _('Luxembourg')), ('LU', _('Luxembourg')),
('LV', _('Latvia')), ('LV', _('Latvia')),
('LY', _('Libya')), ('LY', _('Libyan Arab Jamahiriya')),
('MA', _('Morocco')), ('MA', _('Morocco')),
('MC', _('Monaco')), ('MC', _('Monaco')),
('MD', _('Moldova')), ('MD', _('Moldova, Republic of')),
('ME', _('Montenegro')),
('MF', _('St. Martin')),
('MG', _('Madagascar')), ('MG', _('Madagascar')),
('MH', _('Marshall Islands')), ('MH', _('Marshall Islands')),
('MK', _('North Macedonia')),
('ML', _('Mali')), ('ML', _('Mali')),
('MM', _('Myanmar (Burma)')),
('MN', _('Mongolia')), ('MN', _('Mongolia')),
('MO', _('Macao SAR China')), ('MM', _('Myanmar')),
('MO', _('Macau')),
('MP', _('Northern Mariana Islands')), ('MP', _('Northern Mariana Islands')),
('MQ', _('Martinique')), ('MQ', _('Martinique')),
('MR', _('Mauritania')), ('MR', _('Mauritania')),
('MS', _('Montserrat')), ('MS', _('Monserrat')),
('MT', _('Malta')), ('MT', _('Malta')),
('MU', _('Mauritius')), ('MU', _('Mauritius')),
('MV', _('Maldives')), ('MV', _('Maldives')),
@ -184,17 +174,15 @@ COUNTRIES = (
('PK', _('Pakistan')), ('PK', _('Pakistan')),
('PL', _('Poland')), ('PL', _('Poland')),
('PM', _('St. Pierre & Miquelon')), ('PM', _('St. Pierre & Miquelon')),
('PN', _('Pitcairn Islands')), ('PN', _('Pitcairn')),
('PR', _('Puerto Rico')), ('PR', _('Puerto Rico')),
('PS', _('Palestinian Territories')),
('PT', _('Portugal')), ('PT', _('Portugal')),
('PW', _('Palau')), ('PW', _('Palau')),
('PY', _('Paraguay')), ('PY', _('Paraguay')),
('QA', _('Qatar')), ('QA', _('Qatar')),
('RE', _('Reunion')), ('RE', _('Reunion')),
('RO', _('Romania')), ('RO', _('Romania')),
('RS', _('Serbia')), ('RU', _('Russian Federation')),
('RU', _('Russia')),
('RW', _('Rwanda')), ('RW', _('Rwanda')),
('SA', _('Saudi Arabia')), ('SA', _('Saudi Arabia')),
('SB', _('Solomon Islands')), ('SB', _('Solomon Islands')),
@ -204,19 +192,17 @@ COUNTRIES = (
('SG', _('Singapore')), ('SG', _('Singapore')),
('SH', _('St. Helena')), ('SH', _('St. Helena')),
('SI', _('Slovenia')), ('SI', _('Slovenia')),
('SJ', _('Svalbard and Jan Mayen')), ('SJ', _('Svalbard & Jan Mayen Islands')),
('SK', _('Slovakia')), ('SK', _('Slovakia')),
('SL', _('Sierra Leone')), ('SL', _('Sierra Leone')),
('SM', _('San Marino')), ('SM', _('San Marino')),
('SN', _('Senegal')), ('SN', _('Senegal')),
('SO', _('Somalia')), ('SO', _('Somalia')),
('SR', _('Suriname')), ('SR', _('Suriname')),
('SS', _('South Sudan')),
('ST', _('Sao Tome & Principe')), ('ST', _('Sao Tome & Principe')),
('SV', _('El Salvador')), ('SV', _('El Salvador')),
('SX', _('Sint Maarten')), ('SY', _('Syrian Arab Republic')),
('SY', _('Syria')), ('SZ', _('Swaziland')),
('SZ', _('Eswatini')),
('TC', _('Turks & Caicos Islands')), ('TC', _('Turks & Caicos Islands')),
('TD', _('Chad')), ('TD', _('Chad')),
('TF', _('French Southern Territories')), ('TF', _('French Southern Territories')),
@ -224,34 +210,36 @@ COUNTRIES = (
('TH', _('Thailand')), ('TH', _('Thailand')),
('TJ', _('Tajikistan')), ('TJ', _('Tajikistan')),
('TK', _('Tokelau')), ('TK', _('Tokelau')),
('TL', _('Timor-Leste')),
('TM', _('Turkmenistan')), ('TM', _('Turkmenistan')),
('TN', _('Tunisia')), ('TN', _('Tunisia')),
('TO', _('Tonga')), ('TO', _('Tonga')),
('TP', _('East Timor')),
('TR', _('Turkey')), ('TR', _('Turkey')),
('TT', _('Trinidad & Tobago')), ('TT', _('Trinidad & Tobago')),
('TV', _('Tuvalu')), ('TV', _('Tuvalu')),
('TW', _('Taiwan')), ('TW', _('Taiwan, Province of China')),
('TZ', _('Tanzania')), ('TZ', _('Tanzania, United Republic of')),
('UA', _('Ukraine')), ('UA', _('Ukraine')),
('UG', _('Uganda')), ('UG', _('Uganda')),
('UM', _('U.S. Outlying Islands')), ('UM', _('United States Minor Outlying Islands')),
('US', _('United States')), ('US', _('United States of America')),
('UY', _('Uruguay')), ('UY', _('Uruguay')),
('UZ', _('Uzbekistan')), ('UZ', _('Uzbekistan')),
('VA', _('Vatican City')), ('VA', _('Vatican City State (Holy See)')),
('VC', _('St. Vincent & Grenadines')), ('VC', _('St. Vincent & the Grenadines')),
('VE', _('Venezuela')), ('VE', _('Venezuela')),
('VG', _('British Virgin Islands')), ('VG', _('British Virgin Islands')),
('VI', _('U.S. Virgin Islands')), ('VI', _('United States Virgin Islands')),
('VN', _('Vietnam')), ('VN', _('Viet Nam')),
('VU', _('Vanuatu')), ('VU', _('Vanuatu')),
('WF', _('Wallis & Futuna')), ('WF', _('Wallis & Futuna Islands')),
('WS', _('Samoa')), ('WS', _('Samoa')),
('YE', _('Yemen')), ('YE', _('Yemen')),
('YT', _('Mayotte')), ('YT', _('Mayotte')),
('YU', _('Yugoslavia')),
('ZA', _('South Africa')), ('ZA', _('South Africa')),
('ZM', _('Zambia')), ('ZM', _('Zambia')),
('ZR', _('Zaire')),
('ZW', _('Zimbabwe')), ('ZW', _('Zimbabwe')),
) )

View file

@ -4,8 +4,6 @@ from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django_recaptcha.fields import ReCaptchaField
from membership.models import CustomUser from membership.models import CustomUser
from .models import ContactMessage, BillingAddress, UserBillingAddress from .models import ContactMessage, BillingAddress, UserBillingAddress
@ -190,7 +188,6 @@ class UserBillingAddressForm(forms.ModelForm):
class ContactUsForm(forms.ModelForm): class ContactUsForm(forms.ModelForm):
error_css_class = 'autofocus' error_css_class = 'autofocus'
captcha = ReCaptchaField()
class Meta: class Meta:
model = ContactMessage model = ContactMessage
@ -209,12 +206,11 @@ class ContactUsForm(forms.ModelForm):
} }
def send_email(self, email_to='info@digitalglarus.ch'): def send_email(self, email_to='info@digitalglarus.ch'):
pass text_content = render_to_string(
#text_content = render_to_string( 'emails/contact.txt', {'data': self.cleaned_data})
# 'emails/contact.txt', {'data': self.cleaned_data}) html_content = render_to_string(
#html_content = render_to_string( 'emails/contact.html', {'data': self.cleaned_data})
# 'emails/contact.html', {'data': self.cleaned_data}) email = EmailMultiAlternatives('Subject', text_content)
#email = EmailMultiAlternatives('Subject', text_content) email.attach_alternative(html_content, "text/html")
#email.attach_alternative(html_content, "text/html") email.to = [email_to]
#email.to = [email_to] email.send()
#email.send()

View file

@ -3,7 +3,6 @@ import hashlib
import random import random
import ldap3 import ldap3
import logging import logging
import unicodedata
from django.conf import settings from django.conf import settings
@ -102,7 +101,7 @@ class LdapManager:
"uidNumber": [str(uidNumber)], "uidNumber": [str(uidNumber)],
"gidNumber": [str(settings.LDAP_CUSTOMER_GROUP_ID)], "gidNumber": [str(settings.LDAP_CUSTOMER_GROUP_ID)],
"loginShell": ["/bin/bash"], "loginShell": ["/bin/bash"],
"homeDirectory": ["/home/{}".format(unicodedata.normalize('NFKD', user).encode('ascii','ignore'))], "homeDirectory": ["/home/{}".format(user).encode("utf-8")],
"mail": email.encode("utf-8"), "mail": email.encode("utf-8"),
"userPassword": [self._ssha_password( "userPassword": [self._ssha_password(
password.encode("utf-8") password.encode("utf-8")
@ -267,7 +266,7 @@ class LdapManager:
logger.error( logger.error(
"Error reading int value from {}. {}" "Error reading int value from {}. {}"
"Returning default value {} instead".format( "Returning default value {} instead".format(
settings.LDAP_MAX_UID_FILE_PATH, settings.LDAP_MAX_UID_PATH,
str(ve), str(ve),
settings.LDAP_DEFAULT_START_UID settings.LDAP_DEFAULT_START_UID
) )

View file

@ -34,7 +34,6 @@ def handleStripeError(f):
logger.error(str(e)) logger.error(str(e))
return response return response
except stripe.error.RateLimitError as e: except stripe.error.RateLimitError as e:
logger.error(str(e))
response.update( response.update(
{'error': "Too many requests made to the API too quickly"}) {'error': "Too many requests made to the API too quickly"})
return response return response
@ -70,7 +69,7 @@ class StripeUtils(object):
CURRENCY = 'chf' CURRENCY = 'chf'
INTERVAL = 'month' INTERVAL = 'month'
SUCCEEDED_STATUS = 'succeeded' SUCCEEDED_STATUS = 'succeeded'
RESOURCE_ALREADY_EXISTS_ERROR_CODE = 'resource_already_exists' STRIPE_PLAN_ALREADY_EXISTS = 'Plan already exists'
STRIPE_NO_SUCH_PLAN = 'No such plan' STRIPE_NO_SUCH_PLAN = 'No such plan'
PLAN_EXISTS_ERROR_MSG = 'Plan {} exists already.\nCreating a local StripePlan now.' PLAN_EXISTS_ERROR_MSG = 'Plan {} exists already.\nCreating a local StripePlan now.'
PLAN_DOES_NOT_EXIST_ERROR_MSG = 'Plan {} does not exist.' PLAN_DOES_NOT_EXIST_ERROR_MSG = 'Plan {} does not exist.'
@ -83,31 +82,20 @@ class StripeUtils(object):
customer.save() customer.save()
@handleStripeError @handleStripeError
def associate_customer_card(self, stripe_customer_id, id_payment_method, def associate_customer_card(self, stripe_customer_id, token,
set_as_default=False): set_as_default=False):
customer = stripe.Customer.retrieve(stripe_customer_id) customer = stripe.Customer.retrieve(stripe_customer_id)
stripe.PaymentMethod.attach( card = customer.sources.create(source=token)
id_payment_method,
customer=stripe_customer_id,
)
if set_as_default: if set_as_default:
customer.invoice_settings.default_payment_method = id_payment_method customer.default_source = card.id
customer.save() customer.save()
return True return True
@handleStripeError @handleStripeError
def dissociate_customer_card(self, stripe_customer_id, card_id): def dissociate_customer_card(self, stripe_customer_id, card_id):
customer = stripe.Customer.retrieve(stripe_customer_id) customer = stripe.Customer.retrieve(stripe_customer_id)
if card_id.startswith("pm"): card = customer.sources.retrieve(card_id)
logger.debug("PaymentMethod %s detached %s" % (card_id, card.delete()
stripe_customer_id))
pm = stripe.PaymentMethod.retrieve(card_id)
stripe.PaymentMethod.detach(card_id)
pm.delete()
else:
logger.debug("card %s detached %s" % (card_id, stripe_customer_id))
card = customer.sources.retrieve(card_id)
card.delete()
@handleStripeError @handleStripeError
def update_customer_card(self, customer_id, token): def update_customer_card(self, customer_id, token):
@ -199,24 +187,6 @@ class StripeUtils(object):
} }
return card_details return card_details
@handleStripeError
def get_cards_details_from_payment_method(self, payment_method_id):
payment_method = stripe.PaymentMethod.retrieve(payment_method_id)
# payment_method does not always seem to have a card with id
# if that is the case, fallback to payment_method_id for card_id
card_id = payment_method_id
if hasattr(payment_method.card, 'id'):
card_id = payment_method.card.id
card_details = {
'last4': payment_method.card.last4,
'brand': payment_method.card.brand,
'exp_month': payment_method.card.exp_month,
'exp_year': payment_method.card.exp_year,
'fingerprint': payment_method.card.fingerprint,
'card_id': card_id
}
return card_details
def check_customer(self, stripe_cus_api_id, user, token): def check_customer(self, stripe_cus_api_id, user, token):
try: try:
customer = stripe.Customer.retrieve(stripe_cus_api_id) customer = stripe.Customer.retrieve(stripe_cus_api_id)
@ -236,11 +206,11 @@ class StripeUtils(object):
return customer return customer
@handleStripeError @handleStripeError
def create_customer(self, id_payment_method, email, name=None): def create_customer(self, token, email, name=None):
if name is None or name.strip() == "": if name is None or name.strip() == "":
name = email name = email
customer = self.stripe.Customer.create( customer = self.stripe.Customer.create(
payment_method=id_payment_method, source=token,
description=name, description=name,
email=email email=email
) )
@ -297,17 +267,11 @@ class StripeUtils(object):
stripe_plan_db_obj = StripePlan.objects.create( stripe_plan_db_obj = StripePlan.objects.create(
stripe_plan_id=stripe_plan_id) stripe_plan_id=stripe_plan_id)
except stripe.error.InvalidRequestError as e: except stripe.error.InvalidRequestError as e:
logger.error(str(e)) if self.STRIPE_PLAN_ALREADY_EXISTS in str(e):
logger.error("error_code = %s" % str(e.__dict__))
if self.RESOURCE_ALREADY_EXISTS_ERROR_CODE in e.error.code:
logger.debug( logger.debug(
self.PLAN_EXISTS_ERROR_MSG.format(stripe_plan_id)) self.PLAN_EXISTS_ERROR_MSG.format(stripe_plan_id))
stripe_plan_db_obj, c = StripePlan.objects.get_or_create( stripe_plan_db_obj = StripePlan.objects.create(
stripe_plan_id=stripe_plan_id) stripe_plan_id=stripe_plan_id)
if c:
logger.debug("Created stripe plan %s" % stripe_plan_id)
else:
logger.debug("Plan %s exists already" % stripe_plan_id)
return stripe_plan_db_obj return stripe_plan_db_obj
@handleStripeError @handleStripeError
@ -336,14 +300,10 @@ class StripeUtils(object):
@handleStripeError @handleStripeError
def subscribe_customer_to_plan(self, customer, plans, trial_end=None, def subscribe_customer_to_plan(self, customer, plans, trial_end=None,
coupon="", tax_rates=list(), coupon="", tax_rates=list()):
default_payment_method=""):
""" """
Subscribes the given customer to the list of given plans Subscribes the given customer to the list of given plans
:param default_payment_method:
:param tax_rates:
:param coupon:
:param customer: The stripe customer identifier :param customer: The stripe customer identifier
:param plans: A list of stripe plans. :param plans: A list of stripe plans.
:param trial_end: An integer representing when the Stripe subscription :param trial_end: An integer representing when the Stripe subscription
@ -357,17 +317,12 @@ class StripeUtils(object):
] ]
:return: The subscription StripeObject :return: The subscription StripeObject
""" """
logger.debug("Subscribing %s to plan %s : coupon = %s" % (
customer, str(plans), str(coupon)
))
subscription_result = self.stripe.Subscription.create( subscription_result = self.stripe.Subscription.create(
customer=customer, items=plans, trial_end=trial_end, customer=customer, items=plans, trial_end=trial_end,
coupon=coupon, coupon=coupon,
default_tax_rates=tax_rates, default_tax_rates=tax_rates,
payment_behavior='allow_incomplete',
default_payment_method=default_payment_method
) )
logger.debug("Done subscribing")
return subscription_result return subscription_result
@handleStripeError @handleStripeError
@ -525,48 +480,6 @@ class StripeUtils(object):
) )
return tax_id_obj return tax_id_obj
@handleStripeError
def get_payment_intent(self, amount, customer):
""" Create a stripe PaymentIntent of the given amount and return it
:param amount: the amount of payment_intent
:return:
"""
payment_intent_obj = stripe.PaymentIntent.create(
amount=amount,
currency='chf',
customer=customer,
setup_future_usage='off_session'
)
return payment_intent_obj
@handleStripeError
def get_available_payment_methods(self, customer):
""" Retrieves all payment methods of the given customer
:param customer: StripeCustomer object
:return: a list of available payment methods
"""
return_list = []
if customer is None:
return return_list
cu = stripe.Customer.retrieve(customer.stripe_id)
pms = stripe.PaymentMethod.list(
customer=customer.stripe_id,
type="card",
)
default_source = None
if cu.default_source:
default_source = cu.default_source
else:
default_source = cu.invoice_settings.default_payment_method
for pm in pms.data:
return_list.append({
'last4': pm.card.last4, 'brand': pm.card.brand, 'id': pm.id,
'exp_year': pm.card.exp_year,
'exp_month': '{:02d}'.format(pm.card.exp_month),
'preferred': pm.id == default_source
})
return return_list
def compare_vat_numbers(self, vat1, vat2): def compare_vat_numbers(self, vat1, vat2):
_vat1 = vat1.replace(" ", "").replace(".", "").replace("-","") _vat1 = vat1.replace(" ", "").replace(".", "").replace("-","")
_vat2 = vat2.replace(" ", "").replace(".", "").replace("-","") _vat2 = vat2.replace(" ", "").replace(".", "").replace("-","")

View file

@ -1,17 +1,14 @@
import datetime import datetime
import logging import logging
import json
import stripe
import stripe
# Create your views here. # Create your views here.
from django.conf import settings from django.conf import settings
from django.http import HttpResponse from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
from datacenterlight.views import do_provisioning, do_provisioning_generic
from membership.models import StripeCustomer from membership.models import StripeCustomer
from hosting.models import IncompleteSubscriptions, IncompletePaymentIntents
from utils.models import BillingAddress, UserBillingAddress from utils.models import BillingAddress, UserBillingAddress
from utils.tasks import send_plain_email_task from utils.tasks import send_plain_email_task
@ -114,193 +111,8 @@ def handle_webhook(request):
'to': settings.DCL_ERROR_EMAILS_TO_LIST, 'to': settings.DCL_ERROR_EMAILS_TO_LIST,
'body': "Response = %s" % str(tax_id_obj), 'body': "Response = %s" % str(tax_id_obj),
} }
send_plain_email_task.delay(email_data) send_plain_email_task.delay(email_data)
elif event.type == 'invoice.paid':
#More info: https://stripe.com/docs/billing/migration/strong-customer-authentication#scenario-1-handling-fulfillment
invoice_obj = event.data.object
logger.debug("Webhook Event: invoice.paid")
logger.debug("invoice_obj %s " % str(invoice_obj))
logger.debug("invoice_obj.paid = %s %s" % (invoice_obj.paid, type(invoice_obj.paid)))
logger.debug("invoice_obj.billing_reason = %s %s" % (invoice_obj.billing_reason, type(invoice_obj.billing_reason)))
# We should check for billing_reason == "subscription_create" but we
# check for "subscription_update"
# because we are using older api.
# See https://stripe.com/docs/upgrades?since=2015-07-13
# The billing_reason attribute of the invoice object now can take the
# value of subscription_create, indicating that it is the first
# invoice of a subscription. For older API versions,
# billing_reason=subscription_create is represented as
# subscription_update.
if (invoice_obj.paid and
invoice_obj.billing_reason == "subscription_update"):
logger.debug("""invoice_obj.paid and
invoice_obj.billing_reason == subscription_update""")
logger.debug("Start provisioning")
try:
logger.debug("Looking for subscription %s" %
invoice_obj.subscription)
stripe_subscription_obj = stripe.Subscription.retrieve(
invoice_obj.subscription)
try:
incomplete_sub = IncompleteSubscriptions.objects.get(
subscription_id=invoice_obj.subscription)
request = ""
soc = ""
card_details_response = ""
gp_details = ""
template = ""
specs = ""
billing_address_data = ""
if incomplete_sub.request:
request = json.loads(incomplete_sub.request)
if incomplete_sub.specs:
specs = json.loads(incomplete_sub.specs)
if incomplete_sub.stripe_onetime_charge:
soc = json.loads(incomplete_sub.stripe_onetime_charge)
if incomplete_sub.gp_details:
gp_details = json.loads(incomplete_sub.gp_details)
if incomplete_sub.card_details_response:
card_details_response = json.loads(
incomplete_sub.card_details_response)
if incomplete_sub.template:
template = json.loads(
incomplete_sub.template)
if incomplete_sub.billing_address_data:
billing_address_data = json.loads(
incomplete_sub.billing_address_data)
logger.debug("*******")
logger.debug(str(incomplete_sub))
logger.debug("*******")
logger.debug("1*******")
logger.debug(request)
logger.debug("2*******")
logger.debug(card_details_response)
logger.debug("3*******")
logger.debug(soc)
logger.debug("4*******")
logger.debug(gp_details)
logger.debug("5*******")
logger.debug(template)
logger.debug("6*******")
do_provisioning(
request=request,
stripe_api_cus_id=incomplete_sub.stripe_api_cus_id,
card_details_response=card_details_response,
stripe_subscription_obj=stripe_subscription_obj,
stripe_onetime_charge=soc,
gp_details=gp_details,
specs=specs,
vm_template_id=incomplete_sub.vm_template_id,
template=template,
billing_address_data=billing_address_data,
real_request=None
)
except IncompleteSubscriptions.DoesNotExist as ex:
logger.error(str(ex))
except IncompleteSubscriptions.MultipleObjectsReturned as ex:
logger.error(str(ex))
email_data = {
'subject': "IncompleteSubscriptions error",
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
'to': settings.DCL_ERROR_EMAILS_TO_LIST,
'body': "Response = %s" % str(ex),
}
send_plain_email_task.delay(email_data)
except Exception as ex:
logger.error(str(ex))
email_data = {
'subject': "invoice.paid Webhook error",
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
'to': settings.DCL_ERROR_EMAILS_TO_LIST,
'body': "Response = %s" % str(ex),
}
send_plain_email_task.delay(email_data)
elif event.type == 'invoice.payment_failed':
invoice_obj = event.data.object
logger.debug("Webhook Event: invoice.payment_failed")
logger.debug("invoice_obj %s " % str(invoice_obj))
if (invoice_obj.payment_failed and
invoice_obj.billing_reason == "subscription_update"):
logger.debug("Payment failed, inform the users")
elif event.type == 'payment_intent.succeeded':
payment_intent_obj = event.data.object
logger.debug("Webhook Event: payment_intent.succeeded")
logger.debug("payment_intent_obj %s " % str(payment_intent_obj))
try:
logger.debug("Looking for IncompletePaymentIntents %s " %
payment_intent_obj.id)
incomplete_pm = IncompletePaymentIntents.objects.get(
payment_intent_id=payment_intent_obj.id)
logger.debug("incomplete_pm = %s" % str(incomplete_pm.__dict__))
request = ""
soc = ""
card_details_response = ""
gp_details = ""
template = ""
billing_address_data = ""
if incomplete_pm.request:
request = json.loads(incomplete_pm.request)
logger.debug("request = %s" % str(request))
if incomplete_pm.stripe_charge_id:
soc = incomplete_pm.stripe_charge_id
logger.debug("stripe_onetime_charge = %s" % str(soc))
if incomplete_pm.gp_details:
gp_details = json.loads(incomplete_pm.gp_details)
logger.debug("gp_details = %s" % str(gp_details))
if incomplete_pm.card_details_response:
card_details_response = json.loads(
incomplete_pm.card_details_response)
logger.debug("card_details_response = %s" % str(card_details_response))
if incomplete_pm.billing_address_data:
billing_address_data = json.loads(
incomplete_pm.billing_address_data)
logger.debug("billing_address_data = %s" % str(billing_address_data))
logger.debug("1*******")
logger.debug(request)
logger.debug("2*******")
logger.debug(card_details_response)
logger.debug("3*******")
logger.debug(soc)
logger.debug("4*******")
logger.debug(gp_details)
logger.debug("5*******")
logger.debug(template)
logger.debug("6*******")
logger.debug(billing_address_data)
incomplete_pm.completed_at = datetime.datetime.now()
charges = ""
if len(payment_intent_obj.charges.data) > 0:
for d in payment_intent_obj.charges.data:
if charges == "":
charges = "%s" % d.id
else:
charges = "%s,%s" % (charges, d.id)
logger.debug("Charge ids = %s" % charges)
incomplete_pm.stripe_charge_id=charges
do_provisioning_generic(
request=request,
stripe_api_cus_id=incomplete_pm.stripe_api_cus_id,
card_details_response=card_details_response,
stripe_subscription_id=None,
stripe_charge_id=charges,
gp_details=gp_details,
billing_address_data=billing_address_data
)
incomplete_pm.save()
except IncompletePaymentIntents.DoesNotExist as ex:
logger.error(str(ex))
except (IncompletePaymentIntents.MultipleObjectsReturned,
Exception) as ex:
logger.error(str(ex))
email_data = {
'subject': "IncompletePaymentIntents error",
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
'to': settings.DCL_ERROR_EMAILS_TO_LIST,
'body': "Response = %s" % str(ex),
}
send_plain_email_task.delay(email_data)
else: else:
logger.error("Unhandled event : " + event.type) logger.error("Unhandled event : " + event.type)
return HttpResponse(status=200) return HttpResponse(status=200)