Skip to content

[BUG] STPPaymentHandler stuck after network loss mid-3DS #6335

@MichalTKwiecien

Description

@MichalTKwiecien

Summary

STPPaymentHandler.confirmPayment completion handler never fires when network connectivity is lost mid-3DS authentication. This leaves the private static var inProgress flag permanently set to true, causing every subsequent confirmPayment/handleNextAction call to immediately fail with noConcurrentActionsErrorCode (error code 7) for the rest of the app session.

Since inProgress is private static (shared across all instances, including new ones) and there is no public API to reset it, the SDK becomes permanently unusable for any payment confirmation until the app process is killed.

In production, a user experienced 418 consecutive 3DS failures over ~2 hours across 11 different payment IDs before giving up.

Code to reproduce

Standard implementation per Stripe's 3DS docs — minimal STPAuthenticationContext (no prepare(forPresentation:) override), URL callback correctly wired via StripeAPI.handleURLCallback(with:) in our scene delegate.

  1. Call STPPaymentHandler.sharedHandler.confirmPayment with a 3DS-required card (EU/SCA market)
  2. Lose network connectivity while the SDK is mid-3DS (after confirm API call, during ACS communication)
  3. The completion handler never fires — Self.inProgress is never reset to false
  4. Restore connectivity, call confirmPayment with a completely new client secret and payment intent
  5. Immediately fails with code 7: "The current action is not yet completed. STPPaymentHandler does not support concurrent calls to its API."
  6. All subsequent calls fail identically — creating a new STPPaymentHandler instance doesn't help since inProgress is a static property

Production error payload:

STPPaymentHandlerError( analyticsErrorCode: "noConcurrentActionsErrorCode", errorCode: 7, com.stripe.lib:ErrorMessageKey: "The current action is not yet completed. STPPaymentHandler does not support concurrent calls to its API." )

iOS version

iOS 26.1 (build 23B85), iPhone 12 (iPhone13,2)

Installation method

Swift Package Manager (stripe-ios-spm)

SDK version

25.11.0

Other information

Root cause in SDK source: In STPPaymentHandler.swift, Self.inProgress = false is only set inside wrappedCompletion. If the 3DS flow gets stuck due to network loss and never reaches any completion path, the flag stays true permanently. The authenticationTimeout on STPThreeDSCustomizationSettings (at default value) did not fire either — we never received timedOutErrorCode.

Production timeline:

  • 18:57 — confirmPayment called, 3DS flow starts. No completion callback ever received.
  • 18:59 — Severe network disruption: socket timeouts, NSURLErrorDomain -999/-1001.
  • 19:02–21:06 — User retries across 11 different payment IDs. Every call immediately returns error code 7 without ever reaching Stripe's servers. 418 total failures.

Requests:

  1. Guarantee wrappedCompletion always fires — every code path inside Stripe3DS2's STDSTransaction.doChallenge and ACS networking should eventually call the completion handler, even on network failure. Currently, it's possible for the SDK to get stuck with no callback and no timeout.
  2. Consider a public cancelCurrentAction() API, or make inProgress resettable to let us unblock the user without forcing app restart

Related: #1897 (same error code, but triggered by developer not calling prepare(forPresentation:) completion — our case is the SDK itself getting stuck), #1383, #2094

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions