I added the features for users to use promotion code and for promoters to create on their events. May be rewrite to discount code?
320 lines
15 KiB
Plaintext
320 lines
15 KiB
Plaintext
<div class="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 py-8">
|
|
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
<!-- Breadcrumb -->
|
|
<%= render 'components/breadcrumb', crumbs: [
|
|
{ name: 'Accueil', path: root_path },
|
|
{ name: 'Événements', path: events_path },
|
|
{ name: @order.event.name, path: event_path(@order.event.slug, @order.event) },
|
|
{ name: "Commande ##{@order.id}", path: nil }
|
|
] %>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
<!-- Order Summary -->
|
|
<div class="bg-white rounded-2xl shadow-xl p-6 md:p-8 h-fit">
|
|
<!-- Warning for expiring order -->
|
|
<% if @expiring_soon %>
|
|
<div class="mb-6 bg-orange-50 border border-orange-200 rounded-lg p-4">
|
|
<div class="flex items-start">
|
|
<i data-lucide="alert-triangle" class="w-5 h-5 text-orange-600 mr-2 mt-0.5 flex-shrink-0"></i>
|
|
<div>
|
|
<h3 class="font-medium text-orange-800 mb-1">Attention - Commande bientôt expirée</h3>
|
|
<p class="text-orange-700 text-sm">Votre commande va expirer dans quelques minutes. Veuillez procéder rapidement au paiement pour éviter son expiration automatique.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% end %>
|
|
|
|
<!-- Payment attempts warning -->
|
|
<% if @order.payment_attempts > 0 %>
|
|
<div class="mb-6 bg-blue-50 border border-blue-200 rounded-lg p-4">
|
|
<div class="flex items-start">
|
|
<i data-lucide="info" class="w-5 h-5 text-blue-600 mr-2 mt-0.5 flex-shrink-0"></i>
|
|
<div>
|
|
<h3 class="font-medium text-blue-800 mb-1">Nouvelle tentative de paiement</h3>
|
|
<p class="text-blue-700 text-sm">
|
|
Tentative <%= @order.payment_attempts + 1 %> sur <%= @order.class::MAX_PAYMENT_ATTEMPTS %>.
|
|
<% if @order.payment_attempts >= @order.class::MAX_PAYMENT_ATTEMPTS - 1 %>
|
|
<strong>Dernière tentative avant expiration !</strong>
|
|
<% end %>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% end %>
|
|
|
|
<div class="border-b border-gray-200 pb-6 mb-6">
|
|
<h1 class="text-2xl font-bold text-gray-900 mb-2">Commande pour <%= @order.event.name %></h1>
|
|
<div class="flex items-center text-sm text-gray-600 space-x-4">
|
|
<div class="flex items-center">
|
|
<i data-lucide="clock" class="w-4 h-4 mr-1"></i>
|
|
<% if @order.expires_at %>
|
|
Expire dans <%= time_ago_in_words(@order.expires_at, include_seconds: true) %>
|
|
<% end %>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<i data-lucide="file-text" class="w-4 h-4 mr-1"></i>
|
|
Commande #<%= @order.id %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Order Items -->
|
|
<div class="space-y-4 mb-6 border-b border-gray-200 pb-6 mb-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 mb-4 ">Récapitulatif de votre commande</h3>
|
|
|
|
<% @tickets.each do |ticket| %>
|
|
<div class="flex items-center justify-between py-3 border-b border-gray-100 last:border-b-0">
|
|
<div class="flex-1 min-w-0">
|
|
<h4 class="text-sm font-medium text-gray-900 truncate"><%= ticket.ticket_type.name %></h4>
|
|
<div class="flex items-center text-xs text-gray-500 mt-1">
|
|
<i data-lucide="user" class="w-3 h-3 mr-1"></i>
|
|
<%= ticket.first_name %> <%= ticket.last_name %>
|
|
</div>
|
|
</div>
|
|
<div class="text-right">
|
|
<div class="text-lg font-semibold text-gray-900"><%= ticket.price_euros %>€</div>
|
|
<% if ticket.ticket_type.description.present? %>
|
|
<div class="text-xs text-gray-500"><%= truncate(ticket.ticket_type.description, length: 30) %></div>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
|
|
<!-- Promotion Code Discount -->
|
|
<% if @order.promotion_codes.any? %>
|
|
<div class="space-y-2 mb-6 pb-6 border-b border-gray-200">
|
|
<% @order.promotion_codes.each do |promo_code| %>
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-sm font-medium text-green-600">
|
|
<i data-lucide="tag" class="w-4 h-4 mr-1"></i>
|
|
Code: <%= promo_code.code %>
|
|
</span>
|
|
<span class="text-sm font-semibold text-green-600">-<%= promo_code.discount_amount_euros %>€</span>
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
<% end %>
|
|
|
|
<!-- Order Total -->
|
|
<div class=" pt-12">
|
|
<div class="space-y-2">
|
|
<!-- Subtotal -->
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-sm text-gray-600">Sous-total</span>
|
|
<span class="text-sm font-medium text-gray-600"><%= @order.subtotal_amount_euros %>€</span>
|
|
</div>
|
|
|
|
<!-- Discount -->
|
|
<% if @order.discount_amount_cents > 0 %>
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-sm text-green-600">Réduction</span>
|
|
<span class="text-sm font-semibold text-green-600">-<%= @order.discount_amount_euros %>€</span>
|
|
</div>
|
|
<% end %>
|
|
|
|
<!-- Total -->
|
|
<div class="flex items-center justify-between text-lg pt-2 border-t border-gray-200">
|
|
<span class="font-medium text-gray-900">Total</span>
|
|
<% if @order.total_amount_cents == 0 %>
|
|
<span class="font-bold text-2xl text-green-600">GRATUIT</span>
|
|
<% else %>
|
|
<span class="font-bold text-2xl text-purple-600"><%= @order.total_amount_euros %>€</span>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
<p class="text-xs text-gray-500 mt-2">TVA incluse</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Payment Section -->
|
|
<div class="bg-white rounded-2xl shadow-xl p-6 md:p-8 h-fit">
|
|
<div class="border-b border-gray-200 pb-6 mb-6">
|
|
<h2 class="text-xl font-bold text-gray-900 mb-2">Paiement sécurisé</h2>
|
|
<p class="text-sm text-gray-600">Procédez au paiement pour finaliser votre commande</p>
|
|
</div>
|
|
|
|
<!-- Promotion Code Section -->
|
|
<%= form_tag checkout_order_path(@order), method: :get, class: "mb-6" do %>
|
|
<div class="flex items-center bg-gray-50 border border-gray-200 rounded-lg p-3">
|
|
<%= text_field_tag :promotion_code, params[:promotion_code], class: "flex-1 border-none bg-transparent focus:ring-0 text-sm", placeholder: "Code promotionnel (optionnel)" %>
|
|
<%= button_tag type: "submit", class: "ml-2 btn btn-secondary py-2 px-4 text-sm" do %>
|
|
Appliquer
|
|
<% end %>
|
|
</div>
|
|
<% end %>
|
|
|
|
<% if @checkout_session.present? %>
|
|
<!-- Stripe Checkout -->
|
|
<div class="space-y-6">
|
|
<div class="bg-gradient-to-r from-purple-50 to-pink-50 rounded-lg p-4 border border-purple-200">
|
|
<div class="flex items-start">
|
|
<i data-lucide="shield" class="w-5 h-5 text-purple-600 mr-2 mt-0.5 flex-shrink-0"></i>
|
|
<div>
|
|
<h3 class="font-medium text-purple-800 mb-1">Paiement 100% sécurisé</h3>
|
|
<p class="text-purple-700 text-sm">Vos données bancaires sont protégées par le cryptage SSL et traitées par Stripe, leader mondial du paiement en ligne.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
id="checkout-button"
|
|
data-order-id="<%= @order.id %>"
|
|
data-increment-url="/api/v1/orders/<%= @order.id %>/increment_payment_attempt"
|
|
data-session-id="<%= @checkout_session.id if @checkout_session.present? %>"
|
|
class="w-full btn btn-primary py-4 px-6 rounded-xl transition-all duration-200 transform hover:scale-105 active:scale-95 shadow-lg hover:shadow-xl"
|
|
>
|
|
<div class="flex items-center justify-center">
|
|
<i data-lucide="credit-card" class="w-5 h-5 mr-2"></i>
|
|
<% if @order.total_amount_cents == 0 %>
|
|
Confirmer la commande
|
|
<% else %>
|
|
Payer <%= @order.total_amount_euros %>€
|
|
<% end %>
|
|
</div>
|
|
</button>
|
|
|
|
<div class="flex items-center justify-center space-x-4 text-xs text-gray-500">
|
|
<span class="flex items-center">
|
|
<i data-lucide="credit-card" class="w-4 h-4 mr-1"></i>
|
|
Visa
|
|
</span>
|
|
<span class="flex items-center">
|
|
<i data-lucide="credit-card" class="w-4 h-4 mr-1"></i>
|
|
Mastercard
|
|
</span>
|
|
<span class="flex items-center">
|
|
<i data-lucide="shield" class="w-4 h-4 mr-1"></i>
|
|
Sécurisé par Stripe
|
|
</span>
|
|
</div>
|
|
|
|
<script src="https://js.stripe.com/v3/"></script>
|
|
<script>
|
|
// Wait for Stripe library to load and DOM to be ready
|
|
function initializeStripeCheckout() {
|
|
if (typeof Stripe === 'undefined') {
|
|
console.log('Waiting for Stripe library to load...');
|
|
setTimeout(initializeStripeCheckout, 100);
|
|
return;
|
|
}
|
|
|
|
console.log('Initializing Stripe with publishable key:', '<%= Rails.application.config.stripe[:publishable_key] %>');
|
|
const stripe = Stripe('<%= Rails.application.config.stripe[:publishable_key] %>');
|
|
|
|
const checkoutButton = document.getElementById('checkout-button');
|
|
if (!checkoutButton) {
|
|
console.error('Checkout button not found');
|
|
return;
|
|
}
|
|
|
|
checkoutButton.addEventListener('click', async function() {
|
|
console.log('Checkout button clicked');
|
|
const button = this;
|
|
button.disabled = true;
|
|
button.innerHTML = `
|
|
<div class="flex items-center justify-center">
|
|
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
</svg>
|
|
Initialisation du paiement...
|
|
</div>
|
|
`;
|
|
|
|
try {
|
|
// Increment payment attempt counter
|
|
const orderId = checkoutButton.dataset.orderId;
|
|
const incrementUrl = checkoutButton.dataset.incrementUrl;
|
|
console.log('Incrementing payment attempt for order:', orderId);
|
|
const response = await fetch(incrementUrl, {
|
|
method: 'PATCH',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-Token': document.querySelector('[name=csrf-token]').content
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
console.error('Payment attempt increment failed:', response.status, response.statusText);
|
|
throw new Error('Failed to increment payment attempt');
|
|
}
|
|
|
|
console.log('Payment attempt incremented successfully');
|
|
|
|
// Update button text for redirect
|
|
button.innerHTML = `
|
|
<div class="flex items-center justify-center">
|
|
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
</svg>
|
|
Redirection vers le paiement...
|
|
</div>
|
|
`;
|
|
|
|
// Redirect to Stripe
|
|
const sessionId = checkoutButton.dataset.sessionId;
|
|
console.log('Redirecting to Stripe with session ID:', sessionId);
|
|
const stripeResult = await stripe.redirectToCheckout({
|
|
sessionId: sessionId
|
|
});
|
|
|
|
if (stripeResult.error) {
|
|
throw new Error(stripeResult.error.message);
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Checkout error:', error);
|
|
// Reset button on error
|
|
button.disabled = false;
|
|
button.innerHTML = `
|
|
<div class="flex items-center justify-center">
|
|
<i data-lucide="credit-card" class="w-5 h-5 mr-2"></i>
|
|
<% if @order.total_amount_cents == 0 %>
|
|
Confirmer la commande
|
|
<% else %>
|
|
Payer <%= @order.total_amount_euros %>€
|
|
<% end %>
|
|
</div>
|
|
`;
|
|
alert('Erreur: ' + error.message);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Initialize when DOM is ready
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', initializeStripeCheckout);
|
|
} else {
|
|
initializeStripeCheckout();
|
|
}
|
|
</script>
|
|
</div>
|
|
<% else %>
|
|
<!-- No Stripe Configuration -->
|
|
<div class="text-center py-8">
|
|
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-6">
|
|
<i data-lucide="alert-triangle" class="w-12 h-12 text-yellow-600 mx-auto mb-4"></i>
|
|
<h3 class="font-semibold text-yellow-800 mb-2">Paiement temporairement indisponible</h3>
|
|
<p class="text-yellow-700 text-sm">Le système de paiement n'est pas encore configuré. Veuillez contacter l'organisateur pour plus d'informations.</p>
|
|
</div>
|
|
</div>
|
|
<% end %>
|
|
|
|
<!-- Order Actions -->
|
|
<div class="border-t border-gray-200 pt-6 mt-6">
|
|
<div class="space-y-3">
|
|
<%= link_to event_path(@order.event.slug, @order.event), class: "block w-full text-center py-3 px-4 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition-colors" do %>
|
|
<div class="flex items-center justify-center">
|
|
<i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i>
|
|
Retour à l'événement
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|