             fatalError()
         }
     }
-
-    func _retrieveAndCheckIntentForCurrentAction() {
+    
+    func _retryWithExponentialDelay(retryCount: Int, block: @escaping STPVoidBlock) {
+        // Add some backoff time:
+        let delayTime = TimeInterval(
+            pow(Double(1 + Self.maxChallengeRetries - retryCount), Double(2))
+        )
+        
+        DispatchQueue.main.asyncAfter(deadline: .now() + delayTime) {
+            block()
+        }
+    }
+
+    func _retrieveAndCheckIntentForCurrentAction(retryCount: Int = maxChallengeRetries) {
         // Alipay requires us to hit an endpoint before retrieving the PI, to ensure the status is up to date.
         let pingMarlinIfNecessary:
             ((STPPaymentHandlerPaymentIntentActionParams, @escaping STPVoidBlock) -> Void) = {

                // This isn't guaranteed to succeed if the ACS isn't ready yet.
                // Try it a few more times if it fails with a 400. (RUN_MOBILESDK-126)
                if retryCount > 0 && (error as NSError?)?.code == STPErrorCode.invalidRequestError.rawValue {
                    // Add some backoff time:
                    let delayTime = TimeInterval(
                        pow(Double(1 + Self.maxChallengeRetries - retryCount), Double(2))
                    )
                    
                    DispatchQueue.main.asyncAfter(deadline: .now() + delayTime) {
                        self._markChallengeCompleted(withCompletion: completion,
                                                     retryCount: retryCount - 1)
                    }
                } else {
                    completion(success, error)
                }

        {

            currentAction.apiClient.retrieveSetupIntent(
                withClientSecret: setupIntent.clientSecret,
                expand: ["payment_method"]
            ) { retrievedSetupIntent, error in
                currentAction.setupIntent = retrievedSetupIntent
                if let error = error {
                    currentAction.complete(
                        with: STPPaymentHandlerActionStatus.failed, error: error as NSError?)
                } else {
                    if let type = retrievedSetupIntent?.paymentMethod?.type,
                       !STPPaymentHandler._isProcessingIntentSuccess(for: type),  retrievedSetupIntent?.status == .processing && retryCount > 0 {
                        self._retryWithExponentialDelay(retryCount: retryCount) {
                            self._retrieveAndCheckIntentForCurrentAction( retryCount: retryCount - 1)
                        }
                    } else {
                        let requiresAction: Bool = self._handleSetupIntentStatus(
                            forAction: currentAction)

                        if requiresAction {
                            // If the status is still RequiresAction, the user exited from the redirect before the
                            // payment intent was updated. Consider it a cancel, unless it's a valid terminal next action
                            if self.isNextActionSuccessState(nextAction: retrievedSetupIntent?.nextAction) {
                                currentAction.complete(with: .succeeded, error: nil)
                            } else {
                                // If this is a web-based 3DS2 transaction that is still in requires_action, we may just need to refresh the SI a few more times.
                                if retryCount > 0 && retrievedSetupIntent?.paymentMethod?.type == .card && retrievedSetupIntent?.nextAction?.type == .useStripeSDK {
                                    self._retryWithExponentialDelay(retryCount: retryCount) {
                                        self._retrieveAndCheckIntentForCurrentAction( retryCount: retryCount - 1)
                                    }
                                } else {
                                    // If the status is still RequiresAction, the user exited from the redirect before the
                                    // setup intent was updated. Consider it a cancel
                                    self._markChallengeCanceled(withCompletion: { _, _ in
                                        // We don't forward cancelation errors
                                        currentAction.complete(
                                            with: STPPaymentHandlerActionStatus.canceled, error: nil)
                                    })
                                }
                            }
                        }
                    }
                }
