Skip to main content
elements.submit() guards against a second call while the first is still running, and throttles rapid repeat calls — but your UI should prevent the double-click in the first place, since these guards throw rather than queue.
TL;DR: submit:in_progress or submit:rate_limited fires → disable the Pay button for the whole attempt instead of building a retry loop.
Keep idempotency on the server side too: your charge call should use one request_id per checkout attempt (see Prevent duplicate payments), so even if a click somehow reaches your server twice, the second call resolves to the same result instead of creating a second charge.

When this happens

  • The shopper double-clicks (or double-taps) the pay button before the first submit() call resolves.
  • Your code calls submit() again within one second of the previous call, even if the first call already finished.
  • The bind itself gets locked after too many attempts on the same session — a stricter, non-retryable lockout, distinct from the client-side throttle.

What you see

What to do

1

Disable the pay button for the whole attempt

Disable it as soon as the shopper clicks, and only re-enable it once your server has responded to the charge — including on failure, so a fixable error (like a validation message) doesn’t leave the button stuck.
See Accept a card payment: Tokenize the card on submit for the full guarded pattern (vanilla JS and React) — this excerpt is the shape, that page is the source of truth.
2

Don't build your own retry loop around submit:in_progress/rate_limited

These two codes mean “wait, don’t resubmit yet” — show a brief message and let the shopper try again after the button re-enables, rather than programmatically retrying.
3

Treat a bind lockout as terminal

If you see token:bind-rate-limit, start a new session rather than retrying the same one — the lockout is scoped to that session, not to your account.

Prevent it

  • Disable the Pay button for the whole attempt (see What to do above) — the SDK’s own throttle is a backstop, not a substitute for that.
  • Same-card re-binds and repeated purchase calls with the same request_id are both safe to retry; only a fresh key on a genuinely new attempt creates a new charge.

Accept a card payment

The submit-then-charge flow this guard protects.

Prevent duplicate payments

The full cross-product guide to avoiding duplicate charges.

Tokenization errors

The full tokenization error code table.
Last modified on September 15, 2026