Implement the Success page of the order

This commit is contained in:
amalelshihaby 2021-08-02 11:24:19 +02:00
parent 5564400ef8
commit 030a0cd501
13 changed files with 328 additions and 47 deletions

View File

@ -26,9 +26,10 @@ $( document ).ready(function() {
} else { } else {
// The payment has succeeded // The payment has succeeded
// Display a success message // Display a success message
modal_btn.attr('href', data.redirect).removeClass('sr-only sr-only-focusable'); window.location.href = '/order/success/';
$('#createvm-modal-title').text("Order Succeeded"); // modal_btn.attr('href', data.redirect).removeClass('sr-only sr-only-focusable');
$('#createvm-modal-body').html("Order has been added and the instance will be ready soon"); // $('#createvm-modal-title').text("Order Succeeded");
// $('#createvm-modal-body').html("Order has been added and the instance will be ready soon");
} }
} }
}); });

View File

@ -10,7 +10,7 @@ function setBrandIcon(brand) {
$(document).ready(function () { $(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 == undefined) { if (window.pm_id == undefined) {
var element_style = { var element_style = {

View File

@ -16,16 +16,18 @@
<!-- Primary Navigation <!-- Primary Navigation
============================== --> ============================== -->
{% url 'matrix:index' as index_url %}
{% url 'matrix:payments' as payments_url %}
<nav class="primary-menu navbar navbar-expand-lg"> <nav class="primary-menu navbar navbar-expand-lg">
<div id="header-nav" class="collapse navbar-collapse"> <div id="header-nav" class="collapse navbar-collapse">
<ul class="navbar-nav mr-auto"> <ul class="navbar-nav mr-auto">
<li><a href="{% url 'matrix:index' %}">Home</a></li> <li class="{% if request.path == index_url %}active{%endif%}"><a href="{{index_url}}">Home</a></li>
{% if not request.user.is_authenticated %} {% if not request.user.is_authenticated %}
<li><a href="">Pricing</a></li> <li><a href="">Pricing</a></li>
<li><a href="">Contact Us</a></li> <li><a href="">Contact Us</a></li>
{% else %} {% else %}
<li><a href="">Dashboard</a></li> <li><a href=>Dashboard</a></li>
<li><a href="">Instances</a></li> <li class="{% if request.path == payments_url %}active{%endif%}"><a href="{{payments_url}}">Payments</a></li>
<li><a href="">Help</a></li> <li><a href="">Help</a></li>
{% endif %} {% endif %}
</ul> </ul>

View File

@ -0,0 +1,49 @@
{% load static i18n %}
{% get_current_language as LANGUAGE_CODE %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Matrix Hosting by ungleich" />
<meta name="author" content="ungleich glarus ag" />
<title>
Hosting Invoice
</title>
<!-- Web Fonts
======================= -->
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Rubik:300,300i,400,400i,500,500i,700,700i,900,900i' type='text/css'>
<!-- Stylesheet
======================= -->
<link href="{% static 'matrixhosting/css/bootstrap.min.css' %}" rel="stylesheet" />
</head>
<body>
<!-- Container -->
<div class="container-fluid invoice-container">
<!-- Header -->
<header>
<div class="row align-items-center">
<div class="col-sm-7 text-center text-sm-start mb-3 mb-sm-0">
<img id="logo" src="{% static 'matrixhosting/images/logo.png' %}" title="matrixhosting" alt="matrixhosting" />
</div>
<div class="col-sm-5 text-center text-sm-end">
<h4 class="text-7 mb-0">Invoice</h4>
</div>
</div>
<hr>
</header>
<!-- Main Content -->
<main>
</main>
<!-- Footer -->
<footer class="text-center mt-4">
</footer>
</div>
</body>
</html>

View File

@ -10,7 +10,7 @@
<div class="row mt-3 mb-4"> <div class="row mt-3 mb-4">
<div class="col-lg-12 mx-auto mb-4"> <div class="col-lg-12 mx-auto mb-4">
<div class="row widget-steps"> <div class="row widget-steps">
<div class="col-4 step disabled"> <div class="col-4 step complete">
<div class="step-name">{%trans "Details" %}</div> <div class="step-name">{%trans "Details" %}</div>
<div class="progress"> <div class="progress">
<div class="progress-bar"></div> <div class="progress-bar"></div>
@ -186,7 +186,7 @@ aria-hidden="true" data-backdrop="static" data-keyboard="false">
</div> </div>
</div> </div>
<div class="modal-footer mt-4"> <div class="modal-footer mt-4">
<a id="createvm-modal-done-btn" class="btn btn-success btn-ok btn-wide sr-only sr-only-focusable" href="">{% trans "OK" %}</a> <a id="createvm-modal-done-btn" class="btn btn-success btn-ok btn-wide sr-only sr-only-focusable" href="{% url 'matrix:order_success' %}">{% trans "OK" %}</a>
<button id="createvm-modal-close-btn" type="button" class="btn btn-danger btn-ok btn-wide sr-only sr-only-focusable" data-dismiss="modal" aria-label="create-vm-close">{% trans "Close" %}</button> <button id="createvm-modal-close-btn" type="button" class="btn btn-danger btn-ok btn-wide sr-only sr-only-focusable" data-dismiss="modal" aria-label="create-vm-close">{% trans "Close" %}</button>
</div> </div>
</div> </div>
@ -196,16 +196,11 @@ aria-hidden="true" data-backdrop="static" data-keyboard="false">
{% endblock %} {% endblock %}
{% block js_extra %} {% block js_extra %}
{% if stripe_key %} <script type="text/javascript">
{% get_current_language as LANGUAGE_CODE %}
<script type="text/javascript">
var error_url = '{{ error_msg.redirect }}'; var error_url = '{{ error_msg.redirect }}';
var success_url = '{{ success_msg.redirect }}'; var success_url = '{{ success_msg.redirect }}';
</script> </script>
{%endif%}
<!-- jQuery --> <!-- jQuery -->
<script src="https://js.stripe.com/v3/"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.min.js"></script>
<!-- Custom JS --> <!-- Custom JS -->
<script type="text/javascript" src="{% static 'matrixhosting/js/order.js' %}"></script> <script type="text/javascript" src="{% static 'matrixhosting/js/order.js' %}"></script>

View File

@ -233,9 +233,9 @@
<label class="custom-control-label" for="new-card"><h6 class="mb-0">{% trans "Add New Card" %}</h6></label> <label class="custom-control-label" for="new-card"><h6 class="mb-0">{% trans "Add New Card" %}</h6></label>
</div> </div>
<div class="text-right"> <div class="text-right">
<button id="checkout-btn" class="btn btn-primary btn-wide" type="submit" name="payment-button">{%trans "Checkout" %}</button> <button id="checkout-btn" class="btn btn-primary btn-wide" style="display:none;" type="submit" name="payment-button">{%trans "Checkout" %}</button>
</div> </div>
<div id="newcard"> <div id="newcard" style="display:none;">
<div class="card-details-box p-4 bg-light"> <div class="card-details-box p-4 bg-light">
{% include "matrixhosting/includes/_card.html" %} {% include "matrixhosting/includes/_card.html" %}
</div> </div>

View File

@ -0,0 +1,56 @@
{% extends "matrixhosting/base.html" %}
{% load static i18n %}
{% block title %} Request Details {% endblock %}
{% block content %}
<div class="container">
<div class="row mt-3 mb-4">
<div class="col-lg-12 mx-auto mb-4">
<div class="row widget-steps">
<div class="col-4 step complete">
<div class="step-name">{%trans "Details" %}</div>
<div class="progress">
<div class="progress-bar"></div>
</div>
<a href="#" class="step-dot"></a>
</div>
<div class="col-4 step complete">
<div class="step-name">{%trans "Confirm" %}</div>
<div class="progress">
<div class="progress-bar"></div>
</div>
<a href="#" class="step-dot"></a>
</div>
<div class="col-4 step complete">
<div class="step-name">{%trans "Success" %}</div>
<div class="progress">
<div class="progress-bar"></div>
</div>
<a href="#" class="step-dot"></a>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-9 col-lg-7 col-xl-6 mx-auto">
<div class="bg-white text-center shadow-sm rounded p-3 pt-sm-4 pb-sm-5 px-sm-5 mb-4">
<div class="my-4">
<p class="text-success text-20 line-height-07"><i class="fas fa-check-circle"></i></p>
<p class="text-success text-8 font-weight-500 line-height-07">{%trans "Success!" %}</p>
<p class="lead">{%trans "Order has been successfully added" %}</p>
</div>
<p class="text-3 mb-4">{%trans "Your current balance is" %}<span class="text-4 font-weight-500"> {{balance}} CHF. </span>{%trans "See transaction details under" %} <a class="btn-link" href="{% url 'matrix:payments' %}">{%trans "Payments" %}</a>.</p>
<a href="{% url 'matrix:invoice_download' %}" class="btn btn-white border border-success text-success" role="button"><i class="fas fa-print"></i> {%trans "Download Invoice" %}</a>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js_extra %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.min.js"></script>
<!-- Custom JS -->
<script type="text/javascript" src="{% static 'matrixhosting/js/order.js' %}"></script>
{% endblock js_extra %}

View File

@ -0,0 +1,124 @@
{% extends "matrixhosting/base.html" %}
{% load static i18n %}
{% block title %} Payments {% endblock %}
{% block content %}
<div class="container">
<div class="bg-white shadow-md rounded p-4 m-4">
<div class="row">
<div class="col-md-3">
<ul class="nav nav-tabs flex-column" id="myTabVertical" role="tablist">
<li class="nav-item"> <a class="nav-link active" id="first-tab" data-toggle="tab" href="#transactions" role="tab" aria-controls="firstTab" aria-selected="true">All Transactions</a> </li>
<li class="nav-item"> <a class="nav-link" id="fourth-tab" data-toggle="tab" href="#cards" role="tab" aria-controls="fourthTab" aria-selected="false">Payment Methods</a> </li>
</ul>
</div>
<div class="col-md-9">
<div class="tab-content my-3" id="myTabContentVertical">
<div class="tab-pane fade show active" id="transactions" role="tabpanel" aria-labelledby="transactions">
<div class="">
<!-- Filter
============================================= -->
<div class="row">
<div class="col mb-2">
<form id="filterTransactions" method="post">
<div class="form-row">
<!-- Date Range
========================= -->
<div class="col-sm-6 col-md-6 form-group">
<input id="dateRange" type="text" class="form-control" placeholder="Date Range">
<span class="icon-inside"><i class="fas fa-calendar-alt"></i></span>
</div>
<div class="col col-sm-6 text-right text-2">
<div class="featured-box float-right style-3">
<div class="featured-box-icon text-9 text-light"> <i class="fas fa-wallet"></i> </div>
<h3 class="text-6 font-weight-400 text-dark">{{balance}} CHF</h3>
<p class="text-muted text-3 opacity-8">{% trans "Available Balance"%}</p>
</div>
</div>
<!-- All Filters
================================ -->
<div class="col-12 mb-3" id="allFilters">
<div class="custom-control custom-radio custom-control-inline">
<input type="radio" id="allTransactions" name="allFilters" class="custom-control-input" checked="">
<label class="custom-control-label" for="allTransactions">{% trans "All Transactions"%}</label>
</div>
<div class="custom-control custom-radio custom-control-inline">
<input type="radio" id="withdrawal" name="allFilters" class="custom-control-input">
<label class="custom-control-label" for="withdrawal">{% trans "Withdrawal"%}</label>
</div>
<div class="custom-control custom-radio custom-control-inline">
<input type="radio" id="deposit" name="allFilters" class="custom-control-input">
<label class="custom-control-label" for="deposit">{% trans "Deposit"%}</label>
</div>
</div>
<!-- All Filters End -->
</div>
</form>
</div>
</div>
<!-- Filter End -->
<!-- All Transactions
============================================= -->
<div class="bg-white shadow-sm border border-light rounded py-4 mb-4">
<h3 class="text-5 font-weight-400 d-flex align-items-center px-4 mb-4">{% trans "All Transactions"%}</h3>
<!-- Title
=============================== -->
<div class="transaction-title py-2 px-4">
<div class="row">
<div class="col-1 col-sm-1 text-center"><span class="">{% trans "ID"%}</span></div>
<div class="col-2 col-sm-2 text-center"><span class="">{% trans "Date"%}</span></div>
<div class="col col-sm-4">{% trans "Description" %}</div>
<div class="col-auto col-sm-2 d-none d-sm-block text-center">{% trans "Type"%}</div>
<div class="col-3 col-sm-3 text-right">{% trans "Amount"%}</div>
</div>
</div>
<!-- Title End -->
<!-- Transaction List
=============================== -->
<div class="transaction-list">
{% for payment in object_list %}
<div class="transaction-item px-4 py-3" data-id={{payment.id}}>
<div class="row align-items-center flex-row">
<div class="col-1 col-sm-1 text-center"> <span class="d-block text-3 font-weight-400">#{{payment.id}}</span></div>
<div class="col-2 col-sm-2 text-center"> <span class="d-block text-3 font-weight-300">{{payment.timestamp|date:"M d, Y"}}</span> <span class="d-block text-1 font-weight-300 text-uppercase">{{payment.timestamp|date:"H:i"}}</span> </div>
<div class="col col-sm-4"> <span class="d-block text-muted text-3">{{payment.notes}}</span></div>
<div class="col-auto col-sm-2 d-none d-sm-block text-center text-2"> <span class=" text-uppercase">{{payment.type}}</span> </div>
<div class="col-3 col-sm-3 text-right text-3"> <span class="text-nowrap">{% if payment.type == 'withdraw' %}- {%else%}+ {%endif%}{{payment.amount}}</span> <span class="text-1 text-uppercase">({{payment.currency}})</span> </div>
</div>
</div>
{%endfor%}
</div>
<!-- Transaction List End -->
<!-- Pagination will handled later
============================================= -->
<ul class="pagination justify-content-center mt-4 mb-0" style="display: none;">
<li class="page-item disabled"> <a class="page-link" href="#" tabindex="-1"><i class="fas fa-angle-left"></i></a> </li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item active"> <a class="page-link" href="#">2 <span class="sr-only">(current)</span></a> </li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item d-flex align-content-center flex-wrap text-muted text-5 mx-1">......</li>
<li class="page-item"><a class="page-link" href="#">15</a></li>
<li class="page-item"> <a class="page-link" href="#"><i class="fas fa-angle-right"></i></a> </li>
</ul>
<!-- Paginations end -->
</div>
<!-- All Transactions End -->
</div>
</div>
<div class="tab-pane fade" id="cards" role="tabpanel" aria-labelledby="cards">
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js_extra %}
{% endblock js_extra %}

View File

@ -2,13 +2,16 @@ from django.urls import path, include
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from .views import IndexView, OrderPaymentView, OrderDetailsView, Dashboard from .views import *
app_name = 'matrixhosting' app_name = 'matrixhosting'
urlpatterns = [ urlpatterns = [
path('order/new/', OrderPaymentView.as_view(), name='payment'), path('order/new/', OrderPaymentView.as_view(), name='payment'),
path('order/confirm/', OrderDetailsView.as_view(), name='order_confirmation'), path('order/confirm/', OrderDetailsView.as_view(), name='order_confirmation'),
path('order/success/', OrderSuccessView.as_view(), name='order_success'),
path('order/invoice/download', InvoiceDownloadView.as_view(), name='invoice_download'),
path('payments/', PaymentsView.as_view(), name='payments'),
path('dashboard/', Dashboard.as_view(), name='dashboard'), path('dashboard/', Dashboard.as_view(), name='dashboard'),
path('', IndexView.as_view(), name='index'), path('', IndexView.as_view(), name='index'),
] ]

19
matrixhosting/utils.py Normal file
View File

@ -0,0 +1,19 @@
import os
from io import BytesIO
from django.http import HttpResponse
from django.template.loader import get_template
from django.conf import settings
from xhtml2pdf import pisa
def render_to_pdf(template_src, context_dict={}):
template = get_template(template_src)
html = template.render(context_dict)
result = BytesIO()
# pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)
links = lambda uri, rel: os.path.join(settings.MEDIA_ROOT, uri.replace(settings.MEDIA_URL, ''))
pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")),dest=result)
if not pdf.err:
return HttpResponse(result.getvalue(), content_type='application/pdf')
return None

View File

@ -15,7 +15,7 @@ from matrixhosting.forms import InitialRequestForm, RequestHostedVMForm, Billing
from django.urls import reverse from django.urls import reverse
from django.conf import settings from django.conf import settings
from django.http import ( from django.http import (
HttpResponseRedirect, JsonResponse HttpResponseRedirect, JsonResponse, HttpResponse
) )
from rest_framework import viewsets, permissions from rest_framework import viewsets, permissions
@ -27,6 +27,7 @@ from uncloud_pay.selectors import get_billing_address_for_user, has_enough_balan
import uncloud_pay.stripe as uncloud_stripe import uncloud_pay.stripe as uncloud_stripe
from .models import VMInstance from .models import VMInstance
from .serializers import * from .serializers import *
from .utils import render_to_pdf
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -156,8 +157,6 @@ class OrderPaymentView(FormView):
amount = get_balance_for_user(self.request.user) - decimal.Decimal(pricing["total"]) amount = get_balance_for_user(self.request.user) - decimal.Decimal(pricing["total"])
if (amount < 0): if (amount < 0):
payment_id = Payment.deposit(request.user, abs(amount), source='stripe') payment_id = Payment.deposit(request.user, abs(amount), source='stripe')
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>")
print(payment_id)
self.request.session['pricing'] = pricing self.request.session['pricing'] = pricing
self.request.session['order'] = specs self.request.session['order'] = specs
self.request.session['vat_validation_status'] = vat_validation_status self.request.session['vat_validation_status'] = vat_validation_status
@ -230,6 +229,42 @@ def finalize_order(request, customer, billing_address,
) )
return order return order
class OrderSuccessView(DetailView):
template_name = "matrixhosting/order_success.html"
context_object_name = "order"
model = Order
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
def get(self, request, *args, **kwargs):
context = {
'order': self.request.session.get('order'),
'balance': get_balance_for_user(self.request.user)
}
# if ('order' not in request.session):
# return HttpResponseRedirect(reverse('matrix:index'))
return render(request, self.template_name, context)
class InvoiceDownloadView(View):
def get(self, request, *args, **kwargs):
data = {
'today': datetime.date.today(),
'amount': 39.99,
'customer_name': 'Cooper Mann',
'order_id': 1233434,
}
pdf = render_to_pdf('matrixhosting/invoice.html', data)
if pdf:
response = HttpResponse(pdf, content_type='application/pdf')
content = "inline; filename=invoice.pdf"
content = "attachment; filename=invoice.pdf"
response['Content-Disposition'] = content
return response
return HttpResponse("Not found")
class Dashboard(ListView): class Dashboard(ListView):
template_name = "matrixhosting/dashboard.html" template_name = "matrixhosting/dashboard.html"
@ -248,28 +283,6 @@ class Dashboard(ListView):
return JsonResponse({'message': 'Successfully Cancelled'}) return JsonResponse({'message': 'Successfully Cancelled'})
def get_error_response_dict(request):
response = {
'status': False,
'redirect': "{url}#{section}".format(
url=(reverse('matrix:payment')),
section='payment_error'
),
'msg_title': str(_('Error.')),
'msg_body': str(
_('There was a payment related error.'
' On close of this popup, you will be redirected back to'
' the payment page.'))
}
return response
def show_error(msg, request):
messages.add_message(request, messages.ERROR, msg,
extra_tags='failed_payment')
return JsonResponse(get_error_response_dict(request))
class MachineViewSet(viewsets.ReadOnlyModelViewSet): class MachineViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = VMInstanceSerializer serializer_class = VMInstanceSerializer
permission_classes = [permissions.IsAuthenticated] permission_classes = [permissions.IsAuthenticated]
@ -278,3 +291,21 @@ class MachineViewSet(viewsets.ReadOnlyModelViewSet):
return VMInstance.objects.filter(owner=self.request.user) return VMInstance.objects.filter(owner=self.request.user)
class PaymentsView(ListView):
template_name = "matrixhosting/payments.html"
model = Payment
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
def get_context_data(self, **kwargs):
context = super(PaymentsView, self).get_context_data(**kwargs)
context.update({
'balance': get_balance_for_user(self.request.user)
})
return context
def get_queryset(self):
return Payment.objects.filter(owner=self.request.user)

View File

@ -8,6 +8,7 @@ psycopg2
ldap3 ldap3
django-allauth django-allauth
xmltodict xmltodict
xhtml2pdf
parsedatetime parsedatetime
# Follow are for creating graph models # Follow are for creating graph models
pyparsing pyparsing

View File

@ -82,7 +82,7 @@ class Payment(models.Model):
choices = ( choices = (
('withdraw', 'Withdraw Money'), ('withdraw', 'Withdraw Money'),
('deposit', 'Deposit Money') ('deposit', 'Deposit Money')
), null=False, blank=False, default="send") ), null=False, blank=False, default="deposit")
notes = models.TextField(default="", null=True, blank=True) notes = models.TextField(default="", null=True, blank=True)