fix: Only increment payment attempts when user actually attempts payment

- Remove payment attempt increment from checkout page load
- Add new increment_payment_attempt action triggered only on pay button click
- Update checkout JavaScript to make AJAX call before Stripe redirect
- Add proper error handling and button state management
- Prevent inflated payment attempt counts from page refreshes

This ensures payment attempts accurately reflect actual payment tries rather than page visits.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
kbe
2025-09-02 23:16:31 +02:00
parent 0ba6634e99
commit 6965eb89fd
3 changed files with 57 additions and 20 deletions

View File

@@ -4,7 +4,7 @@
# Orders group multiple tickets together for better transaction management
class OrdersController < ApplicationController
before_action :authenticate_user!
before_action :set_order, only: [:show, :checkout, :retry_payment]
before_action :set_order, only: [:show, :checkout, :retry_payment, :increment_payment_attempt]
# Display order summary
def show
@@ -31,7 +31,6 @@ class OrdersController < ApplicationController
if Rails.application.config.stripe[:secret_key].present?
begin
@checkout_session = create_stripe_session
@order.increment_payment_attempt!
rescue => e
error_message = e.message.present? ? e.message : "Erreur Stripe inconnue"
Rails.logger.error "Stripe checkout session creation failed: #{error_message}"
@@ -40,6 +39,12 @@ class OrdersController < ApplicationController
end
end
# Increment payment attempt - called via AJAX when user clicks pay button
def increment_payment_attempt
@order.increment_payment_attempt!
render json: { success: true, attempts: @order.payment_attempts }
end
# Allow users to retry payment for failed/cancelled payments
def retry_payment
unless @order.can_retry_payment?

View File

@@ -180,7 +180,7 @@
<script>
const stripe = Stripe('<%= Rails.application.config.stripe[:publishable_key] %>');
document.getElementById('checkout-button').addEventListener('click', function() {
document.getElementById('checkout-button').addEventListener('click', async function() {
const button = this;
button.disabled = true;
button.innerHTML = `
@@ -189,26 +189,57 @@
<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...
Initialisation du paiement...
</div>
`;
stripe.redirectToCheckout({
sessionId: '<%= @checkout_session.id %>'
}).then(function (result) {
if (result.error) {
button.disabled = false;
button.innerHTML = `
<div class="flex items-center justify-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/>
</svg>
Payer <%= @order.total_amount_euros %>€
</div>
`;
alert('Erreur: ' + result.error.message);
try {
// Increment payment attempt counter
const response = await fetch('<%= increment_payment_attempt_order_path(@order) %>', {
method: 'POST',
headers: {
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error('Failed to increment payment attempt');
}
});
// 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 stripeResult = await stripe.redirectToCheckout({
sessionId: '<%= @checkout_session.id %>'
});
if (stripeResult.error) {
throw new Error(stripeResult.error.message);
}
} catch (error) {
// Reset button on error
button.disabled = false;
button.innerHTML = `
<div class="flex items-center justify-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/>
</svg>
Payer <%= @order.total_amount_euros %>€
</div>
`;
alert('Erreur: ' + error.message);
}
});
</script>
</div>

View File

@@ -43,6 +43,7 @@ Rails.application.routes.draw do
member do
get :checkout
post :retry_payment
post :increment_payment_attempt
end
end