I’m using stripe in a POS system that I’m developing for debit/credit card payment method. Thus, in my development environment (DE) which is running on Windows 11, the checkout payment is working perfectly. Now I’ve deployed the system to the Testing Environments (TE) which are running on Ubuntu 24 and Ubuntu 22, but I’m noticing that when I select “cash” as a payment method, in the DE the system shows the amount that need to be returned to customer.When I select “Credit/Debit” card as a payment method, the system shows fields that the customer needs to typpe the card information such card number, expiration date and CVV. However, in my TE none of this features are working.
Here are my codes:
Checkout View
class CheckoutView(View):
@method_decorator(login_required)
def get(self, request, order_id):
"""Handles GET requests to load the checkout page."""
order = get_object_or_404(Order, id=order_id)
if not order.items.exists():
modal_message = "Cette commande ne contient aucun produit. Veuillez ajouter des produits ou contactez un manager pour supprimer la commande."
return render(request, 'pos/orders/checkout.html', {
'order': order,
'modal_message': modal_message,
'currency': None,
'stripe_publishable_key': settings.STRIPE_PUBLISHABLE_KEY
})
# Fetch active currency
active_currency = Currency.objects.filter(is_active=True).first()
if not active_currency:
return render(request, 'pos/orders/checkout.html', {
'order': order,
'modal_message': 'Aucune devise active trouvée pour le magasin.',
'currency': None,
'stripe_publishable_key': settings.STRIPE_PUBLISHABLE_KEY
})
return render(request, 'pos/orders/checkout.html', {
'order': order,
'currency': active_currency,
'stripe_publishable_key': settings.STRIPE_PUBLISHABLE_KEY
})
@method_decorator(login_required)
def post(self, request, order_id):
"""Handles POST requests to process the checkout."""
order = get_object_or_404(Order, id=order_id)
# Ensure the order has items
if not order.items.exists():
modal_message = "Cette commande ne contient aucun produit. Le paiement ne peut pas être traité."
return render(request, 'pos/orders/checkout.html', {
'order': order,
'modal_message': modal_message,
'currency': None,
'stripe_publishable_key': settings.STRIPE_PUBLISHABLE_KEY
})
# Get active currency
active_currency = Currency.objects.filter(is_active=True).first()
if not active_currency:
return render(request, 'pos/orders/checkout.html', {
'order': order,
'modal_message': 'Aucune devise active trouvée pour le magasin.',
'currency': None,
'stripe_publishable_key': settings.STRIPE_PUBLISHABLE_KEY
})
# Get payment details
payment_method = request.POST.get('payment_method')
received_amount = request.POST.get('received_amount')
discount_type = request.POST.get('discount_type')
discount_amount = request.POST.get('discount_amount')
# Validate received amount
try:
received_amount = Decimal(received_amount) if received_amount else None
except (ValueError, InvalidOperation):
return render(request, 'pos/orders/checkout.html', {
'order': order,
'modal_message': 'Montant reçu invalide.',
'currency': active_currency,
'stripe_publishable_key': settings.STRIPE_PUBLISHABLE_KEY
})
# Apply discount if any
try:
if discount_type and discount_amount:
discount_amount = Decimal(discount_amount)
order.discount_type = discount_type
order.discount_amount = discount_amount
order.update_totals() # Recalculate totals
else:
order.discount_type = None
order.discount_amount = Decimal('0.00')
except (ValueError, InvalidOperation):
return render(request, 'pos/orders/checkout.html', {
'order': order,
'modal_message': 'Montant de remise invalide.',
'currency': active_currency,
'stripe_publishable_key': settings.STRIPE_PUBLISHABLE_KEY
})
payment_amount = order.total_amount_with_tax
change = None
if payment_method == 'cash':
# Cash payment logic
if received_amount is None or received_amount < payment_amount:
return render(request, 'pos/orders/checkout.html', {
'order': order,
'modal_message': 'Le montant reçu est inférieur au montant total.',
'currency': active_currency,
'stripe_publishable_key': settings.STRIPE_PUBLISHABLE_KEY
})
change = received_amount - payment_amount
else:
# Non-cash payment logic
payment_currency = 'USD' # Assuming the payment gateway uses USD
exchange_rate = 1.00 # Default to 1.00 if currencies are the same
if active_currency.code != payment_currency:
exchange_rate = active_currency.exchange_rate_to_usd
payment_amount = payment_amount / exchange_rate
# Process payment via Stripe
try:
PaymentService.initiate_payment(order, payment_amount, payment_currency)
except Exception as e:
return render(request, 'pos/orders/checkout.html', {
'order': order,
'modal_message': f'Erreur lors du traitement du paiement: {str(e)}',
'currency': active_currency,
'stripe_publishable_key': settings.STRIPE_PUBLISHABLE_KEY
})
received_amount = payment_amount
change = Decimal('0.00')
# Create the Bill record
bill = Bill.objects.create(
order=order,
bill_id=f'{order.id}-{timezone.now().strftime("%Y%m%d%H%M%S")}',
payment_method=payment_method,
payment_amount=payment_amount,
received_amount=received_amount,
change_amount=change
)
# Finalize the order
order.user = request.user
order.payment_method = payment_method
order.status = 'completed'
order.save()
# Update user profile and handle notifications
self.update_user_profile_and_notifications(order, request.user)
return render(request, 'pos/orders/checkout_complete.html', {
'order': order,
'bill': bill,
'received_amount': received_amount,
'change': change,
'currency': active_currency,
'stripe_publishable_key': settings.STRIPE_PUBLISHABLE_KEY
})
def update_user_profile_and_notifications(self, order, user):
"""Update user profile's daily balance and create notifications."""
today = timezone.now().date()
user_profile = user.profile
# Reset the daily balance if needed
if user_profile.last_balance_reset != today:
user_profile.daily_balance = Decimal('0.00')
user_profile.last_balance_reset = today
user_profile.daily_balance += order.total_amount_with_tax
user_profile.save()
# Notify the user
Notification.objects.create(
user=user,
message=f'Solde du jour mis à jour: {user_profile.daily_balance:.2f} {order.items.first().product_batch.product.currency.symbol}',
notification_type='balance_update'
)
# Handle stock alerts
stock_alert_roles = ['General_Manager', 'Manager', 'Stock_Controller', 'Supervisor']
if user_profile.roles.filter(name__in=stock_alert_roles).exists():
for item in order.items.all():
if item.product_batch.should_send_alert():
Notification.objects.create(
user=user,
message=self.generate_stock_alert_message(item.product_batch),
notification_type='stock_alert'
)
def generate_stock_alert_message(self, product_batch):
"""Generate a stock alert message based on stock levels."""
if product_batch.stock <= product_batch.product.minimum_stock_level:
return f"Alerte de stock faible pour {product_batch.product.name} (Seulement {product_batch.stock} restants !)"
elif product_batch.stock <= product_batch.product.low_stock_level:
return f"Le stock est faible pour {product_batch.product.name} ({product_batch.stock} restants !)"
return ""
{% load multiply %}
{% load static %}
<!DOCTYPE html>
<html lang="fr">
<head>
<title>Checkout</title>
<!-- Stylesheets -->
<link rel="stylesheet" href="{% static 'css/bootstrap4.min.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/font-awesome.min2.css' %}">
<link rel="stylesheet" href="{% static 'css/all.min.css' %}">
<!-- Include Select2 CSS -->
<link href="{% static 'css/select2.min.css' %}" rel="stylesheet" />
<!-- jQuery -->
<script src="{% static 'js/jquery-3.2.1.min.js' %}"></script>
<!-- Custom Styles -->
<style>
body {
display: flex;
height: 100vh;
margin: 0;
font-family: Arial, sans-serif;
background-color: #f8f9fa;
}
/* Sidebar styling */
.sidebar {
width: 200px; /* Reduced from 250px */
background-color: #2A3F54; /* Dark Slate */
padding-top: 20px;
position: fixed;
height: 100%;
transition: width 0.3s ease;
}
.sidebar.collapsed {
width: 70px;
}
.sidebar .nav-link {
color: white;
margin: 10px;
white-space: nowrap;
overflow: hidden;
transition: all 0.3s ease;
}
.sidebar.collapsed .nav-link {
text-align: center;
padding: 10px 0;
}
.sidebar.collapsed .nav-link span {
display: none;
}
.sidebar .nav-link:hover {
background-color: #34495e;
border-radius: 4px;
}
.sidebar .nav-link i {
margin-right: 10px;
}
.sidebar.collapsed .nav-link i {
margin-right: 0;
}
/* Top navbar styling */
.top-navbar {
background-color: #3C8DBC; /* Light blue */
color: white;
width: calc(100% - 200px); /* Adjusted for the new sidebar width */
margin-left: 200px; /* Adjusted for the new sidebar width */
position: fixed;
top: 0;
z-index: 1030;
transition: margin-left 0.3s ease, width 0.3s ease;
}
/* Content area styling */
.content {
margin-left: 200px; /* Adjusted for the new sidebar width */
margin-top: 10px; /* Adjust for navbar height */
padding: 100px;
width: calc(100% - 200px); /* Adjusted for the new sidebar width */
transition: margin-left 0.3s ease, width 0.3s ease;
}
.collapsed + .top-navbar {
margin-left: 70px; /* Maintains behavior for collapsed sidebar */
width: calc(100% - 70px);
}
.collapsed ~ .content {
margin-left: 70px; /* Maintains behavior for collapsed sidebar */
width: calc(100% - 70px);
}
.toggle-icon {
cursor: pointer;
font-size: 1.5rem;
}
/* Table styling */
.table th, .table td {
vertical-align: middle;
}
.form-control {
margin-bottom: 15px;
}
/* Button styling */
button {
margin-top: 10px;
}
</style>
</head>
<body>
<!-- Content Section -->
<div class="content">
<div class="row">
<div class="col-md-8">
<label align="center">Commande N° {{ order.id }}</label>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Produit</th>
<th>Quantité</th>
<th>Prix unitaire</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{% for item in order.items.all %}
<tr>
<td>{{ item.product_batch.product.name }}</td>
<td>{{ item.quantity }}</td>
<td>
{% if item.product_batch.discounted_price %}
{{ item.product_batch.discounted_price }} {{ currency.symbol }}
{% else %}
{{ item.product_batch.price }} {{ currency.symbol }}
{% endif %}
</td>
<td>
{% if item.product_batch.discounted_price %}
{{ item.quantity|multiply:item.product_batch.discounted_price|floatformat:2 }} {{ currency.symbol }}
{% else %}
{{ item.quantity|multiply:item.product_batch.price|floatformat:2 }} {{ currency.symbol }}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="3" class="text-right"><strong>Total à payer:</strong></td>
<td><strong>{{ order.total_amount_with_tax|floatformat:2 }} {{ currency.symbol }}</strong></td>
</tr>
</tfoot>
</table>
</div>
</div>
<!-- Payment Section -->
<div class="col-md-4">
<form id="checkout-form" method="post">
{% csrf_token %}
<!-- Mode de Paiement -->
<div class="form-group">
<label for="payment_method">Mode de Paiement</label>
<select class="form-control" id="payment_method" name="payment_method" required>
<option value="cash" selected>Cash</option>
<option value="credit_card">Credit Card</option>
<option value="debit_card">Debit Card</option>
<option value="holo">Holo</option>
<option value="huri_money">Huri Money</option>
</select>
</div>
<!-- Discount Type -->
<div class="form-group">
<label for="discount_type">Type de réduction</label>
<select class="form-control" id="discount_type" name="discount_type">
<option value="">Aucune</option>
<option value="rabais">Rabais</option>
<option value="remise">Remise</option>
<option value="ristourne">Ristourne</option>
</select>
</div>
<!-- Discount Amount -->
<div class="form-group">
<label for="discount_amount">Montant de la réduction</label>
<input type="number" class="form-control" id="discount_amount" name="discount_amount" min="0" step="0.01" value="0.00">
</div>
<!-- Montant reçu (for cash payment) -->
<div class="form-group" id="cash-payment">
<label for="received_amount">Montant reçu</label>
<input type="number" class="form-control" id="received_amount" name="received_amount" min="0" step="0.01">
<small id="change" class="form-text text-muted"></small>
</div>
<!-- Payment card fields for Stripe -->
<div id="card-element" class="form-group" style="display:none;">
<!-- A Stripe Element will be inserted here. -->
</div>
<div id="card-errors" role="alert" class="form-text text-danger"></div>
<button type="submit" class="btn btn-success btn-block">Confirmer la commande</button>
</form>
</div>
</div>
</div>
<!-- Stripe Integration & Checkout Form Handling -->
<script src="https://js.stripe.com/v3/"></script>
<script>
$(document).ready(function () {
console.log("Initializing Stripe...");
try {
// Initialize Stripe
var stripe = Stripe("{{ stripe_publishable_key }}");
var elements = stripe.elements();
// Create a card element
var card = elements.create('card', {
style: {
base: {
fontSize: '16px',
color: '#32325d',
'::placeholder': { color: '#aab7c4' }
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
}
});
// Mount the card element
card.mount('#card-element');
// Function to toggle payment fields
function togglePaymentFields() {
var paymentMethod = $('#payment_method').val();
console.log("Selected payment method:", paymentMethod);
if (paymentMethod === 'cash') {
$('#cash-payment').show();
$('#card-element').hide();
card.unmount(); // Ensure card fields are unmounted
$('#received_amount').val('');
$('#change').text('');
} else if (paymentMethod === 'credit_card' || paymentMethod === 'debit_card') {
$('#cash-payment').hide();
$('#card-element').show();
card.mount('#card-element'); // Remount card fields
} else {
$('#cash-payment').hide();
$('#card-element').hide();
card.unmount();
}
}
// Initialize the field toggle
togglePaymentFields();
// Trigger toggle on payment method change
$('#payment_method').change(function () {
togglePaymentFields();
});
// Update change amount dynamically
$('#received_amount').on('input', function () {
var received = parseFloat($(this).val());
var total = parseFloat("{{ order.total_amount_with_tax }}");
if (!isNaN(received) && received >= total) {
var change = received - total;
$('#change').text('Montant à retourner: ' + change.toFixed(2) + ' {{ currency.symbol }}');
} else {
$('#change').text('');
}
});
// Handle form submission
$('#checkout-form').submit(function (e) {
var paymentMethod = $('#payment_method').val();
if (paymentMethod === 'cash') {
var received = parseFloat($('#received_amount').val());
var total = parseFloat("{{ order.total_amount_with_tax }}");
var discountAmount = parseFloat($('#discount_amount').val()) || 0;
total -= discountAmount;
if (isNaN(received) || received < total) {
alert('Le montant reçu est insuffisant.');
e.preventDefault();
}
} else if (paymentMethod === 'credit_card' || paymentMethod === 'debit_card') {
e.preventDefault();
stripe.createPaymentMethod({
type: 'card',
card: card
}).then(function (result) {
if (result.error) {
console.error("Error creating payment method:", result.error.message);
$('#card-errors').text(result.error.message);
} else {
$.post("{% url 'posApp:create_stripe_payment_intent' order.id %}", {
'csrfmiddlewaretoken': '{{ csrf_token }}',
'discount_type': $('#discount_type').val(),
'discount_amount': $('#discount_amount').val()
}).done(function (data) {
stripe.confirmCardPayment(data.client_secret, {
payment_method: result.paymentMethod.id
}).then(function (result) {
if (result.error) {
console.error("Error confirming payment:", result.error.message);
$('#card-errors').text(result.error.message);
} else if (result.paymentIntent.status === 'succeeded') {
$('#checkout-form').off('submit').submit();
}
});
});
}
});
}
});
} catch (err) {
console.error("Error initializing Stripe or setting up payment fields:", err);
alert("An error occurred while setting up the payment system. Check the console for details.");
}
});
</script>
<!-- Sidebar Collapse/Expand -->
<script>
document.getElementById('fusionPro').addEventListener('click', function() {
var sidebar = document.getElementById('sidebar');
var icon = document.getElementById('toggle-icon');
sidebar.classList.toggle('collapsed');
if (sidebar.classList.contains('collapsed')) {
icon.classList.remove('fa-bars');
icon.classList.add('fa-chevron-right');
} else {
icon.classList.remove('fa-chevron-right');
icon.classList.add('fa-bars');
}
});
</script>
<!-- Bootstrap and other scripts -->
<script src="{% static 'js/popper.min.js' %}"></script>
<script src="{% static 'js/bootstrap5.bundle.min.js' %}"></script>
<script src="{% static 'js/all.min.js' %}"></script>
<!-- Include Select2 JavaScript -->
<script src="{% static 'js/select2.min.js' %}"></script>
</body>
</html>
Can someone tell me what I’ve to do or what I have to change in order to make this code (.py and thml) working in Ubuntu?