diff --git a/datacenterlight/templates/datacenterlight/landing_payment.html b/datacenterlight/templates/datacenterlight/landing_payment.html
index f112f0d9..66a0e63f 100644
--- a/datacenterlight/templates/datacenterlight/landing_payment.html
+++ b/datacenterlight/templates/datacenterlight/landing_payment.html
@@ -187,7 +187,6 @@
window.enter_your_card_text = '{%trans "Enter your credit card number" %}';
(function () {
- window.paymentIntentSecret = "{{payment_intent_secret}}";
window.stripeKey = "{{stripe_key}}";
window.current_lan = "{{LANGUAGE_CODE}}";
})();
diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html
index d8eb4934..1914acb8 100644
--- a/datacenterlight/templates/datacenterlight/order_detail.html
+++ b/datacenterlight/templates/datacenterlight/order_detail.html
@@ -2,6 +2,14 @@
{% load staticfiles bootstrap3 i18n custom_tags humanize %}
{% block content %}
+
{% if messages %}
@@ -321,5 +329,10 @@
{%endblock%}
diff --git a/datacenterlight/views.py b/datacenterlight/views.py
index 5d12a878..f50cf422 100644
--- a/datacenterlight/views.py
+++ b/datacenterlight/views.py
@@ -285,28 +285,13 @@ class PaymentOrderView(FormView):
product = GenericProduct.objects.get(
id=self.request.session['product_id']
)
- # TODO get the correct price of the product from order
- # confirmation
- stripe_utils = StripeUtils()
- payment_intent_response = stripe_utils.get_payment_intent(
- int(product.get_actual_price() * 100)
- )
- payment_intent = payment_intent_response.get('response_object')
- if not payment_intent:
- logger.error("Could not create payment_intent %s" %
- str(payment_intent_response))
- else:
- logger.debug("payment_intent.client_secret = %s" %
- str(payment_intent.client_secret))
context.update({'generic_payment_form': ProductPaymentForm(
prefix='generic_payment_form',
- initial={
- 'product_name': product.product_name,
- 'amount': float(product.get_actual_price()),
- 'recurring': product.product_is_subscription,
- 'description': product.product_description,
- 'payment_intent_secret': payment_intent.client_secret
- },
+ initial={'product_name': product.product_name,
+ 'amount': float(product.get_actual_price()),
+ 'recurring': product.product_is_subscription,
+ 'description': product.product_description,
+ },
product_id=product.id
), })
else:
@@ -479,8 +464,9 @@ class PaymentOrderView(FormView):
context['generic_payment_form'] = generic_payment_form
context['billing_address_form'] = address_form
return self.render_to_response(context)
- token = address_form.cleaned_data.get('token')
- if token is '':
+ id_payment_method = self.request.POST.get('id_payment_method',
+ None)
+ if id_payment_method is None:
card_id = address_form.cleaned_data.get('card')
logger.debug("token is empty and card_id is %s" % card_id)
try:
@@ -508,15 +494,16 @@ class PaymentOrderView(FormView):
)
request.session['card_id'] = user_card_detail.id
else:
- request.session['token'] = token
- logger.debug("token is %s" % token)
+ request.session["id_payment_method"] = id_payment_method
+ logger.debug("id_payment_method is %s" % id_payment_method)
if request.user.is_authenticated():
this_user = {
'email': request.user.email,
'name': request.user.name
}
customer = StripeCustomer.get_or_create(
- email=this_user.get('email'), token=token
+ email=this_user.get('email'),
+ id_payment_method=id_payment_method
)
else:
user_email = address_form.cleaned_data.get('email')
@@ -539,7 +526,7 @@ class PaymentOrderView(FormView):
)
customer = StripeCustomer.create_stripe_api_customer(
email=user_email,
- token=token,
+ token=id_payment_method,
customer_name=user_name)
except CustomUser.DoesNotExist:
logger.debug(
@@ -550,7 +537,7 @@ class PaymentOrderView(FormView):
)
customer = StripeCustomer.create_stripe_api_customer(
email=user_email,
- token=token,
+ token=id_payment_method,
customer_name=user_name)
billing_address = address_form.save()
@@ -622,11 +609,11 @@ class OrderConfirmationView(DetailView, FormView):
if (('specs' not in request.session or 'user' not in request.session)
and 'generic_payment_type' not in request.session):
return HttpResponseRedirect(reverse('datacenterlight:index'))
- if 'token' in self.request.session:
- token = self.request.session['token']
+ if 'id_payment_method' in self.request.session:
+ payment_method = self.request.session['id_payment_method']
stripe_utils = StripeUtils()
- card_details = stripe_utils.get_cards_details_from_token(
- token
+ card_details = stripe_utils.get_cards_details_from_payment_method(
+ payment_method
)
if not card_details.get('response_object'):
return HttpResponseRedirect(reverse('hosting:payment'))
@@ -635,6 +622,7 @@ class OrderConfirmationView(DetailView, FormView):
context['cc_brand'] = card_details_response['brand']
context['cc_exp_year'] = card_details_response['exp_year']
context['cc_exp_month'] = '{:02d}'.format(card_details_response['exp_month'])
+ context['id_payment_method'] = payment_method
else:
card_id = self.request.session.get('card_id')
card_detail = UserCardDetail.objects.get(id=card_id)
@@ -718,6 +706,27 @@ class OrderConfirmationView(DetailView, FormView):
'form': UserHostingKeyForm(request=self.request),
'keys': get_all_public_keys(self.request.user)
})
+
+ # Obtain PaymentIntent so that we can initiate and charge/subscribe
+ # the customer
+ stripe_utils = StripeUtils()
+ payment_intent_response = stripe_utils.get_payment_intent(
+ int(request.session['generic_payment_details']['amount'] *
+ 100),
+ customer=request.session['customer']
+ )
+ payment_intent = payment_intent_response.get(
+ 'response_object')
+ if not payment_intent:
+ logger.error("Could not create payment_intent %s" %
+ str(payment_intent_response))
+ else:
+ logger.debug("payment_intent.client_secret = %s" %
+ str(payment_intent.client_secret))
+ context.update({
+ 'payment_intent_secret': payment_intent.client_secret
+ })
+
context.update({
'site_url': reverse('datacenterlight:index'),
'page_header_text': _('Confirm Order'),
@@ -725,6 +734,8 @@ class OrderConfirmationView(DetailView, FormView):
request.session.get('billing_address_data')
),
'cms_integration': get_cms_integration('default'),
+ 'error_msg': get_error_response_dict("Error", request),
+ 'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
})
return render(request, self.template_name, context)
@@ -765,9 +776,9 @@ class OrderConfirmationView(DetailView, FormView):
'generic_payment_details': generic_payment_details
}
- if 'token' in request.session:
- card_details = stripe_utils.get_cards_details_from_token(
- request.session.get('token')
+ if 'id_payment_method' in request.session:
+ card_details = stripe_utils.get_cards_details_from_payment_method(
+ request.session.get('id_payment_method')
)
logger.debug(
"card_details=%s" % (card_details))
@@ -788,7 +799,7 @@ class OrderConfirmationView(DetailView, FormView):
)
if not ucd:
acc_result = stripe_utils.associate_customer_card(
- stripe_api_cus_id, request.session['token'],
+ stripe_api_cus_id, request.session['id_payment_method'],
set_as_default=True
)
if acc_result['response_object'] is None:
@@ -799,6 +810,21 @@ class OrderConfirmationView(DetailView, FormView):
)
)
return show_error(msg, self.request)
+ else:
+ # Associate PaymentMethod with the stripe customer
+ # and set it as the default source
+ acc_result = stripe_utils.associate_customer_card(
+ stripe_api_cus_id, request.session['id_payment_method'],
+ set_as_default=True
+ )
+ if acc_result['response_object'] is None:
+ msg = _(
+ 'An error occurred while associating the card.'
+ ' Details: {details}'.format(
+ details=acc_result['error']
+ )
+ )
+ return show_error(msg, self.request)
elif 'card_id' in request.session:
card_id = request.session.get('card_id')
user_card_detail = UserCardDetail.objects.get(id=card_id)
@@ -1334,9 +1360,7 @@ def do_provisioning(request, user, stripe_api_cus_id, card_details_response,
clear_all_session_vars(real_request)
-def show_error(msg, request):
- messages.add_message(request, messages.ERROR, msg,
- extra_tags='failed_payment')
+def get_error_response_dict(msg, request):
response = {
'status': False,
'redirect': "{url}#{section}".format(
@@ -1356,4 +1380,10 @@ def show_error(msg, request):
' On close of this popup, you will be redirected back to'
' the payment page.'))
}
- return JsonResponse(response)
+ return response
+
+
+def show_error(msg, request):
+ messages.add_message(request, messages.ERROR, msg,
+ extra_tags='failed_payment')
+ return JsonResponse(get_error_response_dict(msg,request))
diff --git a/hosting/static/hosting/js/payment.js b/hosting/static/hosting/js/payment.js
index 933e15df..a2e2717a 100644
--- a/hosting/static/hosting/js/payment.js
+++ b/hosting/static/hosting/js/payment.js
@@ -84,68 +84,72 @@ $(document).ready(function () {
var hasCreditcard = window.hasCreditcard || false;
if (!hasCreditcard && window.stripeKey) {
var stripe = Stripe(window.stripeKey);
- var element_style = {
- fonts: [{
- family: 'lato-light',
- src: 'url(https://cdn.jsdelivr.net/font-lato/2.0/Lato/Lato-Light.woff) format("woff2")'
- }, {
- family: 'lato-regular',
- src: 'url(https://cdn.jsdelivr.net/font-lato/2.0/Lato/Lato-Regular.woff) format("woff2")'
- }
- ],
- locale: window.current_lan
- };
- var elements = stripe.elements(element_style);
- var credit_card_text_style = {
- base: {
- iconColor: '#666EE8',
- color: '#31325F',
- lineHeight: '25px',
- fontWeight: 300,
- fontFamily: "'lato-light', sans-serif",
- fontSize: '14px',
- '::placeholder': {
- color: '#777'
+ if (window.pm_id) {
+
+ } else {
+ var element_style = {
+ fonts: [{
+ family: 'lato-light',
+ src: 'url(https://cdn.jsdelivr.net/font-lato/2.0/Lato/Lato-Light.woff) format("woff2")'
+ }, {
+ family: 'lato-regular',
+ src: 'url(https://cdn.jsdelivr.net/font-lato/2.0/Lato/Lato-Regular.woff) format("woff2")'
}
- },
- invalid: {
- iconColor: '#eb4d5c',
- color: '#eb4d5c',
- lineHeight: '25px',
- fontWeight: 300,
- fontFamily: "'lato-regular', sans-serif",
- fontSize: '14px',
- '::placeholder': {
+ ],
+ locale: window.current_lan
+ };
+ var elements = stripe.elements(element_style);
+ var credit_card_text_style = {
+ base: {
+ iconColor: '#666EE8',
+ color: '#31325F',
+ lineHeight: '25px',
+ fontWeight: 300,
+ fontFamily: "'lato-light', sans-serif",
+ fontSize: '14px',
+ '::placeholder': {
+ color: '#777'
+ }
+ },
+ invalid: {
+ iconColor: '#eb4d5c',
color: '#eb4d5c',
- fontWeight: 400
+ lineHeight: '25px',
+ fontWeight: 300,
+ fontFamily: "'lato-regular', sans-serif",
+ fontSize: '14px',
+ '::placeholder': {
+ color: '#eb4d5c',
+ fontWeight: 400
+ }
}
- }
- };
+ };
- var enter_ccard_text = "Enter your credit card number";
- if (typeof window.enter_your_card_text !== 'undefined') {
- enter_ccard_text = window.enter_your_card_text;
+ var enter_ccard_text = "Enter your credit card number";
+ if (typeof window.enter_your_card_text !== 'undefined') {
+ enter_ccard_text = window.enter_your_card_text;
+ }
+ var cardNumberElement = elements.create('cardNumber', {
+ style: credit_card_text_style,
+ placeholder: enter_ccard_text
+ });
+ cardNumberElement.mount('#card-number-element');
+
+ var cardExpiryElement = elements.create('cardExpiry', {
+ style: credit_card_text_style
+ });
+ cardExpiryElement.mount('#card-expiry-element');
+
+ var cardCvcElement = elements.create('cardCvc', {
+ style: credit_card_text_style
+ });
+ cardCvcElement.mount('#card-cvc-element');
+ cardNumberElement.on('change', function (event) {
+ if (event.brand) {
+ setBrandIcon(event.brand);
+ }
+ });
}
- var cardNumberElement = elements.create('cardNumber', {
- style: credit_card_text_style,
- placeholder: enter_ccard_text
- });
- cardNumberElement.mount('#card-number-element');
-
- var cardExpiryElement = elements.create('cardExpiry', {
- style: credit_card_text_style
- });
- cardExpiryElement.mount('#card-expiry-element');
-
- var cardCvcElement = elements.create('cardCvc', {
- style: credit_card_text_style
- });
- cardCvcElement.mount('#card-cvc-element');
- cardNumberElement.on('change', function (event) {
- if (event.brand) {
- setBrandIcon(event.brand);
- }
- });
}
var submit_form_btn = $('#payment_button_with_creditcard');
@@ -163,7 +167,7 @@ $(document).ready(function () {
if (parts.length === 2) return parts.pop().split(";").shift();
}
- function submitBillingForm() {
+ function submitBillingForm(pmId) {
var billing_form = $('#billing-form');
var recurring_input = $('#id_generic_payment_form-recurring');
billing_form.append('');
@@ -174,20 +178,46 @@ $(document).ready(function () {
billing_form.append('');
}
billing_form.append('');
+ billing_form.append('');
billing_form.submit();
}
var $form_new = $('#payment-form-new');
$form_new.submit(payWithPaymentIntent);
+ window.result = "";
+ window.card = "";
function payWithPaymentIntent(e) {
e.preventDefault();
- stripe.confirmCardPayment(
+ function stripePMHandler(paymentMethod) {
+ // Insert the token ID into the form so it gets submitted to the server
+ console.log(paymentMethod);
+ $('#id_payment_method').val(paymentMethod.id);
+ submitBillingForm(paymentMethod.id);
+ }
+ stripe.createPaymentMethod({
+ type: 'card',
+ card: cardNumberElement,
+ })
+ .then(function(result) {
+ // Handle result.error or result.paymentMethod
+ window.result = result;
+ if(result.error) {
+ var errorElement = document.getElementById('card-errors');
+ errorElement.textContent = result.error.message;
+ } else {
+ console.log("created paymentMethod " + result.paymentMethod.id);
+ stripePMHandler(result.paymentMethod);
+ }
+ });
+ window.card = cardNumberElement;
+ /* stripe.confirmCardPayment(
window.paymentIntentSecret,
{
payment_method: {card: cardNumberElement}
}
).then(function(result) {
+ window.result = result;
if (result.error) {
// Display error.message in your UI.
var errorElement = document.getElementById('card-errors');
@@ -198,7 +228,7 @@ $(document).ready(function () {
alert("Thanks for the order. Your product will be provisioned " +
"as soon as we receive the payment. Thank you.");
}
- });
+ }); */
}
function payWithStripe_new(e) {
e.preventDefault();
diff --git a/hosting/static/hosting/js/virtual_machine_detail.js b/hosting/static/hosting/js/virtual_machine_detail.js
index dec8e680..e1bfd3a8 100644
--- a/hosting/static/hosting/js/virtual_machine_detail.js
+++ b/hosting/static/hosting/js/virtual_machine_detail.js
@@ -92,6 +92,35 @@ $(document).ready(function() {
});
var create_vm_form = $('#virtual_machine_create_form');
+ create_vm_form.submit(placeOrderPaymentIntent);
+
+ function placeOrderPaymentIntent(e) {
+ e.preventDefault();
+ var stripe = Stripe(window.stripeKey);
+ stripe.confirmCardPayment(
+ window.paymentIntentSecret,
+ {
+ payment_method: window.pm_id
+ }
+ ).then(function(result) {
+ window.result = result;
+ if (result.error) {
+ // Display error.message in your UI.
+ var errorElement = document.getElementById('card-errors');
+ errorElement.textContent = result.error.message;
+ } else {
+ // The payment has succeeded
+ // Display a success message
+ alert("Thanks for the order. Your product will be provisioned " +
+ "as soon as we receive the payment. Thank you.");
+ modal_btn.attr('href', err).removeClass('hide');
+ fa_icon.attr('class', 'checkmark');
+ $('#createvm-modal-title').text(data.success.msg_title);
+ $('#createvm-modal-body').html(data.success.msg_body);
+ }
+ });
+ }
+ /*
create_vm_form.submit(function () {
$('#btn-create-vm').prop('disabled', true);
$.ajax({
@@ -154,7 +183,7 @@ $(document).ready(function() {
}
});
return false;
- });
+ });*/
$('#createvm-modal').on('hidden.bs.modal', function () {
$(this).find('.modal-footer .btn').addClass('hide');
});
diff --git a/hosting/views.py b/hosting/views.py
index cc038d12..f18a22b7 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -694,16 +694,17 @@ class SettingsView(LoginRequiredMixin, FormView):
msg = _("Billing address updated successfully")
messages.add_message(request, messages.SUCCESS, msg)
else:
- token = form.cleaned_data.get('token')
+ # TODO : Test this flow
+ id_payment_method = form.cleaned_data.get('id_payment_method')
stripe_utils = StripeUtils()
- card_details = stripe_utils.get_cards_details_from_token(
- token
+ card_details = stripe_utils.get_cards_details_from_payment_method(
+ id_payment_method
)
if not card_details.get('response_object'):
form.add_error("__all__", card_details.get('error'))
return self.render_to_response(self.get_context_data())
stripe_customer = StripeCustomer.get_or_create(
- email=request.user.email, token=token
+ email=request.user.email, id_payment_method=id_payment_method
)
card = card_details['response_object']
if UserCardDetail.get_user_card_details(stripe_customer, card):
@@ -711,7 +712,7 @@ class SettingsView(LoginRequiredMixin, FormView):
messages.add_message(request, messages.ERROR, msg)
else:
acc_result = stripe_utils.associate_customer_card(
- request.user.stripecustomer.stripe_id, token
+ request.user.stripecustomer.stripe_id, id_payment_method
)
if acc_result['response_object'] is None:
msg = _(
@@ -1085,7 +1086,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView):
template, specs, stripe_customer_id, billing_address_data,
vm_template_id, stripe_api_cus_id)
)
- if 'token' in self.request.session:
+ if 'id_payment_method' in self.request.session:
card_details = stripe_utils.get_cards_details_from_token(
request.session['token']
)
@@ -1102,7 +1103,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView):
)
if not ucd:
acc_result = stripe_utils.associate_customer_card(
- stripe_api_cus_id, request.session['token'],
+ stripe_api_cus_id, request.session['id_payment_method'],
set_as_default=True
)
if acc_result['response_object'] is None:
diff --git a/membership/models.py b/membership/models.py
index 703b4800..079b60e0 100644
--- a/membership/models.py
+++ b/membership/models.py
@@ -296,7 +296,7 @@ class StripeCustomer(models.Model):
return None
@classmethod
- def get_or_create(cls, email=None, token=None):
+ def get_or_create(cls, email=None, token=None, id_payment_method=None):
"""
Check if there is a registered stripe customer with that email
or create a new one
diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py
index bf731508..a4cc2c6a 100644
--- a/utils/stripe_utils.py
+++ b/utils/stripe_utils.py
@@ -83,12 +83,15 @@ class StripeUtils(object):
customer.save()
@handleStripeError
- def associate_customer_card(self, stripe_customer_id, token,
+ def associate_customer_card(self, stripe_customer_id, id_payment_method,
set_as_default=False):
customer = stripe.Customer.retrieve(stripe_customer_id)
- card = customer.sources.create(source=token)
+ stripe.PaymentMethod.attach(
+ id_payment_method,
+ customer=stripe_customer_id,
+ )
if set_as_default:
- customer.default_source = card.id
+ customer.invoice_settings.default_payment_method = id_payment_method
customer.save()
return True
@@ -100,6 +103,7 @@ class StripeUtils(object):
@handleStripeError
def update_customer_card(self, customer_id, token):
+ # TODO replace token with payment intent
customer = stripe.Customer.retrieve(customer_id)
current_card_token = customer.default_source
customer.sources.retrieve(current_card_token).delete()
@@ -188,6 +192,24 @@ class StripeUtils(object):
}
return card_details
+ @handleStripeError
+ def get_cards_details_from_payment_method(self, payment_method_id):
+ payment_method = stripe.PaymentMethod.retrieve(payment_method_id)
+ # payment_method does not always seem to have a card with id
+ # if that is the case, fallback to payment_method_id for card_id
+ card_id = payment_method_id
+ if hasattr(payment_method.card, 'id'):
+ card_id = payment_method.card.id
+ card_details = {
+ 'last4': payment_method.card.last4,
+ 'brand': payment_method.card.brand,
+ 'exp_month': payment_method.card.exp_month,
+ 'exp_year': payment_method.card.exp_year,
+ 'fingerprint': payment_method.card.fingerprint,
+ 'card_id': card_id
+ }
+ return card_details
+
def check_customer(self, stripe_cus_api_id, user, token):
try:
customer = stripe.Customer.retrieve(stripe_cus_api_id)
@@ -207,11 +229,11 @@ class StripeUtils(object):
return customer
@handleStripeError
- def create_customer(self, token, email, name=None):
+ def create_customer(self, id_payment_method, email, name=None):
if name is None or name.strip() == "":
name = email
customer = self.stripe.Customer.create(
- source=token,
+ payment_method=id_payment_method,
description=name,
email=email
)
@@ -494,19 +516,19 @@ class StripeUtils(object):
return tax_id_obj
@handleStripeError
- def get_payment_intent(self, amount):
- """
- Adds VM metadata to a subscription
- :param amount: the amount of payment_intent
- :return:
+ def get_payment_intent(self, amount, customer):
+ """ Create a stripe PaymentIntent of the given amount and return it
+ :param amount: the amount of payment_intent
+ :return:
"""
payment_intent_obj = stripe.PaymentIntent.create(
amount=amount,
- currency='chf'
+ currency='chf',
+ customer=customer
)
return payment_intent_obj
def compare_vat_numbers(self, vat1, vat2):
_vat1 = vat1.replace(" ", "").replace(".", "").replace("-","")
_vat2 = vat2.replace(" ", "").replace(".", "").replace("-","")
- return True if _vat1 == _vat2 else False
\ No newline at end of file
+ return True if _vat1 == _vat2 else False