Duplicate payment prevention logic exists
Why it matters
A payment submit button that stays enabled during an in-flight request lets users double-click or rapidly resubmit, dispatching concurrent payment requests for the same cart. Even if you use idempotency keys on the backend, two concurrent requests before the first response arrives may each generate a separate key — resulting in two distinct charges. Beyond accidental double-clicks, an enabled button during processing provides no user feedback, increasing anxiety and intentional resubmission. CWE-362 (Race Condition) and CWE-799 (Improper Control of Interaction Frequency) both apply. The fix is a two-layer guard: UI state disabling the button immediately on first submission, and backend idempotency preventing duplicates if the UI guard is bypassed.
Severity rationale
High because an always-enabled submit button under network latency allows concurrent submissions that can produce duplicate charges before the idempotency layer has a chance to deduplicate them.
Remediation
Set an isProcessing flag on the first submission click and disable the button until the round-trip completes or fails.
function PaymentForm() {
const [isProcessing, setIsProcessing] = useState(false)
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (isProcessing) return // synchronous guard for rapid double-click
setIsProcessing(true)
try {
const { error, paymentIntent } = await stripe!.confirmPayment({ /* ... */ })
if (!error && paymentIntent?.status === 'succeeded') {
router.push('/order/complete')
} else if (error) {
setError(error.message)
}
} finally {
setIsProcessing(false)
}
}
return (
<form onSubmit={handleSubmit}>
<button type="submit" disabled={isProcessing} aria-busy={isProcessing}>
{isProcessing ? 'Processing…' : 'Pay Now'}
</button>
</form>
)
}
The finally block ensures the button re-enables on errors, letting users correct card details and resubmit without refreshing the page.
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Payment Security Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe · CWE-362 — Concurrent Execution Using Shared Resource with Improper Synchronization (Race Condition)
- cwe · CWE-799 — Improper Control of Interaction Frequency
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ecommerce-payment-security·automated