如何阻止客户重复点击Ajax表单提交按钮?OpenCart 2.0.3.1集成WorldPay支付模块重复下单问题求助
Got it, let’s tackle that frustrating double-click issue causing duplicate orders and charges. The root problem is customers can click the confirm button multiple times before the page redirects, triggering repeated form submissions. Here’s a straightforward fix that adds submission state tracking and disables the button once it’s clicked:
Key Changes Needed
We’ll implement three core fixes:
- Add a flag to track if a submission is already in progress
- Disable the submit button and show loading text when clicked
- Re-enable the button if there’s a payment error (so customers can retry without reloading)
Modified Script Section (Full Updated Code)
Replace the existing <script> block in your checkout page with this version. I’ve highlighted the critical changes with comments:
<script type="text/javascript"><!-- //Load Worldpay.js and run script functions $.getScript("<?php echo $worldpay_script; ?>", function (data, textStatus, jqxhr) { Worldpay.setClientKey("<?php echo $worldpay_client_key; ?>"); // disable new card form if existing cards <?php if (!empty($existing_cards)) { ?> $('#payment-new-form :input').prop('disabled', true); <?php } ?> // Set if token is reusable, remove first value when Worldpay update Worldpay.reusable = true; <?php if (isset($recurring_products)) { ?> Worldpay.reusable = true; <?php } else { ?> $('input[name=\'save-card\']').on('change', function () { if ($(this).is(':checked')) { Worldpay.reusable = true; } else { Worldpay.reusable = false; } }); <?php } ?> Worldpay.templateSaveButton = false; Worldpay.useTemplate('payment-new-form', 'paymentDetailsHere', 'inline', function (obj) { var _el = document.createElement('input'); _el.value = obj.token; _el.type = 'hidden'; _el.name = 'token'; document.getElementById('payment-new-form').appendChild(_el); }); // -------------------------- // NEW: Track submission state // -------------------------- let isSubmitting = false; //Submit form $('input[type=\'submit\']').on('click', function (e) { // Prevent default form submit to take full control e.preventDefault(); // Block clicks if we're already processing a payment if (isSubmitting) return; const $button = $(this); const originalButtonText = $button.val(); // -------------------------- // NEW: Disable button and show loading state // -------------------------- isSubmitting = true; $button.prop('disabled', true).button('loading'); var existing = $('input[name=\'existing-card\']:checked').val(); if (existing === '1') { var form = document.getElementById('payment-existing-form'); Worldpay.useForm(form, function (status, response) { if (response.error || status != 200) { Worldpay.handleError(form, document.getElementById('payment-errors'), response.error); // -------------------------- // NEW: Reset state on error so customers can retry // -------------------------- isSubmitting = false; $button.prop('disabled', false).button('reset').val(originalButtonText); } else { // Submit the form only once form.submit(); } }, true); } else { // Handle new card submission with error recovery Worldpay.submitTemplateForm(function(status, response) { if (response.error || status !== 200) { Worldpay.handleError(document.getElementById('payment-new-form'), document.getElementById('payment-errors'), response.error); // -------------------------- // NEW: Reset state on error // -------------------------- isSubmitting = false; $button.prop('disabled', false).button('reset').val(originalButtonText); } }); } }); }); //Delete a card $('#button-delete').on('click', function () { var token = $('select[name=\'token\'] option:selected'); if (confirm('<?php echo $text_confirm_delete ?>\n' + token.text())) { $.ajax({ url: 'index.php?route=payment/worldpay/deleteCard', type: 'post', data: {token: token.val()}, dataType: 'json', beforeSend: function () { $('#button-delete').button('loading'); }, complete: function () { $('#button-delete').button('reset'); }, success: function (json) { if (json['error']) { alert(json['error']); } if (json['success']) { alert(json['success']); if (json['existing_cards']) { token.remove(); } else { $('input[name=\'existing-card\'][value=0]').click(); $('#choose-card').remove(); $('#payment-existing-form').remove(); } } } }); } }); // enable or disable forms based on exiting or new card option $('input[name=\'existing-card\']').on('change', function () { if (this.value === '1') { $('#payment-existing-form').show(); $('#payment-new-form').hide(); $('#payment-new-form :input').prop('disabled', true); $('#payment-existing-form :input').prop('disabled', false); } else { $('#payment-existing-form').hide(); $('#payment-new-form').show(); $('#payment-new-form :input').prop('disabled', false); $('#payment-existing-form :input').prop('disabled', true); } }); //--></script>
How It Works
- Submission Flag: The
isSubmittingvariable acts as a gatekeeper—once set totrue, any subsequent clicks on the button are ignored. - Button Disabling: When the button is clicked, we immediately disable it and show the loading text (using OpenCart’s built-in Bootstrap button methods) to give customers visual feedback that their payment is processing.
- Error Recovery: If WorldPay returns an error (like an invalid CVC), we reset the flag and re-enable the button so customers can correct their details and try again.
This should eliminate duplicate orders entirely while keeping the checkout flow user-friendly for customers who need to retry their payment.
内容的提问来源于stack exchange,提问作者ci_j
相关产品推荐
相关产品推荐

